Qamomile v0.12.6 adds circuit inversion: qmc.inverse turns a built-in gate, a whole @qkernel, or QFT/IQFT into its inverse, and the result is called exactly like the original. New arithmetic primitives qmc.modular_increment / qmc.modular_decrement shift a computational-basis register by ±1 modulo 2^n and compose with qmc.control. The job and result types used by sample() / run() executions are now importable directly from qamomile.circuit. This release also fixes transpilation of parameter-expression gate angles, value resolution for bound Vector elements, qmc.expval qubit mapping, and HUBO instance handling in the optimization converters.
pip install qamomile==0.12.6New Features¶
Circuit inversion with qmc.inverse¶
qmc.inverse(target) returns the inverse of a built-in gate function, a @qkernel, or a stdlib QFT function. Built-in fixed gates map to their adjoint counterparts (qmc.inverse(qmc.s) is qmc.sdg, self-inverse gates such as qmc.h and qmc.cx map to themselves), rotation gates (rx, ry, rz, p, cp, rzz) negate their angle, and qmc.inverse(qmc.qft) returns qmc.iqft directly so backend-native composite emission stays available. Broadcasting over Vector[Qubit] works the same as for the forward gates (#445).
For a @qkernel, the wrapper is called with the same arguments as the original kernel and applies the body in reverse with each operation inverted. Nested kernel calls, composite gates, qmc.control(...) results, qmc.pauli_evolve (the evolution time is negated), and qmc.range loops (the iteration order is reversed without unrolling) are all inverted recursively, and inverting a kernel that itself calls qmc.inverse(...) cancels the nested inverse back to the original operations.
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def layer(q: qmc.Qubit, angle: qmc.Float) -> qmc.Qubit:
q = qmc.h(q)
q = qmc.rz(q, angle)
return q
@qmc.qkernel
def roundtrip(angle: qmc.Float) -> qmc.Bit:
q = qmc.qubit("q")
q = layer(q, angle)
q = qmc.inverse(layer)(q, angle) # uncompute: q is back to |0>
return qmc.measure(q)
executable = QiskitTranspiler().transpile(roundtrip, bindings={"angle": 0.37})Kernels containing if / while / for ... in qmc.items(...) control flow, or qmc.range bounds that are still symbolic when the inverse wrapper is traced, are not supported and raise NotImplementedError (a loop bound supplied via bindings resolves fine); kernels that allocate qubits internally are also rejected.
Modular increment and decrement¶
qmc.modular_increment and qmc.modular_decrement apply |j> -> |j ± 1 mod 2^n> to a little-endian qubit register (q[0] is the least-significant bit). The construction is QFT-free, built from X and multi-controlled X gates, and controlled variants are composed with the existing control machinery: qmc.control(qmc.modular_increment, num_controls=1). Both kernels are also importable from qamomile.circuit.algorithm.arithmetic (#453).
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def plus_one(n: qmc.UInt) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
q[1] = qmc.x(q[1]) # prepare |j=2> (little-endian)
q = qmc.modular_increment(q) # |j=2> -> |j=3>
return qmc.measure(q)
executable = QiskitTranspiler().transpile(plus_one, bindings={"n": 3})QURI Parts support is currently partial: the plain shifts run on registers of up to two qubits, and the controlled variants only on single-qubit registers.
Job and result types under qamomile.circuit¶
SampleResult, SampleJob, RunJob, ExpvalJob, Job, and JobStatus are now re-exported from qamomile.circuit. These are the types around job execution: executable.sample() returns a SampleJob whose .result() is a SampleResult, executable.run() returns a RunJob or an ExpvalJob, and Job / JobStatus are the common base class and status enum. Annotations like qmc.SampleResult no longer need the internal qamomile.circuit.transpiler.job import path, which remains importable for backward compatibility (#407).
import qamomile.circuit as qmc
def best_bitstring(result: qmc.SampleResult) -> tuple[int, ...]:
"""Pick the most sampled measurement outcome."""
((value, _count),) = result.most_common(1)
return valueBug Fixes¶
Parameter-expression gate angles no longer raise a spurious
MultipleQuantumSegmentsError. In a pure-quantum kernel, using an arithmetic expression over a runtime parameter as a gate angle —qmc.rx(q, -phase), a symbolic multi-control phase withtheta=-phase, or longer chains like(phase * 2) - 1— incorrectly split the program into multiple quantum segments at transpile time. Such expressions now stay inside the surrounding quantum segment and are folded into backend parameter expressions at emit time (#455).Compile-time-bound
Vectorelements resolve at emit time. Bound vector elements and sliced-view elements used as gate angles (e.g. the manual controlled-RY half-angle pattern-angle[i] / 2), loop bounds, helper-kernel arguments, or controlled-QPE parameters previously failed to resolve during emission, and the QPE library decomposition could not extract phases from bound array elements. All of these now resolve back to the root container, while runtime-parameter arrays and symbolic indices stay symbolic (#454, #457, #458).qmc.expvalbinds the right qubits in more layouts. Tuple-formqmc.expval((q[i],), obs)keeps the caller register’s identity through inlined sub-kernels (#448), and whole-Vectorobservables now remap Pauli indices through the compiled qubit map instead of falling back to identity when the register does not start at physical qubit 0 (#449).Controlled composite gates over parameterized sub-kernel slices can now emit on Qiskit.
qmc.control(...)of a sub-kernel that receives a classicalUIntwidth and applies QFT to aq[:m]slice previously raisedEmitError. Backends whose controlled-U emitter can convert the nested block to a gate (Qiskit) now execute it; backends that fall back to gate-by-gate controlled emission still reject this shape (#453).Optimization converters accept HUBO
ommx.v1.Instanceinputs. Problem normalization now converts instances throughto_hubo(), so objectives with degree-3+ terms reach the converters that support HUBO (such asQAOAConverter) instead of failing with an opaqueRuntimeError; converters that do not support HUBO raise a clearValueError(#459).
Documentation¶
Tutorial 04 — Controlled Gates reworks the rejected-patterns section into “Rejected and edge-case patterns”: the controlled QFT over a sub-kernel
UIntslice now runs as a supported Qiskit example instead of asserting an expectedEmitError(#453).Quantum Error Correction imports
SampleResultfrom the publicqamomile.circuitpath (#407).