In [1]:
Copied!
import numpy as np
try:
import numqi
except ImportError:
%pip install numqi
import numqi
np_rng = np.random.default_rng()
import numpy as np
try:
import numqi
except ImportError:
%pip install numqi
import numqi
np_rng = np.random.default_rng()
First, we build a variational quantum circuit ansatz for searching the Quantum Error Correction Code (QECC). We are going to find the five-qubit QECC wiki/five-qubit-code.
In [2]:
Copied!
def build_circuit(num_depth, num_qubit):
prime = [x for x in range(3, num_qubit) if np.gcd(x,num_qubit)==1]
assert len(prime)>=1
prime = prime[0]
circ = numqi.sim.Circuit(default_requires_grad=True)
for _ in range(num_depth):
for x in range(num_qubit):
circ.u3(x)
tmp0 = np.mod(-np.arange(num_qubit+1), num_qubit)
for x,y in zip(tmp0[:-1],tmp0[1:]):
circ.cu3(x, y)
for x in range(num_qubit):
circ.u3(x)
tmp0 = np.mod(-np.arange(num_qubit+1)*prime, num_qubit)
for x,y in zip(tmp0[:-1],tmp0[1:]):
circ.cu3(x, y)
return circ
def build_circuit(num_depth, num_qubit):
prime = [x for x in range(3, num_qubit) if np.gcd(x,num_qubit)==1]
assert len(prime)>=1
prime = prime[0]
circ = numqi.sim.Circuit(default_requires_grad=True)
for _ in range(num_depth):
for x in range(num_qubit):
circ.u3(x)
tmp0 = np.mod(-np.arange(num_qubit+1), num_qubit)
for x,y in zip(tmp0[:-1],tmp0[1:]):
circ.cu3(x, y)
for x in range(num_qubit):
circ.u3(x)
tmp0 = np.mod(-np.arange(num_qubit+1)*prime, num_qubit)
for x,y in zip(tmp0[:-1],tmp0[1:]):
circ.cu3(x, y)
return circ
hyper-parameters for the QECC specification
In [3]:
Copied!
str_qecc = '((5,2,3))' #'((6,2,de(2)=4))'
tmp0 = numqi.qec.parse_str_qecc(str_qecc)
num_qubit = tmp0['num_qubit']
num_logical_dim = tmp0['num_logical_dim']
distance = tmp0['distance']
weight_z = tmp0['weight_z']
num_layer = 5
if weight_z is None:
error_list = numqi.qec.make_error_list(num_qubit, distance)
else:
error_list = numqi.qec.make_asymmetric_error_set(num_qubit, distance, weight_z)
str_qecc = '((5,2,3))' #'((6,2,de(2)=4))'
tmp0 = numqi.qec.parse_str_qecc(str_qecc)
num_qubit = tmp0['num_qubit']
num_logical_dim = tmp0['num_logical_dim']
distance = tmp0['distance']
weight_z = tmp0['weight_z']
num_layer = 5
if weight_z is None:
error_list = numqi.qec.make_error_list(num_qubit, distance)
else:
error_list = numqi.qec.make_asymmetric_error_set(num_qubit, distance, weight_z)
In [4]:
Copied!
circuit = build_circuit(num_layer, num_qubit)
model = numqi.qec.VarQEC(circuit, num_logical_dim, error_list, loss_type='L2')
theta_optim = numqi.optimize.minimize(model, ('uniform',0,2*np.pi), num_repeat=1, tol=1e-10, print_freq=40)
code0 = model.get_code()
theta_optim = numqi.optimize.minimize(model, ('uniform',0,2*np.pi), num_repeat=1, tol=1e-10, print_freq=40)
code1 = model.get_code()
circuit = build_circuit(num_layer, num_qubit)
model = numqi.qec.VarQEC(circuit, num_logical_dim, error_list, loss_type='L2')
theta_optim = numqi.optimize.minimize(model, ('uniform',0,2*np.pi), num_repeat=1, tol=1e-10, print_freq=40)
code0 = model.get_code()
theta_optim = numqi.optimize.minimize(model, ('uniform',0,2*np.pi), num_repeat=1, tol=1e-10, print_freq=40)
code1 = model.get_code()
[step=0][time=0.183 seconds] loss=6.127519236311905
[step=40][time=4.045 seconds] loss=0.04793809085918552
[step=80][time=4.028 seconds] loss=2.9884811442195e-05
[step=120][time=4.051 seconds] loss=1.9988066186261673e-08
[round=0] min(f)=8.568652343942634e-10, current(f)=8.568652343942634e-10
[step=0][time=0.256 seconds] loss=4.786113901253431
[step=40][time=4.128 seconds] loss=0.001263181101381637
[step=80][time=4.111 seconds] loss=3.6289952687308665e-07
[round=0] min(f)=7.735472817216684e-10, current(f)=7.735472817216684e-10
Above, we run the search algorithm algorithm and obtain two QECC ((5,2,3))
. We can run an optimization algorithm to determine whether the two QECCs are equivalent.
In [5]:
Copied!
model1 = numqi.qec.QECCEqualModel(code0, code1)
tmp0 = numqi.optimize.minimize(model, 'uniform', num_repeat=1, tol=1e-10, print_freq=40)
if tmp0.fun<1e-5:
print('equivalent QECC')
model1 = numqi.qec.QECCEqualModel(code0, code1)
tmp0 = numqi.optimize.minimize(model, 'uniform', num_repeat=1, tol=1e-10, print_freq=40)
if tmp0.fun<1e-5:
print('equivalent QECC')
[step=0][time=0.176 seconds] loss=8.83264861343416
[step=40][time=4.024 seconds] loss=0.2505768926837576
[step=80][time=4.095 seconds] loss=0.010512615437324256
[step=120][time=4.231 seconds] loss=0.00029034812886661785
[step=160][time=4.348 seconds] loss=7.288458845195527e-06
[step=200][time=4.115 seconds] loss=1.7468032555850736e-07
[step=240][time=3.951 seconds] loss=2.5738333567362738e-09
[round=0] min(f)=1.8058332274049393e-09, current(f)=1.8058332274049393e-09 equivalent QECC
As expected, all ((5,2,3))
QECC are local equivalent. Besides the variational ansatz, we can also directly parametrize a unitary matrix.