Compare a VQE expectation with sampled candidates¶
Use VQE to minimize a two-spin Ising model. Unlike QAOA's alternating cost and mixer, the default VQE circuit uses parameterized rotations and entangling gates. Complete QAOA result interpretation and use the installed environment.
The classical model is
E(s0,s1) = 0.1 − 0.8 s0 + 0.35 s1 + 0.6 s0 s1
s ∈ {−1,+1}, s = 2x−1 = −Z
H = 0.1 I + 0.8 Z0 − 0.35 Z1 + 0.6 Z0 Z1
In (q0,q1) bit order, the energies of 00, 01, 10, 11 are respectively 1.15, 0.65, −1.65, 0.25. Thus 10 is the ground state of this diagonal operator. The Hamiltonian expectation of any normalized state is at least −1.65; the value depends on the whole probability distribution.
Keep optimization separate from the final draw¶
"""Run VQE for a typed two-spin Ising model.
VQE turns the model into a Pauli Hamiltonian, evaluates one real Backend Job per
objective point, optimizes with SciPy, and uses a separate final-sampling Job.
The bounded baseline is evidence for this small case, not a scalability claim.
"""
from __future__ import annotations
import json
from cascaqit import VQE, IsingModelIR, LocalBackend, OptimizerConfig
def main() -> None:
"""Optimize a small Ising energy with deterministic inputs."""
model = IsingModelIR.from_terms(
problem_id="lesson.optimization.vqe",
spins=("q0", "q1"),
fields={"q0": -0.8, "q1": 0.35},
couplings={("q0", "q1"): 0.6},
offset=0.1,
)
result = VQE(model, layers=1).run(
backend=LocalBackend(seed=502),
optimizer=OptimizerConfig(
method="Powell", max_iterations=6, max_evaluations=8, seed=502
),
initial_parameters=(0.1, 0.0, -0.2, 0.0),
final_shots=64,
)
candidate = result.best_observed_candidate
baseline = result.baseline
assert candidate is not None and baseline is not None
assert result.final_result is not None
payload = {
"track": "optimization_researcher",
"level": "advanced",
"lesson": "vqe_workflow",
"facts": {
"objective_estimator": result.metadata["objective_estimator"],
"objective_total_shots": result.metadata["objective_total_shots"],
"workflow_total_shots": result.metadata["workflow_total_shots"],
"backend_executions": result.metadata["workflow_backend_execution_count"],
"final_counts": result.final_result.counts,
"final_probabilities": result.final_result.probabilities,
"candidate_count": candidate.count,
"candidate_value": candidate.objective_value,
"baseline_bitstring": baseline.bitstring,
"evaluations": len(result.evaluations),
"best_energy": round(result.best_evaluation.energy, 10),
"counts_total": sum(result.final_result.counts.values()),
"candidate_bitstring": candidate.bitstring,
"baseline_value": baseline.objective_value,
"termination_reason": result.termination.reason,
},
"boundaries": {
"hardware_execution": False,
"cloud_execution": False,
"network_accessed": False,
"credentials_loaded": False,
},
}
print(json.dumps(payload, sort_keys=True))
if __name__ == "__main__":
main()
python3 examples/user/tracks/optimization_researcher/04_advanced_vqe_workflow_en.py
The default one-layer Ansatz uses RY and RZ on each of the two qubits, followed by linear CX entanglement. It has four parameters; the supplied initial vector follows the circuit's declared parameter order. Changing the Ansatz or layer count requires checking that order and dimension again.
Powell is capped at eight objective evaluations and six iterations. Every objective uses the exact state expectation. After selecting the lowest evaluated energy, a separate job draws 64 final samples. No finite-shot objective estimator or noise model is requested here.
{
"boundaries": {
"cloud_execution": false,
"credentials_loaded": false,
"hardware_execution": false,
"network_accessed": false
},
"facts": {
"backend_executions": 9,
"baseline_bitstring": "10",
"baseline_value": -1.65,
"best_energy": 0.231067983,
"candidate_bitstring": "10",
"candidate_count": 1,
"candidate_value": -1.65,
"counts_total": 64,
"evaluations": 8,
"final_counts": {
"10": 1,
"11": 63
},
"final_probabilities": {
"00": 5.128156145191454e-06,
"01": 5.162538597544479e-08,
"10": 0.009966659453993207,
"11": 0.9900281607644755
},
"objective_estimator": "exact_state",
"objective_total_shots": 0,
"termination_reason": "max_evaluations",
"workflow_total_shots": 64
},
"lesson": "vqe_workflow",
"level": "advanced",
"track": "optimization_researcher"
}
Read termination_reason before interpreting the energy. This deliberately short run can stop at max_evaluations far above the baseline. Its candidate can nevertheless be 10 if at least one optimal sample appears. candidate_count reveals how often it occurred; one occurrence and 64 occurrences imply very different output distributions.
Independently reconstruct the final expectation by summing P(bits) × E(bits) over all four strings. It should match best_energy up to rounding. Then do the same with count/64. The latter is a sampled estimate and need not equal the exact expectation.
Change one source of error¶
- Raise only
final_shots. Does that improve the exact objective history? - Allow 40 objective evaluations and enough iterations. Compare the best energy, stopping reason and actual backend cost with the original run.
- For the original final state, calculate both the energy gap above
−1.65and the probability of10. Can two states with the same gap have different optimal-state probabilities?
More final shots only improve the sampling estimate. More evaluations give Powell additional search opportunities; they do not guarantee convergence or a global optimum. A larger budget run includes the same initial search for this deterministic setup, but compare its recorded evaluations rather than assuming that all budgets are used. Equal mean energy does not fix all four probabilities, so it does not uniquely determine the ground-state probability.
This model is diagonal in Z. For a general Pauli Hamiltonian with X or Y terms, computational-basis counts alone cannot reconstruct its energy; additional measurement bases are required. Variational algorithms describes finite-shot VQE and measurement grouping. Continue with baseline analysis before making an algorithm comparison.