qolumbina.utils.quantum_measurement module#

Utilities for appending Pauli-basis measurements to Qiskit circuits.

This module resolves user-facing measurement specifications into the concrete objects that Qiskit needs:

  • a dense Pauli-basis list with one entry per qubit; and

  • a qubit-to-classical-bit measurement mapping.

The public entry point is QuantumMeasurement.measure(). It supports the legacy dense basis form, a sparse basis dictionary, automatic classical-bit mapping, and explicit conflict detection when both basis and mapping are provided.

class MeaBasisType(value)[source]#

Bases: Enum

Supported measurement-basis input categories.

The enum is intentionally small because measurement currently supports only Pauli bases. The value determines how the user input is normalized into a dense basis list before gates are appended to a circuit.

PAULI_STRING#

Dense basis list, for example ["x", "i", "z"]. The list must cover every qubit of the measured circuit.

PAULI_MAPPING#

Sparse basis mapping, for example {0: "x", 3: "z"}. Unspecified qubits are treated as "i" and are therefore not measured.

class QuantumMeasurement(qc, meas_basis, meas_mapping)[source]#

Bases: object

Append Pauli-basis measurement operations to a circuit.

QuantumMeasurement separates two concerns:

  • meas_basis decides which qubits are measured and in which Pauli basis. A basis value of "i" means identity/no measurement.

  • meas_mapping decides where each measured qubit is written in the classical register.

The two specifications must agree after basis resolution. In other words, the set of qubits whose basis is not "i" must exactly match meas_mapping.keys(). This explicit conflict check prevents accidental full-circuit measurements when the caller intended a partial measurement.

Supported Pauli basis labels are "x", "y", "z", and "i". Measurement in X and Y basis is implemented by applying the standard basis rotations before Qiskit’s computational-basis measurement.

Create a measurement appender for an already allocated circuit.

The constructor does not modify qc. Call apply() to append the actual gates, or use the convenience constructor QuantumMeasurement.measure().

Parameters:
  • qc (qiskit.QuantumCircuit) – Circuit that already contains the required classical bits.

  • meas_basis (list[str] or dict[int, str]) – Dense or sparse Pauli measurement basis. A dense list must cover every qubit in qc. A sparse dict leaves unspecified qubits unmeasured.

  • meas_mapping (dict[int, int]) – Mapping from measured qubit index to classical-bit index. Its keys must exactly match the measured qubits.

PAULI_BASIS_SET = {'i', 'x', 'y', 'z'}#
PAULI_STRING_TRANS = {'i': None, 'x': '_pauli_x_measure', 'y': '_pauli_y_measure', 'z': '_pauli_z_measure'}#
apply()[source]#

Append the resolved measurement gates to self.qc.

This method normalizes sparse basis inputs to a dense Pauli string, validates basis labels and mapping indices, checks that basis and mapping describe the same measured qubits, and then appends the appropriate basis-rotation and measurement gates.

Raises:
  • TypeError – If the measurement mapping has invalid key/value types.

  • ValueError – If the dense basis length does not match the circuit width, if basis labels are unsupported, if qubit/classical indices are invalid, if multiple qubits map to the same classical bit, or if basis and mapping conflict.

Return type:

None

classmethod measure(qc, meas_basis, input_meas_mapping=None)[source]#

Resolve a measurement specification and append measurement gates.

Measurement specification is resolved as follows:

  • If both meas_basis and input_meas_mapping are None, every qubit is measured in Pauli-Z basis with the default q_i -> c_i mapping. This preserves the original full-measurement behavior.

  • If input_meas_mapping is provided but meas_basis is None, only the qubits appearing in the mapping keys are measured, all in Pauli-Z basis.

  • If meas_basis is provided but input_meas_mapping is None, only qubits whose basis is not "i" are measured. Classical bits are assigned contiguously in ascending qubit-index order; for example, measured qubits [0, 3, 4] map to classical bits [0, 1, 2].

  • If both are provided, the non-"i" qubits from meas_basis must exactly match input_meas_mapping.keys(). Missing or extra mapping entries are treated as conflicts and raise ValueError.

meas_basis can be a dense list[str] covering all circuit qubits or a sparse dict[int, str] whose unspecified qubits are treated as "i".

Important

qc must already have enough classical bits for the resolved mapping. If the caller does not know the required width in advance, call required_clbits() before constructing qc.

Examples

Control only input_meas_mapping. The basis is omitted, so only the mapped qubits are measured and they default to Pauli-Z basis:

qc = QuantumCircuit(5, 2)

QuantumMeasurement.measure(
    qc,
    meas_basis=None,
    input_meas_mapping={1: 0, 4: 1},
)

# Resolved basis: ["i", "z", "i", "i", "z"]
# Resolved mapping: {1: 0, 4: 1}
# Measurements appended: q1 -> c0, q4 -> c1.

