Qamomile v0.12.1 is a follow-up release on top of v0.12.0, focused on frontend ergonomics and bug fixes. The frontend gains two ergonomic improvements — single-qubit gates can now broadcast over a Vector[Qubit] (so a whole-register layer is one line instead of an explicit loop), and sub-@qkernel call sites accept raw Python literals (int, float, bool) for UInt / Float / Bit parameters. QURI Parts now accepts linear combinations of parameters such as gamma * Jij, fixing a long-standing transpile error in the canonical QAOA pattern. Two compiler bugs are fixed (the QAOA-blocking nested-loop name collision, and the QURI Parts limitation above), and the qaoa_graph_partition tutorial is rewritten to use the OMMX SampleSet API end-to-end.
pip install qamomile==0.12.1New Features¶
Broadcast single-qubit gates over qubit arrays¶
Single-qubit gates (h, x, y, z, s, t, sdg, tdg, rx, ry, rz, p) now accept a Vector[Qubit] directly, applying the gate to every element of the register. The broadcast form lowers to the same ForOperation that a hand-written for i in qmc.range(n): q[i] = qmc.h(q[i]) would emit (and analogously for any other single-qubit gate), so resource estimation, transpilation, and visualization all see the same IR — only the source is shorter. For rotation gates, the same scalar angle is shared across every qubit; per-qubit angles still need an explicit index loop into a Vector[Float] (#360).
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def rotation_layer_broadcast(n: qmc.UInt, theta: qmc.Float) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
q = qmc.h(q) # broadcast Hadamard over the whole register
q = qmc.ry(q, theta) # shared scalar angle over the whole register
return qmc.measure(q)
transpiler = QiskitTranspiler()
exe = transpiler.transpile(
rotation_layer_broadcast,
bindings={"n": 4},
parameters=["theta"],
)See the updated Tutorial 02 — Parameterized Quantum Kernels for the broadcast section, which contrasts the broadcast and explicit-loop forms side by side.
Auto-promote scalar literals at sub-qkernel call sites¶
When a sub-@qkernel declares a scalar parameter typed qmc.UInt, qmc.Float, or qmc.Bit, the call site now accepts raw Python literals — int is promoted to UInt, int / float to Float, and bool to Bit. Writing helper(q, 0, 0.5) is equivalent to helper(q, qmc.uint(0), qmc.float_(0.5)). This mirrors the literal-acceptance pattern already used by built-in gate primitives such as qmc.rx, which has always accepted float | Float for its angle. As a beneficial side effect, scalar default values like def f(n: qmc.UInt = 4) now work without explicitly wrapping the default in a Handle constructor (#372).
The promotion is conservative. It triggers only when the declared type is exactly one of the three scalar Handle classes and the literal is the matching primitive. bool is intentionally excluded from the int → UInt and int → Float paths so that True cannot silently become UInt(1). Already-Handle arguments and array-typed parameters pass through unchanged, so existing symbolic-execution paths are unaffected.
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def rotate_first(
q: qmc.Vector[qmc.Qubit],
idx: qmc.UInt,
angle: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
q[idx] = qmc.ry(q[idx], angle)
return q
@qmc.qkernel
def helper_with_literals(n: qmc.UInt) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
q = rotate_first(q, 0, 0.5) # int and float literals are accepted directly
return qmc.measure(q)
transpiler = QiskitTranspiler()
exe = transpiler.transpile(helper_with_literals, bindings={"n": 3})See the updated Tutorial 06 — Reuse Patterns, which now covers the literal-promotion convenience explicitly.
Bug Fixes¶
QURI Parts now accepts linear combinations of runtime parameters (#371). The canonical QAOA pattern
qmc.rzz(q[i], q[j], gamma * Jij)previously raisedTypeErrorat emit time on QURI Parts even though the same kernel transpiled fine on Qiskit and CUDA-Q. Scalar multiples and sums of parameters now flow through, so this pattern works. Non-linear combinations (param * param,param ** n, division by a parameter) raiseQamomileQuriPartsTranspileErrorat transpile time.Nested loops with the same display name no longer collide (#365). When two
@qkernelfunctions used a loop variable with the same display name (e.g. bothfor i in qmc.range(...)), the emit-time index resolver consultedbindings[name]beforebindings[uuid]. Inlining the inner kernel pushedbindings["i"]for its own iteration, so any expression captured from the outer scope (such asarr[i]at the call site) was resolved against the inner loop’s index, producing extra runtime parameters and a spuriousassign_parametersfailure. Loop-variable bindings are now UUID-only — eachForOperationgets a freshloop_var_valueUUID, and identical display names across kernels no longer interfere.
Documentation¶
The graph-partition QAOA tutorial is rewritten to use the
ommx.v1.SampleSetAPI introduced in v0.12.0. Feasibility ratio, best feasible solution, and the objective histogram are now read directly fromsummary["feasible"],best_feasible, andsummary.loc[..., "objective"]instead of hand-rolledis_feasible/count_cut_edgeshelpers. The COBYLA cost function continues to usedecode_to_binary_sampleset()because it needs the penalty-included QUBO energy (#373).The legacy
jij-inc.github.io/QamomileGitHub Pages URL now serves a per-language redirect to the Read the Docs site (/en/and/ja/), so old bookmarks land on the corresponding language version of the current docs (#367, #368).