Customized quantum gate¶
The numqi.sim
submodule supports customized quantum gate, even the not-unitary "gate"
import numpy as np
try:
import numqi
except ImportError:
%pip install numqi
import numqi
Let's define a measurement "gate"
class MeasureGate:
def __init__(self, index, seed=None, name='measure'):
self.kind = 'custom'
self.name = name
self.requires_grad = False
index = numqi.utils.hf_tuple_of_int(index)
assert all(x==y for x,y in zip(sorted(index),index)), 'index must be sorted'
self.index = index
self.np_rng = numqi.random.get_numpy_rng(seed)
self.bitstr = None
self.probability = None
def forward(self, q0):
self.bitstr,self.probability,q1 = numqi.sim.state.measure_quantum_vector(q0, self.index, self.np_rng)
return q1
The function numqi.sim.state.measure_quantum_vector
is used to measure a quantum vector on some of qubits. After the measurement, thoes qubits will be collapsed to the computational basis state self.bitstr
according to the probability distribution of the measurement result self.probablity
. The final quantum state will become q1
.
Actually, numqi.sim
has builtin measurement operation, let's name this newer one as measure_custom()
.
circ = numqi.sim.Circuit()
circ.register_custom_gate('measure_custom', MeasureGate)
circ.H(0)
circ.cnot(0, 1)
gate_measure = circ.measure_custom(index=(0,1))
Above, we make a Bell state and measure it.
q0 = numqi.sim.state.new_base(num_qubit=2)
q1 = circ.apply_state(q0)
print(f'{gate_measure.bitstr=}')
print(f'{gate_measure.probability=}')
print(f'{q1=}')
gate_measure.bitstr=[1, 1] gate_measure.probability=array([0.5, 0. , 0. , 0.5]) q1=array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
As expected, only 00
and 11
are possible. Let's make a GHZ state.
circ = numqi.sim.Circuit()
circ.register_custom_gate('measure_custom', MeasureGate)
circ.H(0)
circ.cnot(0, 1)
circ.cnot(1, 2)
gate_measure = circ.measure_custom(index=(0,1,2))
q0 = numqi.sim.state.new_base(num_qubit=3)
q1 = circ.apply_state(q0)
print(f'{gate_measure.bitstr=}')
print(f'{gate_measure.probability=}')
print(f'{q1=}')
gate_measure.bitstr=[1, 1, 1] gate_measure.probability=array([0.5, 0. , 0. , 0. , 0. , 0. , 0. , 0.5]) q1=array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
One can also measure many times, and one can insert quantum circuits between these measurements.
num_qubit = 5
num_layer = 3
measure_gate_list = []
circ = numqi.sim.Circuit()
circ.register_custom_gate('measure_custom', MeasureGate)
np_rng = np.random.default_rng()
for _ in range(num_layer):
for ind0 in range(num_qubit):
circ.u3(ind0, args=np_rng.uniform(0, 2*np.pi, size=3))
tmp0 = list(range(0, num_qubit-1, 2)) + list(range(1, num_qubit-1, 2))
for ind0 in tmp0:
circ.cnot(ind0, ind0+1)
measure_gate_list.append(circ.measure_custom(index=(0,1)))
q0 = numqi.sim.state.new_base(num_qubit)
q1 = circ.apply_state(q0)
print(np.linalg.norm(q1)) #1
print('probability:', measure_gate_list[0].probability)
for ind0,gate_i in enumerate(measure_gate_list):
print(f'[gate-{ind0}] bitstr:', gate_i.bitstr)
0.9999999999999999 probability: [0.69564597 0.03538523 0.01301931 0.25594949] [gate-0] bitstr: [1, 1] [gate-1] bitstr: [0, 0] [gate-2] bitstr: [0, 1]