Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Qamomile v0.12.6

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.6

New 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 value

Bug Fixes

Documentation

Learn More