Skip to content

qiu_quantum_computing.state_preparation

Circuits preparing a quantum state from the all-zero state.

Functions:

state_preparation_circuit

state_preparation_circuit(state: Statevector | ArrayLike, *, method: SynthesisMethod = GATE, inverse: bool = False) -> QuantumCircuit

Return a circuit preparing the state from the all-zero state.

The circuit implements a unitary U with U|0...0> = |state> exactly, including the global phase. Its inverse, with inverse=True, maps the state back to the all-zero state.

Parameters:

  • state (Statevector | ArrayLike) –

    The normalized state to prepare.

  • method (SynthesisMethod, default: GATE ) –

    How the preparation is represented in the circuit: - GATE (default): a single Qiskit StatePreparation gate, synthesized by Qiskit when transpiling. Note that Qiskit's synthesis prepares wrong states for some inputs (qiskit 2.2 to 2.5), see decomposed_state_preparation. - DECOMPOSED: elementary ry, rz and cx gates, synthesized by decomposed_state_preparation. - DENSE: a single unitary gate of the matrix of state_preparation_unitary.

  • inverse (bool, default: False ) –

    If True, return the circuit mapping the state to the all-zero state.

Returns:

Source code in packages/qiu-quantum-computing/src/qiu_quantum_computing/state_preparation.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def state_preparation_circuit(
    state: Statevector | npt.ArrayLike,
    *,
    method: SynthesisMethod = SynthesisMethod.GATE,
    inverse: bool = False,
) -> QuantumCircuit:
    """Return a circuit preparing the state from the all-zero state.

    The circuit implements a unitary `U` with `U|0...0> = |state>` exactly, including
    the global phase. Its inverse, with `inverse=True`, maps the state back to the
    all-zero state.

    Args:
        state: The normalized state to prepare.
        method: How the preparation is represented in the circuit:
            - `GATE` (default): a single Qiskit `StatePreparation` gate, synthesized
              by Qiskit when transpiling. Note that Qiskit's synthesis prepares
              wrong states for some inputs (qiskit 2.2 to 2.5), see
              `decomposed_state_preparation`.
            - `DECOMPOSED`: elementary `ry`, `rz` and `cx` gates, synthesized by
              `decomposed_state_preparation`.
            - `DENSE`: a single unitary gate of the matrix of
              `state_preparation_unitary`.
        inverse: If True, return the circuit mapping the state to the all-zero state.

    Returns:
        The state preparation circuit.
    """
    statevector = validated_statevector(state)
    method = SynthesisMethod(method)

    if method == SynthesisMethod.DECOMPOSED:
        circuit = decomposed_state_preparation(statevector)
        return circuit.inverse() if inverse else circuit

    circuit = QuantumCircuit(statevector.num_qubits, name="state_preparation")
    if method == SynthesisMethod.GATE:
        circuit.append(StatePreparation(statevector, inverse=inverse), circuit.qubits)
    elif method == SynthesisMethod.DENSE:
        unitary = state_preparation_unitary(statevector)
        circuit.unitary(
            unitary.adjoint() if inverse else unitary,
            circuit.qubits,
            label="state_preparation_dg" if inverse else "state_preparation",
        )
    else:
        raise NotImplementedError(f"Unsupported synthesis method: {method.value!r}")
    return circuit

decomposed_state_preparation

decomposed_state_preparation(state: Statevector | ArrayLike) -> QuantumCircuit

Return a circuit of elementary gates preparing the state from the all-zero state.

The synthesis of Möttönen et al., "Transformation of quantum states using uniformly controlled rotations" (2005), first sets the magnitudes of the amplitudes with uniformly controlled ry rotations, from the most significant qubit down, and then their phases with uniformly controlled rz rotations and the global phase of the circuit. It uses at most 2**(n+1) - 4 CNOT gates for n qubits, and skips the phase stage for real non-negative states.

All angles are computed with arctan2 and sums, without eigendecompositions, unlike Qiskit's StatePreparation, whose isometry synthesis prepares wrong states when two of its intermediate single-qubit gates are close but not equal.

Parameters:

Returns:

