0.14.0 (2026-09-02)
v0.14 adds LASSQD, a localised active-space sample-based quantum diagonalisation workflow for larger chemistry problems, together with the LUCJ ansatz that prepares its fragments. ProgramEnsemble.run() becomes an adaptive multi-round workflow that blocks until convergence, and ensembles now checkpoint at every round boundary, so an interrupted run picks up where it stopped. Circuit submission is unified behind a new CircuitPayload, and the core install shrinks by over 500 MB as PennyLane, qiskit-aer and dwave-hybrid move behind optional extras. This release also fixes a silently-wrong parameter-shift gradient for UCCSD and Hartree-Fock ansatze and five checkpointing defects.
⚠️ Breaking Changes
If you are upgrading from v0.13.x, review the following before merging:
| What changed | Migration |
|---|---|
| PennyLane no longer installs by default | Install qoro-divi[pennylane] if your code passes PennyLane circuits/operators as inputs. The all extra still includes it. |
QiskitSimulator and partitioned QUBO solving no longer install by default |
Install qoro-divi[aer] for QiskitSimulator, qoro-divi[qubo-decompose] for partitioned QUBO solving. Both raise an error naming the install command if used without it. Unpartitioned QUBO/HUBO (dimod) is unaffected. |
ai dropped from the all extra |
Install qoro-divi[ai] explicitly if you need divi.ai. |
A parameter-shift gradient on QAOA now raises NotImplementedError instead of returning a near-zero value |
QAOA's layer angles have no parameter-shift frequency family, so the two-term rule was silently wrong. Use a gradient-free optimizer (e.g. COBYLA) or a stochastic one (e.g. SPSA) with QAOA instead. |
Backend authoring: SupportsCircuitTemplates is gone; QoroService has one submit_circuits instead of two submit methods |
A custom backend sets resolves_parameters = True instead of implementing SupportsCircuitTemplates. Callers of QoroService's old dual submit methods move to the single submit_circuits. |
✨ Added
- qprog: LASSQD — localised active-space sample-based quantum diagonalisation lands as a
ProgramEnsemblewhere one round is one macro-cycle: build effective fragment integrals from the current orbitals, prepare and sample one circuit per fragment, recover the subspace with SQD, rebuild the fragment density matrices, and optimise the molecular orbitals until the energy converges. Ships active-space selection with independently localised occupied and virtual blocks, coupling-graph fragmentation, spin-polarised fragments, carryover SQD (arXiv:2512.14936), an analytic generalised-Fock orbital gradient, and aLASSQDRoundReportper round covering energy, per-fragment subspace sizes, orbital diagnostics and stage timings. Fragments are prepared classically by default — fragment ROHF and CCSD seed a one-repetition LUCJ operator that ffsim optimises on the exact statevector, so a round samples one circuit per fragment;preparation_mode="vqe"runs a full VQE loop per fragment instead. Requires thechemextra. Note the energy functional zeroes cross-fragment 2-RDM blocks, so it is not variational for more than one fragment (881e72c, 254604b, bbd05b7, b35ed3d, 9ec8a51, 3ba0cb7, 7ce78b0, 22b1309, f7c52ac, 58b0907, 698cc77) - qprog: LUCJ ansatz — a local unitary cluster Jastrow whose hopping terms stay inside one spin sector, with a full Clements brick-wall orbital rotation reaching U(n), an optional trailing rotation closing each layer, configurable
rotation_depth, explicit Jastrow pair lists andshared_spin_params. Reachable through the newVQE(ansatz_kwargs=...), which forwards to bothn_params_per_layerandbuildso the parameter count and the circuit cannot disagree (eb8f972, 09cfd5f, 8cc97d1) - qprog:
ProgramEnsemble.run()is an adaptive multi-round workflow — it drives rounds (materialise programs, dispatch, reduce results into a workflow state, check convergence), blocks until done and returns self, and Ctrl-C cancels the workflow. The previous one-shot dispatch stays available asrun_one_round(blocking=...),create_programs()now receives the current state, andReportingLevel,WorkflowStatusandRoundRecord/round_historycover progress verbosity and per-round accounting (ca0450d) - qprog: ensembles checkpoint at every round boundary and restore onto an equivalently constructed instance, so an interrupted run picks up where it stopped. Within an interrupted round only work that no valid child artefact represents is rerun; partition children become reusable because every built-in decomposition is reproducible, and non-reproducible binary decomposers are rejected before dispatch (379b3a1)
- qprog:
sampling_backendruns a program's or ensemble's final measurement on a different backend than training, for anything exposingsample_solution(4789123) - qprog:
MoleculeTransformeraccepts PySCF molecules and mean fields — geometry reads and rebuilds dispatch on the molecule type, so a sweep emits the same type it was given (c7cf7ab) - circuits:
CircuitPayloadcarries one circuit plus its labelled parameter sets, with a bound circuit as the degenerate case, and the pipeline compiles to payloads end to end.submit_circuitswidens to accept payloads,{label: circuit}mappings, Qiskit circuits and bare sequences vianormalise_circuit_batch, held across all four runners by shared contracts (a9ca3d9, faed8a0, b807301) - backends: typed Qoro job status errors, with backend status normalisation consolidated in
JobStatus.coerce()so job failures stay tied to canonical statuses (7107838) - backends: reach maestro's
pp_*knobs and match its config surface — the options are assigned after construction (maestro binds them as properties only, so any config carrying one previously raisedTypeErrorbefore submitting), theprecisionandpath_integral_thresholdknobs from 0.2.19 are exposed, cadences are validated (zero aborts maestro withSIGFPE), and thresholds that silently do nothing without a cadence now warn (2e313b2) - qasm:
validate_qasmandis_valid_qasmtake an optionalparameterscollection naming identifiers allowed as bare gate arguments, so a parametric template can be validated before substitution. Undeclared identifiers are still rejected, and a placeholder no longer raises "Unknown symbol" first and masks a real syntax error behind it (d3edce3)
🐛 Fixed
- qprog: each ansatz declares its own parameter-shift rule via
Ansatz.parameter_frequencies— the two-term ±π/2 rule is exact only when a parameter drives one gate whose generator has two distinct eigenvalues, so UCCSD's gradient was numerically zero and Hartree-Fock's off by up to 0.22, both silently. QAOA, whose layer angles have no such frequency family, now refuses a gradient instead of returning a near-zero one (2e03d6d) - qprog: repair five checkpointing defects — load dispatch covers every checkpoint-capable optimizer, so
GridSearchOptimizercheckpoints are loadable rather than writable-only;IterativeQAOAwrites each depth to its own subdirectory and resumes the depth schedule instead of overwriting depths and restarting at 1; runs always checkpoint their final iteration, including on early stop and cancellation, withstop_reasonsurviving a reload; and latest-checkpoint resolution skips directories left incomplete by an interrupted write (e630358) - qprog: allow beam width to exceed the number of partition candidates (ea22b8f)
- backends: flatten
ham_opsgroups and stop stripping measurements — maestro splits observables on;only, so an unflattenedXX|YYbecame one pseudo-term worth 0.0 (f4df502) - backends: hand
on_completethe decoded status payload — it received the rawrequests.Responsewhile both consumers isinstance-check a dict or list, so the callback silently did nothing andtotal_run_timestayed at zero (d415667) - pipeline: show the param-set axis for deferred-binding traces, which backends that resolve parameters themselves leave on the
MetaCircuit, so the tree no longer under-reports the expansion (3ddde8c) - scope the AI index and record SPSA's final exact loss —
_should_skipreceived absolute paths, so a checkout directory nameddivisatisfied the allowlist for every file and.claude/andexprmnts/were indexed; SPSA and QN-SPSA best-tracked the iterate the final step reached only underblocking, unlike QUIVER (231eeb6)
🔄 Changed
- deps: PennyLane, qiskit-aer and dwave-hybrid move behind optional extras — breaking: PennyLane now installs via
qoro-divi[pennylane]and is imported lazily by the code that converts PennyLane inputs,QiskitSimulatorneeds theaerextra, and partitioned QUBO solving needsqubo-decompose; each raises an error naming the install command. Together this trims over 500 MB from the core install. dimod stays in core, so unpartitioned QUBO and HUBO are unaffected, andaiis dropped from theallextra so--all-extras --no-extra aiworks again.HartreeFockAnsatzis rebuilt on Qiskit primitives, reproducing the previous circuits gate for gate at identical depth and CNOT count (171f0e6, 06eaffe, e064fc0) - reporting: event-driven progress sessions replace the reporter hierarchy — immutable progress events, a pure state reducer, and separate Rich and logging views, with
ProgressSessionowning the progress lifecycle including queued ensemble reporting, teardown, drain handling and non-interactive fallback. Compact, off and forced-logging behaviour is preserved (6d2fc67) - backends: consolidate the base contracts and group the runners —
_circuit_runner,_async_job_backendand_execution_resultmerge into one_basemodule, the concrete runners move into arunnerssubpackage, and maestro's protocol facts (QASM dialect, MPS sizing, result bit order) move to_maestro_protocol(4b7012f) - collapse duplicated pipeline, backend and ansatz plumbing — eleven near-identical blocks now have one implementation each, and two hot loops are replaced: the QWC label match becomes a dict of index queues (1.8–3.1× on that phase) and the 2-RDM spin trace four array slices (14–20×) (4ba20f1)
- vectorise bitstring processing across the expectation, parity and SQD occupation paths, and harden QH1/QH2 decoding and wide histogram rendering (3b963e5)
- self-returning methods across programs, ensembles, optimizers and config objects return
Selfinstead of a stringified concrete class, which under-reported the type for every subclass; the three optimizercopy()overrides also build viatype(self), so a subclass copy keeps its type (01c8680)
🔧 Internal
- backends: parity tests guard
MaestroConfigagainstmaestro.SimulatorConfigdrift, so a renamed or newly added simulator option fails at CI instead of at execution time (6008430) - cover the gaps the review pass found — six behaviours had no test that could fail, including every optimizer's descent from its initial cost, a second Ctrl-C during round cleanup, and the session-level HTTP retry policy (e130afb)
- pin BLAS threads to one per process — each xdist worker otherwise spawned one thread per core, making the parallel suite slower than serial (40 s pinned against 214 s unpinned) (07aaa77)
- ci: install the
chemextra for the docs and test jobs, and repair the LASSQD docs and optional tests (6570fd4, 8dc9f19)
📝 Documentation
- reorganise and streamline the guides — the monolithic User Guide is replaced by Algorithms and Execution & Workflows sections, pipeline authoring is separated from pipeline usage, and partitioning and
MaestroSimulatorare featured in the README (aadf9dd) - qprog: document the LASSQD workflow — a user-guide page on explicit and automatic fragment specification, the macro-cycle round model and how to read the workflow state, plus a linear-H4 tutorial (7d953cb)
- convert prose to British English across docstrings, RST, comments and user-facing message strings, and set
spelling_ignore_pypi_package_namestoFalse, since PyPI names a package for nearly every English word and the check was silently accepting "parameterized", "minimize" and "color" (4980c43) - mark dashboard example code blocks with
.. dashboard-example:comments and notify qoro-dash on push to main (7378b7f)