Skip to main content

Quantum states

Quantum states are essential when working on quantum computing. Here we describe basic treatment of them in QURI Parts.

Prerequisite

QURI Parts modules used in this tutorial: quri-parts-circuit and quri-parts-core. You can install them as follows:

!pip install quri-parts

List of quantum state classes and the quantum_state function

Here is the list of quantum states classes in QURI Parts:

  • ComputationalBasisState (source)
  • GeneralCircuitQuantumState (source)
  • QuantumStateVector (source)
  • ParametricCircuitQuantumState (source)
  • ParametricQuantumStateVector (source)

As name suggests, each class has its own way to describe a quantum state. Let's see how we can work with them. The last two states will not be explaind in this tutorial since they use ParametricCircuit inside. Please refer to parametric circuits and states tutorial about them.

It is not necessary to remember all the names of the quantum state classes as we provide a quantum_state function to construct them automatically. The quantum_state function takes in the number of qubits as a positional argument and a subset of (bits, circuit, vector) as keyword argument. Here, we list out the output type of quantum_state with different input combinations.

  • Non-parametric state:

    • quantum_state(n_qubits) \rightarrow ComputationalBasisState
    • quantum_state(n_qubits, bits) \rightarrow ComputationalBasisState
    • quantum_state(n_qubits, circuit) \rightarrow GeneralCircuitQuantumState
    • quantum_state(n_qubits, vector) \rightarrow QuantumStateVector
    • quantum_state(n_qubits, bits, circuit) \rightarrow GeneralCircuitQuantumState
    • quantum_state(n_qubits, vector, circuit) \rightarrow QuantumStateVector
  • Parametric state

    • quantum_state(n_qubits, parametric_circuit) \rightarrow ParametricCircuitQuantumState
    • quantum_state(n_qubits, bits, parametric_circuit) \rightarrow ParametricCircuitQuantumState
    • quantum_state(n_qubits, vector, parametric_circuit) \rightarrow ParametricQuantumStateVector

Next, we start to introduce all of these quantum state objects.

CircuitQuantumState

CircuitQuantumState is an interface for classes representing a quantum state generated by applying a circuit to 000|00\cdots 0\rangle state. ComputationalBasisState and GeneralCircuitQuantumState are the concrete instances of CircuitQuantumState

ComputationalBasisState

ComputationalBasisState represents a computational basis state. It consists of n_qubits, bits represented as an int, and phase. Note that the bits keyword that sets the computational state follows the little endian convention. For example, a state with bits = 0b10 represents a computational basis state with its zeroth qubit being 0 and first qubit being 1, i.e., 10|10\rangle state (in QURI Parts, zeroth qubit comes first to the right). A computational basis state can also be considered as a state given as a result of applying Pauli gates to 000|00\cdots 0\rangle state.

from quri_parts.core.state import quantum_state, ComputationalBasisState

cb_state = quantum_state(5, bits=0b10101)
print(cb_state)

# Or equivalently:
# cb_state = ComputationalBasisState(5, bits=0b10101)
# output
ComputationalBasisState(qubit_count=5, bits=0b10101, phase=0π/2)

ComputationalBasisState has some properties and methods:

from quri_parts.circuit import X

print("(ComputationalBasisState)")

# Bits
print("bits:", bin(cb_state.bits))
# Phase
print("phase:", cb_state.phase)
# Create a new state with a Pauli gate applied
pauli_added_state = cb_state.with_pauli_gate_applied(X(0))
print("new state:\n", pauli_added_state)
# output
(ComputationalBasisState)
bits: 0b10101
phase: 0.0
new state:
ComputationalBasisState(qubit_count=5, bits=0b10100, phase=0π/2)

GeneralCircuitQuantumState

You can create a CircuitQuantumState with a quantum circuit object by GeneralCircuitQuantumState:

from quri_parts.circuit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.add_Z_gate(0)
circuit.add_H_gate(1)

# A quantum state of 2 qubits with a given circuit (i.e. C|00> where C is the ciruict)
circuit_state = quantum_state(2, circuit=circuit)

# Or equivalently,
# from quri_parts.core.state import GeneralCircuitQuantumState
# circuit_state = GeneralCircuitQuantumState(2, circuit)

# A quantum state of 2 qubits with an empty circuit (i.e. |00>)
# circuit_state = GeneralCircuitQuantumState(2)

Note that the ComputationalBasisState we introduced in the previous section is also a CircuitQuantumState, since such a state can always be constructed by applying a circuit to a zero state.

CircuitQuantumState has some properties and methods:

from quri_parts.circuit import CNOT

print("(circuit_state)")

