Krylov subspace & Green’s functions

InQuanto offers built-in support for measuring the moments of an operator to calculate quantities within a Krylov subspace, such as the Lanczos representation of a Hamiltonian or the Green’s function. The key computable class for this is the KrylovSubspaceComputable, which is an example of a composite computable from the inquanto.computables.composite submodule. Given an operator and a state, this computable evaluates to a series of moments as the expectation values of the powers of the operator.

We can demonstrate this using a simple 2-site Hubbard model. First, we prepare the necessary state and operator:

from inquanto.computables.composite import KrylovSubspaceComputable

from inquanto.express import DriverHubbardDimer
from inquanto.operators import FermionOperator
from inquanto.ansatzes import FermionSpaceAnsatzChemicallyAwareUCCSD

driver = DriverHubbardDimer(t=0.3, u=2.15)
hamiltonian, space, state = driver.get_system()
qubit_hamiltonian = hamiltonian.qubit_encode()
ansatz = FermionSpaceAnsatzChemicallyAwareUCCSD(space, state)
parameters = ansatz.state_symbols.construct_random(2, 0.01, 0.1)

The computable can be instantiated as follows, where the Krylov space is expanded up to rank 4:

krylov_subspace_computable = KrylovSubspaceComputable(ansatz, qubit_hamiltonian, 4)

Once we have the computable, we can use a protocol to measure it. Since the KrylovSubspaceComputable calculates expectation values, we may use the PauliAveraging protocol:

from inquanto.protocols import PauliAveraging
from pytket.partition import PauliPartitionStrat
from pytket.extensions.qiskit import AerBackend

protocol = PauliAveraging(
    AerBackend(),
    shots_per_circuit=10000,
    pauli_partition_strategy=PauliPartitionStrat.CommutingSets,
)

protocol.build_from(parameters, krylov_subspace_computable)
protocol.run(seed=2)
<inquanto.protocols.averaging._pauli_averaging.PauliAveraging at 0x7f7dd4e430d0>

Building the protocol from the Krylov subspace computable generates all measurement circuits, and running the protocol submits circuits to the backend and retrieves results. We may then inspect the protocol via dataframe helper methods, and use the evaluator to generate a KrylovSubspace:

print("Measurements:")
print(protocol.dataframe_measurements())
print("Circuits measured:")
print(protocol.dataframe_circuit_shot())

krylov_subspace = krylov_subspace_computable.evaluate(evaluator=protocol.get_evaluator())
Measurements:
   pauli_string    mean    stderr             umean  sample_size
0      Z0 X1 X3 -0.0970  0.009953    -0.097+/-0.010        10000
1            Z2  0.8812  0.004728     0.881+/-0.005        10000
2   X0 X1 Y2 Y3 -0.4608  0.008875    -0.461+/-0.009        10000
3   X0 Z1 X2 Z3  0.1628  0.009867     0.163+/-0.010        10000
4   Z0 X1 Z2 X3 -0.1606  0.009871    -0.161+/-0.010        10000
5         Z0 Z1 -0.9844  0.001760  -0.9844+/-0.0018        10000
6         Z0 Z3  0.9844  0.001760   0.9844+/-0.0018        10000
7      Z0 Z1 Z2 -0.8786  0.004776    -0.879+/-0.005        10000
8      Z0 Y1 Y3 -0.0970  0.009953    -0.097+/-0.010        10000
9      Y0 Z1 Y2 -0.1114  0.009938    -0.111+/-0.010        10000
10  X0 Y1 Y2 X3  0.4608  0.008875     0.461+/-0.009        10000
11  Y0 Z1 Y2 Z3  0.1628  0.009867     0.163+/-0.010        10000
12     X0 Z1 X2 -0.1114  0.009938    -0.111+/-0.010        10000
13        Y1 Y3  0.1606  0.009871     0.161+/-0.010        10000
14     Y0 Y2 Z3  0.1114  0.009938     0.111+/-0.010        10000
15  Z0 Z1 Z2 Z3  1.0000  0.000000           1.0+/-0        10000
16        X0 X2 -0.1628  0.009867    -0.163+/-0.010        10000
17     Z0 Z2 Z3  0.8786  0.004776     0.879+/-0.005        10000
18        Y0 Y2 -0.1628  0.009867    -0.163+/-0.010        10000
19     Z0 Z1 Z3  0.8812  0.004728     0.881+/-0.005        10000
20  Y0 X1 X2 Y3  0.4608  0.008875     0.461+/-0.009        10000
21           Z0 -0.8812  0.004728    -0.881+/-0.005        10000
22     Y1 Z2 Y3  0.0970  0.009953     0.097+/-0.010        10000
23           Z3 -0.8786  0.004776    -0.879+/-0.005        10000
24     Z1 Z2 Z3 -0.8812  0.004728    -0.881+/-0.005        10000
25        Z0 Z2 -1.0000  0.000000          -1.0+/-0        10000
26        Z1 Z2  0.9844  0.001760   0.9844+/-0.0018        10000
27  X0 Y1 X2 Y3 -0.4764  0.008793    -0.476+/-0.009        10000
28        Z1 Z3 -1.0000  0.000000          -1.0+/-0        10000
29  Z0 Y1 Z2 Y3 -0.1606  0.009871    -0.161+/-0.010        10000
30  Y0 X1 Y2 X3 -0.4764  0.008793    -0.476+/-0.009        10000
31  Y0 Y1 Y2 Y3 -0.4764  0.008793    -0.476+/-0.009        10000
32     X1 Z2 X3  0.0970  0.009953     0.097+/-0.010        10000
33  Y0 Y1 X2 X3 -0.4608  0.008875    -0.461+/-0.009        10000
34  X0 X1 X2 X3 -0.4764  0.008793    -0.476+/-0.009        10000
35        Z2 Z3 -0.9844  0.001760  -0.9844+/-0.0018        10000
36        X1 X3  0.1606  0.009871     0.161+/-0.010        10000
37     X0 X2 Z3  0.1114  0.009938     0.111+/-0.010        10000
38           Z1  0.8786  0.004776     0.879+/-0.005        10000
Circuits measured:
    Qubits Depth Depth2q DepthCX  Shots
0        4    42      23      23  10000
1        4    43      24      24  10000
2        4    41      23      23  10000
Sum      -     -       -       -  30000

krylov_subspace is an instance of the KrylovSubspace class that offers various methods to calculate eigenvalues, Lanczos coefficients, Green’s functions, and more.

print("Eigenvalues of the Lanczos matrix:", krylov_subspace.eigenvalues())
exact = qubit_hamiltonian.eigenspectrum(hamming_weight=state.single_term.hamming_weight)
print("Exact diagonalization: ", exact)
Eigenvalues of the Lanczos matrix: [-0.156 -0.     2.15   2.306]
Exact diagonalization:  [-0.156 -0.    -0.     0.     2.15   2.306]