# -*- coding: utf-8 -*-
""" run function for qulacs's cpu/gpu simulator """
import random
from collections import Counter
import cmath
import numpy as np
from qulacs import QuantumState
from qulacs import QuantumCircuit
from qulacs.gate import X, Y, Z
from qulacs.gate import H, S, Sdag, T, Tdag, sqrtX, sqrtXdag
from qulacs.gate import CNOT, CZ, SWAP
from qulacs.gate import RX, RY, RZ
from qulacs.gate import U1, U2, U3
from qulacs.gate import Measurement
from qulacs.gate import DenseMatrix
from qulacs.gate import to_matrix_gate
import qlazy.config as cfg
from qlazy.util import get_qgate_qubit_num, get_qgate_param_num, reverse_bit_order
from qlazy.Result import Result
from qlazy.QState import QState
from qlazy.CMem import CMem
GateFunctionName = {
# 1-qubit, 0-parameter gate
cfg.PAULI_X: 'X',
cfg.PAULI_Y: 'Y',
cfg.PAULI_Z: 'Z',
cfg.ROOT_PAULI_X:'sqrtX',
cfg.ROOT_PAULI_X_:'sqrtXdag',
cfg.HADAMARD:'H',
cfg.PHASE_SHIFT_S:'S',
cfg.PHASE_SHIFT_S_:'Sdag',
cfg.PHASE_SHIFT_T:'T',
cfg.PHASE_SHIFT_T_:'Tdag',
cfg.IDENTITY:'Identity',
# 1-qubit, 1-parameter gate
cfg.ROTATION_X:'RX',
cfg.ROTATION_Y:'RY',
cfg.ROTATION_Z:'RZ',
cfg.PHASE_SHIFT:'__get_P',
cfg.ROTATION_U1:'U1',
# 1-qubit, 2-parameter gate
cfg.ROTATION_U2:'U2',
# 1-qubit, 3-parameter gate
cfg.ROTATION_U3:'U3',
# 2-qubit, 0-parameters gate
cfg.CONTROLLED_X:'CNOT',
cfg.CONTROLLED_Y:'__get_CY',
cfg.CONTROLLED_Z:'CZ',
cfg.CONTROLLED_XR:'__get_CXR',
cfg.CONTROLLED_XR_:'__get_CXR_dg',
cfg.CONTROLLED_H:'__get_CH',
cfg.CONTROLLED_S:'__get_CS',
cfg.CONTROLLED_S_:'__get_CS_dg',
cfg.CONTROLLED_T:'__get_CT',
cfg.CONTROLLED_T_:'__get_CT_dg',
cfg.CONTROLLED_P:'__get_CP',
cfg.SWAP_QUBITS:'SWAP',
# 2-qubit, 1-parameters gate
cfg.CONTROLLED_RX:'__get_CRX',
cfg.CONTROLLED_RY:'__get_CRY',
cfg.CONTROLLED_RZ:'__get_CRZ',
cfg.CONTROLLED_U1:'__get_CU1',
# 2-qubit, 2-parameters gate
cfg.CONTROLLED_U2:'__get_CU2',
# 2-qubit, 3-parameters gate
cfg.CONTROLLED_U3:'__get_CU3',
}
[docs]def run_cpu(qcirc=None, shots=1, cid=None, backend=None, out_state=False, init=None):
""" run the quantum circuit (CPU) """
return __run_all(qcirc_in=qcirc, shots=shots, cid=cid, backend=backend, proc='CPU',
out_state=out_state, init=init)
[docs]def run_gpu(qcirc=None, shots=1, cid=None, backend=None, out_state=False, init=None):
""" run the quantum circuit (GPU) """
return __run_all(qcirc_in=qcirc, shots=shots, cid=cid, backend=backend, proc='GPU',
out_state=out_state, init=init)
def __run_all(qcirc_in=None, shots=1, cid=None, backend=None, proc='CPU', out_state=False, init=None):
if qcirc_in is None:
raise ValueError("quantum circuit must be specified.")
qcirc = qcirc_in.clone()
if init is not None:
raise ValueError("init option is not supported.")
qubit_num = qcirc.qubit_num
cmem_num = qcirc.cmem_num
if cid is None:
cid = list(range(cmem_num))
if cmem_num < len(cid):
raise ValueError("length of cid must be less than classical resister size of qcirc")
#
# initialize
#
if proc == 'CPU':
qstate = QuantumState(qubit_num)
else:
from qulacs import QuantumStateGpu
qstate = QuantumStateGpu(qubit_num)
cmem = [0] * cmem_num
#
# before measurement gate
#
while True:
kind = qcirc.kind_first()
if kind is None or kind is cfg.MEASURE or kind is cfg.RESET:
break
(kind, qid, para, c, ctrl, tag) = qcirc.pop_gate()
if ctrl is None or (ctrl is not None and cmem[ctrl] == 1):
__qulacs_operate_qgate(qstate, qubit_num, kind=kind, qid=qid,
para_phase=para[0], para_gphase=para[1], para_factor=para[2])
if kind is None:
result = Result()
result.qubit_num = qubit_num
result.cmem_num = cmem_num
result.cid = cid
result.shots = shots
result.frequency = None
result.backend = backend
if out_state is True:
result.qstate = __transform_qlazy_qstate(qstate)
result.cmem = __transform_qlazy_cmem(cmem)
return result
#
# after measurement gate
#
if set(qcirc.kind_list()) == {cfg.MEASURE}:
q_list = []
while True:
kind =qcirc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qcirc.pop_gate()
q_list.append(qid[0])
frequency, qstate = __qulacs_measure_shots(qstate, q_list, shots)
result = Result()
result.qubit_num = qubit_num
result.cmem_num = cmem_num
result.cid = cid
result.shots = shots
result.frequency = frequency
result.backend = backend
if out_state is True:
result.qstate = __transform_qlazy_qstate(qstate)
result.cmem = __transform_qlazy_cmem(cmem)
return result
frequency = Counter()
qstate_tmp = None
for _ in range(shots):
qstate_tmp = qstate.copy()
qcirc_tmp = qcirc.clone()
while True:
kind = qcirc_tmp.kind_first()
if kind is None:
break
if kind == cfg.MEASURE:
(kind, qid, para, c, ctrl, tag) = qcirc_tmp.pop_gate()
mval = __qulacs_measure(qstate_tmp, qubit_num, qid[0])
if c is not None:
cmem[c] = mval
elif kind == cfg.RESET:
(kind, qid, para, c, ctrl, tag) = qcirc_tmp.pop_gate()
__qulacs_reset(qstate_tmp, qubit_num, qid[0])
else:
(kind, qid, para, c, ctrl, tag) = qcirc_tmp.pop_gate()
if (ctrl is None or (ctrl is not None and cmem[ctrl] == 1)):
__qulacs_operate_qgate(qstate_tmp, qubit_num, kind=kind, qid=qid,
para_phase=para[0], para_gphase=para[1], para_factor=para[2])
if len(cmem) > 0:
mval = ''.join(map(str, [cmem[i] for i in cid]))
frequency[mval] += 1
if qstate_tmp is not None:
qstate.load(qstate_tmp.get_vector())
if len(frequency) == 0:
frequency = None
result = Result()
result.qubit_num = qubit_num
result.cmem_num = cmem_num
result.cid = cid
result.shots = shots
result.frequency = frequency
result.backend = backend
if out_state is True:
result.qstate = __transform_qlazy_qstate(qstate)
result.cmem = __transform_qlazy_cmem(cmem)
return result
def __transform_qlazy_qstate(qs_qulacs):
vector = reverse_bit_order(qs_qulacs.get_vector())
qs_qlazy = QState(vector=vector)
return qs_qlazy
def __transform_qlazy_cmem(cmem_qulacs):
cmem_num = len(cmem_qulacs)
if cmem_num == 0:
return None
cmem_qlazy = CMem(cmem_num=cmem_num)
cmem_qlazy.set_bits(cmem_qulacs)
return cmem_qlazy
def __is_supported_qgate(kind):
return kind in GateFunctionName.keys()
# not supported as pre-defined gates
def __get_P(q0, phase):
exp = cmath.exp(1.j * phase)
gate = DenseMatrix(q0, [[1., 0.], [0., exp]])
return gate
def __get_CY(q0, q1):
gate = to_matrix_gate(Y(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CXR(q0, q1):
gate = to_matrix_gate(sqrtX(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CXR_dg(q0, q1):
gate = to_matrix_gate(sqrtXdag(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CH(q0, q1):
gate = to_matrix_gate(H(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CS(q0, q1):
gate = to_matrix_gate(S(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CS_dg(q0, q1):
gate = to_matrix_gate(Sdag(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CT(q0, q1):
gate = to_matrix_gate(T(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CT_dg(q0, q1):
gate = to_matrix_gate(Tdag(q1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CP(q0, q1, phase):
exp = cmath.exp(1.j * phase)
gate = DenseMatrix(q1, [[1., 0.], [0., exp]])
gate.add_control_qubit(q0, 1)
return gate
def __get_CRX(q0, q1, phase):
gate = to_matrix_gate(RX(q1, phase))
gate.add_control_qubit(q0, 1)
return gate
def __get_CRY(q0, q1, phase):
gate = to_matrix_gate(RY(q1, phase))
gate.add_control_qubit(q0, 1)
return gate
def __get_CRZ(q0, q1, phase):
gate = to_matrix_gate(RZ(q1, phase))
gate.add_control_qubit(q0, 1)
return gate
def __get_CU1(q0, q1, phase):
gate = to_matrix_gate(U1(q1, phase))
gate.add_control_qubit(q0, 1)
return gate
def __get_CU2(q0, q1, phase, phase1):
gate = to_matrix_gate(U2(q1, phase, phase1))
gate.add_control_qubit(q0, 1)
return gate
def __get_CU3(q0, q1, phase, phase1, phase2):
gate = to_matrix_gate(U3(q1, phase, phase1, phase2))
gate.add_control_qubit(q0, 1)
return gate
def __qulacs_operate_qgate(qstate, qubit_num, kind, qid, para_phase, para_gphase, para_factor):
if __is_supported_qgate(kind) is False:
raise ValueError("not supported quantum gate")
circ = QuantumCircuit(qubit_num)
term_num = get_qgate_qubit_num(kind)
para_num = get_qgate_param_num(kind)
gate_function_name = GateFunctionName[kind]
phase = para_phase * para_factor * np.pi
# the sign-definition of rotation gate on qulacs
if (kind in (cfg.ROTATION_X, cfg.ROTATION_Y, cfg.ROTATION_Z,
cfg.CONTROLLED_RX, cfg.CONTROLLED_RY, cfg.CONTROLLED_RZ)):
phase = -phase
if term_num == 1 and para_num == 0:
circ.add_gate(eval(gate_function_name)(qid[0]))
elif term_num == 1 and para_num == 1:
circ.add_gate(eval(gate_function_name)(qid[0], phase))
elif term_num == 2 and para_num == 0:
circ.add_gate(eval(gate_function_name)(qid[0], qid[1]))
elif term_num == 2 and para_num == 1:
circ.add_gate(eval(gate_function_name)(qid[0], qid[1], phase))
else:
raise ValueError("not supported terminal or parameter-number")
circ.update_quantum_state(qstate)
def __qulacs_reset(qstate, qubit_num, q):
# error check
if q >= qubit_num:
raise ValueError("reset qubit id is out of bound")
circ = QuantumCircuit(qubit_num)
circ.add_gate(Measurement(q, 0))
circ.update_quantum_state(qstate)
circ_flip = QuantumCircuit(qubit_num)
if qstate.get_classical_value(0) == 1:
circ_flip.add_gate(X(q))
circ_flip.update_quantum_state(qstate)
def __qulacs_measure(qstate, qubit_num, q):
# error check
if q >= qubit_num:
raise ValueError("measurement qubit id is out of bound")
circ = QuantumCircuit(qubit_num)
circ.add_gate(Measurement(q, 0))
circ.update_quantum_state(qstate)
mval = qstate.get_classical_value(0)
return mval
def __qulacs_measure_shots(qstate, qid, shots=1):
# error check
qubit_num = qstate.get_qubit_count()
if max(qid) >= qubit_num:
raise ValueError
# list of binary vectors for len(qid) bit integers
qid_sorted = sorted(qid)
mbits_list = []
for i in range(2**len(qid)):
# ex)
# qid = [5,0,2] -> qid_sorted = [0,2,5]
# i = (0,1,2), idx = (2,0,1)
# bits = [q0,q1,q2] -> mbits = [q1,q2,q0]
bits = list(map(int, list(format(i, '0{}b'.format(len(qid))))))
mbits = [0] * len(qid)
for i, q in enumerate(qid):
idx = qid_sorted.index(q)
mbits[idx] = bits[i]
mbits_list.append(mbits)
# list of probabilities
prob_list = []
prob = 0.0
for mbits in mbits_list:
args = [2] * qubit_num
for j, q in enumerate(qid):
args[q] = mbits[j]
prob += qstate.get_marginal_probability(args)
prob_list.append(prob)
if prob_list[-1] != 1.0:
prob_list[-1] = 1.0
# frequency
mval_data = []
if shots > 1:
for i in range(shots-1):
rand = random.random()
for mbits, prob in zip(mbits_list, prob_list):
if rand <= prob:
mval = ''.join(map(str, mbits))
mval_data.append(mval)
break
# last quantum state
circ = QuantumCircuit(qubit_num)
for i, q in enumerate(qid):
circ.add_gate(Measurement(q, i))
circ.update_quantum_state(qstate)
last = ''.join(map(str, [qstate.get_classical_value(i) for i in range(len(qid))]))
mval_data.append(last)
frequency = Counter(mval_data)
return frequency, qstate