Source code in packages/qiu-quantum-computing/src/qiu_quantum_computing/state_preparation.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def decomposed_state_preparation(state: Statevector | npt.ArrayLike) -> QuantumCircuit:
    """Return a circuit of elementary gates preparing the state from the all-zero state.

    The synthesis of Möttönen et al., "Transformation of quantum states using
    uniformly controlled rotations" (2005), first sets the magnitudes of the
    amplitudes with uniformly controlled `ry` rotations, from the most significant
    qubit down, and then their phases with uniformly controlled `rz` rotations and
    the global phase of the circuit. It uses at most `2**(n+1) - 4` CNOT gates for
    `n` qubits, and skips the phase stage for real non-negative states.

    All angles are computed with `arctan2` and sums, without eigendecompositions,
    unlike Qiskit's `StatePreparation`, whose isometry synthesis prepares wrong
    states when two of its intermediate single-qubit gates are close but not equal.

    Args:
        state: The normalized state to prepare.

    Returns:
        The circuit of `ry`, `rz` and `cx` gates.
    """
    statevector = validated_statevector(state)
    num_qubits = statevector.num_qubits
    assert num_qubits is not None  # guaranteed by the validation
    amplitudes = statevector.data

    circuit = QuantumCircuit(num_qubits, name="state_preparation")

    # Magnitudes: rotate each target qubit t, conditioned on the more significant
    # qubits t+1, ..., n-1, into the conditional distribution of its bit.
    probabilities = np.abs(amplitudes) ** 2
    for target in reversed(range(num_qubits)):
        num_controls = num_qubits - 1 - target
        marginals = probabilities.reshape(2**num_controls, 2, 2**target).sum(axis=2)
        angles = 2 * np.arctan2(np.sqrt(marginals[:, 1]), np.sqrt(marginals[:, 0]))
        circuit.compose(
            uniformly_controlled_rotation("y", angles),
            qubits=range(target, num_qubits),
            inplace=True,
        )

    # Phases: split the phases of each pair of amplitudes differing in the bit of
    # the target t into their difference, applied by an rz rotation, and their mean,
    # left to the more significant qubits and finally to the global phase.
    phases = np.where(probabilities > 0, np.angle(amplitudes), 0.0)
    for target in range(num_qubits):
        pairs = phases.reshape(-1, 2)
        circuit.compose(
            uniformly_controlled_rotation("z", pairs[:, 1] - pairs[:, 0]),
            qubits=range(target, num_qubits),
            inplace=True,
        )
        phases = pairs.mean(axis=1)
    circuit.global_phase = phases[0]

    return circuit

state_preparation_unitary

state_preparation_unitary(state: Statevector | ArrayLike) -> Operator

Return a dense unitary whose first column is the state.

The unitary is -e^(i theta) H, where H is the Householder reflection swapping |0...0> and -e^(-i theta)|state>, with theta the phase of the first amplitude. It is exact and numerically stable, and takes O(4**n) memory.

Parameters:

Returns:

  • Operator –

    The unitary operator U with U|0...0> = |state>.

Source code in packages/qiu-quantum-computing/src/qiu_quantum_computing/state_preparation.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def state_preparation_unitary(state: Statevector | npt.ArrayLike) -> Operator:
    """Return a dense unitary whose first column is the state.

    The unitary is `-e^(i theta) H`, where `H` is the Householder reflection swapping
    `|0...0>` and `-e^(-i theta)|state>`, with `theta` the phase of the first
    amplitude. It is exact and numerically stable, and takes `O(4**n)` memory.

    Args:
        state: The normalized state to prepare.

    Returns:
        The unitary operator `U` with `U|0...0> = |state>`.
    """
    amplitudes = validated_statevector(state).data
    theta = np.angle(amplitudes[0]) if amplitudes[0] != 0 else 0.0

    # rotated such that the first amplitude is real and non-negative
    rotated = np.exp(-1j * theta) * amplitudes
    rotated[0] = abs(amplitudes[0])

    # reflecting along u = rotated + |0>, whose norm is at least 1 for stability
    u = rotated.copy()
    u[0] += 1
    reflection = np.eye(u.size) - 2 * np.outer(u, u.conj()) / np.vdot(u, u).real

    return Operator(-np.exp(1j * theta) * reflection)