What’s New in Version 2.1
Version 2.1 adds geometry optimization as a first-class algorithm type, isolates the vendored Q# utilities in a dedicated context so that importing QDK/Chemistry no longer mutates global interpreter state, and corrects the phase-to-energy convention used when phase estimation runs on a time-evolution unitary.
There are no breaking API changes relative to 2.0, but the phase-convention fix changes the numerical results of time-evolution QPE; see Time-evolution phase convention.
Highlights
Geometry optimization. A new
geometry_optimizeralgorithm type relaxes molecular structures on top of the nuclear derivative calculators introduced in 2.0, with a geomeTRIC-backed implementation shipped as an optional plugin.Isolated Q# context. The vendored Q# utilities load into a private
qdk.Contextinstead of the globalqsharpinterpreter, and four helpers inqdk_chemistry.utils.qsharpexpose that context for user-defined Q# operations.Corrected time-evolution phase convention. Energies recovered from \(U = e^{-iHt}\) now use \(E = -\theta / t\), and the
bits_msb_firstfield ofQpeResultfrom iterative QPE is genuinely most-significant-bit first.Python 3.14 Qiskit support. The
qiskit-extrasextra now installs on Python 3.14.
Geometry Optimization
Version 2.0 added nuclear gradients and finite-difference Hessians, but left the
optimization loop to the caller. Version 2.1 closes that gap with a
geometry_optimizer algorithm type
that drives a nuclear derivative calculator to a stationary point.
Optimizers follow the usual factory and settings conventions and are created by name:
# Structure coordinates are in Bohr
structure = Structure([[0.0, 0.0, 0.0], [0.0, 0.0, 1.4]], [1, 1])
optimizer = create("geometry_optimizer", "geometric", max_iterations=20)
The energy and gradient engine is itself an algorithm reference, so any registered nuclear derivative calculator can drive the optimization:
derivative_ref = AlgorithmRef("nuclear_derivative_calculator", "qdk_finite_difference")
derivative_ref.set("finite_difference_step", 1.0e-2)
optimizer.settings().set("derivative_calculator", derivative_ref)
Shared settings on GeometryOptimizerSettings are derivative_calculator,
max_iterations (default 300), and compute_hessian (default False; when
True, a Hessian is evaluated at the converged geometry and returned).
energy, optimized_structure, hessian, wavefunction = optimizer.run(
structure, charge=0, spin_multiplicity=1, input="sto-3g"
)
run() takes an initial Structure, the charge and spin
multiplicity, and an input that is either a basis-set name, a
BasisSet, an Orbitals, or a
Wavefunction. It returns the converged energy, the optimized
structure, an optional NuclearHessian, and the optional
converged wavefunction. The optional trailing n_inactive_orbitals argument excludes
doubly occupied orbitals from the active space used by the derivative calculator.
geomeTRIC plugin
The bundled "geometric" implementation wraps
geomeTRIC and is enabled automatically when the
package is installed:
pip install 'qdk-chemistry[plugins]'
It adds transition-state search (transition_state), a choice of coordinate system
(optimizer, one of tric, tric-p, dlc, hdlc, prim, cart), the
five geomeTRIC convergence thresholds (convergence_energy,
convergence_rms_gradient, convergence_max_gradient,
convergence_rms_displacement, convergence_max_displacement), and
print_level. As with geomeTRIC itself, all five convergence criteria must be
satisfied together.
Q# Context Management
Previously, importing qdk_chemistry initialized the process-global Q# interpreter and
forced its target profile to Base, which silently invalidated Q# code the caller had
already imported. The vendored utilities now load into a dedicated qdk.Context owned
by the library, and importing qdk_chemistry leaves the caller’s global qsharp
state untouched.
QDK requires that composed Q# callables belong to the same context, so
qdk_chemistry.utils.qsharp exposes that context directly:
get_qsharp_context()returns the shared context; define your own Q# operations against it so they compose with the chemistry circuit builders.create_qsharp_context()builds a fresh context preloaded with the chemistry utilities, forwardingtarget_profileand otherqdk.Contextoptions.set_qsharp_context()installs a caller-supplied context process-wide (passNoneto restore the default).use_qsharp_context()is a context manager that overrides the active context on the current thread only.
See Shared Q# context for worked examples of bringing your own Q# operation or your own context.
Bug Fixes
Time-evolution phase convention
For \(U(t) = e^{-iHt}\) an eigenstate of energy \(E\) accumulates phase
\(e^{-iEt}\), so phase estimation measures
\(\varphi = (-Et / 2\pi) \bmod 1\). PauliProductFormulaContainer.eigenvalue_from_phase
previously inverted this as \(E = \theta / t\), dropping the sign. It now returns
\(E = -\theta / t\).
This affects any phase estimation run — standard or iterative — whose unitary comes from a product-formula builder; qubitization-based runs, which recover \(E = \lambda\cos(\theta)\), are unchanged. Energies previously reported for time-evolution QPE have the wrong sign and should be recomputed.
Two related iterative QPE defects are fixed alongside it:
The feedback phase applied to each iteration circuit is now negated to match the same convention.
The
bits_msb_firstfield ofQpeResultis now reversed before being stored. Kitaev’s iteration extracts bits least-significant first, so the field previously held them in the opposite order to its name.
Other fixes
QpeResultfiles are now understood bypython -m qdk_chemistry.migrate, which drops the obsolete evolution-time field while preserving the result. Theqpe_resulttype tag participates in thename.type.extfilename convention. See Migrating data files between serialization versions.The migration converter rejects v1
TimeEvolutionUnitaryfiles explicitly: they do not carry the scale required by the currentUnitaryRepresentationschema and must be regenerated.The geomeTRIC plugin restores the host application’s root logging handlers and level after geomeTRIC reconfigures logging.
Fixed a flaky cube-file generation test.
Infrastructure and Packaging
Python 3.14 Qiskit support.
qiskit-extrasnow installs on Python 3.14.qiskit-aeris omitted on Linux ARM64 (aarch64) for that version only, because no cp314 manylinux aarch64 wheel is published yet; all other platforms install the full set.New
pluginsdependency.geometric>=1.0joins PySCF in thepluginsextra.