Skip to content

Find a ground state with OpenQARP

examples/openqarp_vqe.py

Run a variational algorithm written against OpenQARP, with the platform as its engine. QubitraEngine is a qarp.engines.Engine, so OpenQARP’s own VQE drives it without knowing the backend is remote — and the property worth watching is batching: an analytic parameter-shift gradient evaluates two shifted circuits per parameter, and the engine sends every stencil point of the step as one job.

The chemistry is H2 at its equilibrium bond length, in the two-qubit form its symmetries reduce to. Its ground state is known, so a correct run has somewhere to land.

Needs the extra: pip install 'qubitra-sdk[openqarp]'.

from qarp.operators import QubitOperator
H2 = (
QubitOperator("", -1.0523732)
+ QubitOperator("Z0", 0.39793742)
+ QubitOperator("Z1", -0.39793742)
+ QubitOperator("Z0 Z1", -0.01128010)
+ QubitOperator("X0 X1", 0.18093119)
)
NUCLEAR_REPULSION = 0.7199689

Five Pauli terms, the electronic part of the energy. The nuclei contribute a constant that no circuit computes, added back at the end for the total energy.

def ansatz() -> SimpleBlock:
"""|01> then a single Givens rotation — the one parameter H2's ground state needs."""
block = SimpleBlock(2, name="ucc")
block.x(0)
block.rx(0, np.pi / 2)
block.h(1)
block.cx(0, 1)
block.rz(1, sympy.Symbol("theta"))
block.cx(0, 1)
block.rx(0, -np.pi / 2)
block.h(1)
block.build()
return block

An OpenQARP block like any other. sympy.Symbol("theta") is the free parameter; it becomes an input float[64] theta; declaration in the circuit the platform receives, and each evaluation travels as a parameter row beside it.

# Credentials resolve from QUBITRA_API_KEY / QUBITRA_API_URL, same as QubitraClient.
engine = QubitraEngine(backend_id)

The whole integration. Swapping QarpEngine() for QubitraEngine(backend_id) is the only line that differs from running locally.

vqe = VQE(
operator=H2,
ket=ansatz(),
initial_parameters=np.array([0.0]),
gradient="parameter-shift",
engine=engine,
)
vqe.build()
energy, parameters = vqe.run()

build() compiles the primitive and negotiates the circuit format with the backend; nothing is submitted yet. run() then drives the optimiser: each iteration is one estimator PUB for the energy, then one job carrying both shifted points of the gradient.

The cost model that implies: two platform round trips per iteration, each a few seconds of submit-poll-fetch, whatever the circuit costs. That is workable for prototyping against real backends, and the reason the stencil must hold together as one job.

Terminal window
QUBITRA_API_KEY=qpk_... python examples/openqarp_vqe.py

Output, trimmed:

backend: sim-statevector-26q
theta: +0.223528
electronic energy: -1.857275 Ha (exact: -1.857275)
total energy: -1.137306 Ha (exact: -1.137306)

The total energy is H2’s ground state to six decimals. The example exits non-zero if the optimiser lands anywhere else: the ansatz spans the ground state and the backend’s estimator is exact, so a wrong number is a broken path rather than a hard problem.

  • OpenQARP — the full adapter: which primitive targets run, what is refused, and how sweeps and gradients batch.
  • Circuit formats — how a parameter row binds to an input declaration.
  • Sessions — correlating a whole optimisation run.