# Get how many qubits this state is for.
print("qubit_count:", circuit_state.qubit_count)
# Get the circuit of the state. This returns an immutable circuit.
print("circuit:", circuit_state.circuit)
# Create a new state with some new gates added.
gate_added_state = circuit_state.with_gates_applied([X(1), CNOT(1, 0)])
print("original circuit len:", len(circuit_state.circuit.gates))
print("new circuit len:", len(gate_added_state.circuit.gates))
# output
(circuit_state)
qubit_count: 2
circuit: <quri_parts.circuit.circuit.ImmutableQuantumCircuit object at 0x10d789370>
original circuit len: 2
new circuit len: 4

Note that the state created by applying quantum circuit to a ComputationalBasisState is no longer a ComputationalBasisState but GeneralCircuitQuantumState in general.

You can create the superposition of two ComputationalBasisStates using comp_basis_superposition. The output will be a GeneralCircuitQuantumState. In QURI Parts, the superposition of 2 computational basis states s0|s_0\rangle and s1|s_1\rangle is defined as ψ=cosθs0+sinθeiϕs1|\psi\rangle = \cos \theta | s_0\rangle + \sin \theta e^{i\phi}| s_1\rangle

from math import pi

from quri_parts.core.state import comp_basis_superposition

new_state = cb_state.with_gates_applied([CNOT(0, 1)])
print("new state:\n", new_state)

cb_state1 = quantum_state(2, bits=0b00)
cb_state2 = quantum_state(2, bits=0b11)

superposition_state = comp_basis_superposition(cb_state1, cb_state2, theta=-pi/4, phi=0.0)
print("superposition state circuit:\n", superposition_state.circuit.gates)
# output
new state:
GeneralCircuitQuantumState(n_qubits=5, circuit=<quri_parts.circuit.circuit.ImmutableQuantumCircuit object at 0x1079e3c70>)
superposition state circuit:
(QuantumGate(name='PauliRotation', target_indices=(0, 1), control_indices=(), classical_indices=(), params=(1.5707963267948966,), pauli_ids=(1, 1), unitary_matrix=()), QuantumGate(name='RZ', target_indices=(0,), control_indices=(), classical_indices=(), params=(-1.5707963267948966,), pauli_ids=(), unitary_matrix=()))

QuantumStateVector

QuantumStateVector represents a state defined by a state vector with an optional circuit to be applied.

from quri_parts.core.state import QuantumStateVector
import numpy as np

circuit = QuantumCircuit(2, gates=[X(0)])

sv = quantum_state(2, vector=np.array([1.0, 0.0, 0.0, 0.0]), circuit=circuit)
# Or equivalently,
# sv = QuantumStateVector(2, [1.0, 0.0, 0.0, 0.0], circuit)

QuantumStateVector also have some properties:

print("(quantum state vector)")
# Get how many qubits this state is for.
print("qubit_count:", sv.qubit_count)
# Get the circuit of the state. This returns an immutable circuit.
print("circuit:", sv.circuit)
# Get the vector of the state.
print("vector:", sv.vector)
# Create a new state with some new gates added.
gate_added_state = sv.with_gates_applied([X(1), CNOT(1, 0)])
print("original circuit len:", len(sv.circuit.gates))
print("new circuit len:", len(gate_added_state.circuit.gates))
# output
(quantum state vector)
qubit_count: 2
circuit: <quri_parts.circuit.circuit.ImmutableQuantumCircuit object at 0x10d77e130>
vector: [1.+0.j 0.+0.j 0.+0.j 0.+0.j]
original circuit len: 1
new circuit len: 3
info

Although QuantumStateVector holds a circuit, .vector attribute does not return a state vector updated by the circuit. If you want to get an updated one, consider to use evaluate_state_to_vector in quri-parts-qulacs module. For more details, please refer to simulator tutorial.

info

Classes for quantum states in QURI Parts are always immutable; you cannot modify an already created quantum state object.

Apply circuit to state

QURI Parts provides a useful helper function: apply_circuit for applying a quantum circuit to an existing state. It automatically returns the correct quantum state type.

from quri_parts.core.state import apply_circuit
# Apply a circuit to a ComputationalBasisState.
new_state1 = apply_circuit(
QuantumCircuit(2, gates=[X(1)]), cb_state
) # GeneralCircuitQuantumState

# Apply a circuit to a GeneralCircuitQuantumState.
new_state2 = apply_circuit(
QuantumCircuit(2, gates=[X(1)]), circuit_state
) # GeneralCircuitQuantumState

# Apply a circuit to a QuantumStateVector.
new_state3 = apply_circuit(QuantumCircuit(2, gates=[X(1)]), sv) # QuantumStateVector