Control only meas_basis. The mapping is omitted, so classical bits are assigned contiguously in ascending measured-qubit order:

qc = QuantumCircuit(5, 3)

QuantumMeasurement.measure(
    qc,
    meas_basis=["z", "i", "i", "x", "y"],
)

# Measured qubits: [0, 3, 4]
# Resolved mapping: {0: 0, 3: 1, 4: 2}
# q0 is measured in Z, q3 in X, and q4 in Y.

Use sparse meas_basis and explicit input_meas_mapping together. The two specifications are accepted only when their qubit sets match:

qc = QuantumCircuit(6, 2)

QuantumMeasurement.measure(
    qc,
    meas_basis={2: "x", 5: "z"},
    input_meas_mapping={2: 1, 5: 0},
)

# Resolved basis: ["i", "i", "x", "i", "i", "z"]
# Resolved mapping: {2: 1, 5: 0}

A conflicting pair raises ValueError:

QuantumMeasurement.measure(
    qc,
    meas_basis={2: "x", 5: "z"},
    input_meas_mapping={2: 1, 4: 0},
)
Parameters:
  • qc (qiskit.QuantumCircuit) – Circuit to mutate by appending measurement operations.

  • meas_basis (list[str] or dict[int, str] or None) – Dense basis list, sparse basis mapping, or None.

  • input_meas_mapping (dict[int, int] or None) – Optional explicit qubit-to-classical-bit mapping.

Returns:

The same circuit object after measurement gates are appended.

Return type:

qiskit.QuantumCircuit

Raises:
  • TypeError – If basis or mapping containers have invalid key/value types.

  • ValueError – If the specification is inconsistent, if the circuit has insufficient classical bits, or if an index is outside the valid range.

classmethod required_clbits(num_qubits, meas_basis, input_meas_mapping=None)[source]#

Return the classical-register width required by a measurement spec.

Callers that allocate a circuit before invoking measure() should use this helper so that allocation, default handling, and conflict checks follow the same rules as the measurement implementation.

The returned value is max(meas_mapping.values()) + 1 after the specification is resolved. This preserves sparse explicit classical-bit mappings. For example, {2: 5} requires six classical bits even though it measures only one qubit.

Parameters:
  • num_qubits (int) – Number of qubits in the circuit that will be measured.

  • meas_basis (list[str] or dict[int, str] or None) – Dense basis list, sparse basis mapping, or None.

  • input_meas_mapping (dict[int, int] or None) – Optional explicit qubit-to-classical-bit mapping.

Returns:

Minimum classical register width needed by the resolved measurement mapping.

Return type:

int

Raises:
  • TypeError – If basis or mapping containers have invalid key/value types.

  • ValueError – If the measurement specification is invalid or internally inconsistent.

classmethod resolve_measurement_spec(num_qubits, meas_basis, input_meas_mapping=None)[source]#

Resolve measurement inputs into a dense basis and concrete mapping.

The returned basis always has length num_qubits. The returned mapping contains exactly the qubits whose basis is not "i".

This method is the single source of truth for all default handling:

  • None/None becomes full Pauli-Z measurement.

  • mapping-only input becomes sparse Pauli-Z measurement on mapped qubits.

  • basis-only input gets contiguous classical bits in ascending qubit order.

  • basis-plus-mapping input is accepted only if the measured qubit sets match exactly.

Parameters:
  • num_qubits (int) – Number of qubits in the measured circuit.

  • meas_basis (list[str] or dict[int, str] or None) – Dense basis list, sparse basis mapping, or None.

  • input_meas_mapping (dict[int, int] or None) – Optional explicit qubit-to-classical-bit mapping.

Returns:

(resolved_basis, resolved_mapping) where resolved_basis is a dense list of length num_qubits and resolved_mapping maps each measured qubit to a classical bit.

Return type:

tuple[list[str], dict[int, int]]

Raises:
  • TypeError – If basis or mapping containers have invalid key/value types.

  • ValueError – If num_qubits is invalid, if dense basis length is wrong, if indices are out of range, if basis labels are invalid, or if basis and mapping conflict.

infer_measurement_basis_type(mea_input)[source]#

Infer the measurement-basis input category.

Supported inputs are:

  • list[str]: dense Pauli basis list.

  • dict[int, str]: sparse mapping from qubit index to Pauli basis.

This function validates only the container and element types. Semantic checks such as valid basis labels, valid qubit ranges, dense-list length, and mapping compatibility are handled later by QuantumMeasurement.

Parameters:

mea_input (list[str] or dict[int, str]) – Measurement-basis input to classify.

Returns:

The inferred basis input category.

Return type:

MeaBasisType

Raises:
  • TypeError – If mea_input is not list[str] or dict[int, str].

  • ValueError – If mea_input is an empty dense list.