Getting started¶
Simple example¶
Here we demonstrate the standard workflow using a simple example solving a Heisenberg problem on a 5x5 grid.
To begin, we import the required libraries, and build our Hamiltonian as a QubitOperator:
import scipy.sparse.linalg as spla
from qiskit.transpiler import CouplingMap
import fulqrum as fq
# Build 16-qubit coupling map
cmap = CouplingMap.from_grid(4, 4)
num_qubits = cmap.size()
# Generate Hamiltonian
H = fq.QubitOperator(num_qubits, [])
touched_edges = set({})
coeffs = [-1/2, -1/2, -1]
for edge in cmap.get_edges():
if edge[::-1] not in touched_edges:
touched_edges.add(edge)
H += fq.QubitOperator(num_qubits, [("XX", edge, coeffs[0]),
("YY", edge, coeffs[1]),
("ZZ", edge, coeffs[2])])
The subspace in which our Hamiltonian will be defined is derived from the counts sampled from a quantum computer
and stored as a Subspace object (note that we do not use the counts data itself):
counts = []
for kk in range(2**num_qubits):
counts.append(bin(kk)[2:].zfill(num_qubits))
S = fq.Subspace([counts])
Right now we have a Hamiltonian and a subspace, but they know nothing about each other. We can combine
them into a SubspaceHamiltonian object, which is where all the magic happens, and is what we pass on
to the eigensolver:
Hsub = fq.SubspaceHamiltonian(H, S)
evals, evecs = spla.eigsh(Hsub, k=1, which='SA')
In passing the SubspaceHamiltonian object directly we have solved the problem using a matrix-free method,
but one can build a collection of different, more performant, matrix-representations from this object.
The found eigenenergy is:
print("Eigenvalue:", evals[0])
Eigenvalue: -24.000000000000007
there is of course an associated eigenvector, but because of the way Fulqrum solves the problem, it is not
immediately usable. Instead, we can use SubspaceHamiltonian.interpret_vector() to cast the solution
as a dictionary the statevector amplitudes as values:
Hsub.interpret_vector(evecs)
{'0000000000000000': -0.9896849063220712,
'1111111111111111': 0.14326125155907726}