# -*- coding: utf-8 -*-
""" Quantum Circuit """
import ctypes
from collections import Counter
from fractions import Fraction
import re
import random
import pickle
import qlazy.config as cfg
from qlazy.util import (is_unitary_gate, is_clifford_gate, is_non_clifford_gate,
is_measurement_gate, is_reset_gate,
get_qgate_qubit_num, get_qgate_param_num)
from qlazy.QObject import QObject
[docs]def string_to_args(s): # for from_qasm
""" convert string to args """
token = s.split(' ')
if len(token) == 1:
args = token
elif len(token) > 1:
args = [token[0], ' '.join(token[1:])]
else:
raise ValueError("can't split string {}.".format(s))
return args
[docs]def init_qc_canvas(qubit_num, cmem_num): # for show method
qlen = len(str(qubit_num - 1))
clen = len(str(cmem_num))
qc_canvas = ["q[{:0{digits}d}] -"
.format(i, digits=qlen) + "-" * (clen - 1) for i in range(qubit_num)]
if cmem_num > 0:
qc_canvas.append('c' + ' ' * (qlen+1) + '=/=' + '=' * (clen - 1))
qc_canvas.append(' ' + ' ' * (qlen+1) + ' ' + str(cmem_num) + ' ')
return qc_canvas
[docs]def append_qc_canvas(qc_canvas, gates, qubit_num, cmem_num): # for show method
if len(gates) == 1: # append single gate
g = gates[0]
if get_qgate_param_num(g['kind']) == 0:
g_label = cfg.GATE_LABEL[g['kind']] + '-'
else:
g_label = cfg.GATE_LABEL[g['kind']] + '(' + str(g['para'][0]*g['para'][2]) + ')-'
if get_qgate_qubit_num(g['kind']) == 1 or is_reset_gate(g['kind']):
qc_canvas[g['qid'][0]] += g_label
if g['ctrl'] is not None:
for j in range(g['qid'][0] + 1, qubit_num):
qc_canvas[j] += '|'
qc_canvas[qubit_num] += '^'
qc_canvas[qubit_num + 1] += (str(g['ctrl']) + ' ')
elif get_qgate_qubit_num(g['kind']) == 2:
qc_canvas[g['qid'][0]] += '*'
qc_canvas[g['qid'][1]] += g_label
qid_min = min(g['qid'][0], g['qid'][1])
qid_max = max(g['qid'][0], g['qid'][1])
for i in range(qid_min + 1, qid_max):
qc_canvas[i] += '|'
if g['ctrl'] is not None:
for j in range(qid_max, qubit_num):
qc_canvas[j] += '|'
qc_canvas[qubit_num] += '^'
qc_canvas[qubit_num + 1] += (str(g['ctrl']) + ' ')
elif is_measurement_gate(g['kind']):
qc_canvas[g['qid'][0]] += g_label
qc_canvas[qubit_num + 1] += (str(g['c']) + ' ')
for i in range(g['qid'][0] + 1, qubit_num):
qc_canvas[i] += '|'
qc_canvas[qubit_num] += 'v'
else: # append group of 1-qubit gates
for g in gates:
if get_qgate_param_num(g['kind']) == 0:
g_label = cfg.GATE_LABEL[g['kind']] + '-'
else:
g_label = cfg.GATE_LABEL[g['kind']] + '(' + str(g['para'][0]) + ')-'
qc_canvas[g['qid'][0]] += g_label
# padding
canvas_len = max(map(len, qc_canvas))
for i in range(len(qc_canvas)):
if i < qubit_num:
qc_canvas[i] += ('-' * (canvas_len - len(qc_canvas[i])))
elif i == qubit_num:
qc_canvas[i] += ('=' * (canvas_len - len(qc_canvas[i])))
else:
qc_canvas[i] += (' ' * (canvas_len - len(qc_canvas[i])))
[docs]class QCirc(ctypes.Structure, QObject):
""" Quantum Circuit
Attributes
----------
qubit_num : int
qubit number of the quantum state (= log(state_num)).
cmem_num : int
number of the classical register.
gate_num : int
number of gates in the quantum circuit.
first: object
first gate of the quantum circuit.
last: object
last gate of the quantum circuit.
tag_table: object
tag table..
"""
_fields_ = [
('qubit_num', ctypes.c_int),
('cmem_num', ctypes.c_int),
('gate_num', ctypes.c_int),
('first', ctypes.c_void_p),
('last', ctypes.c_void_p),
('tag_table', ctypes.c_void_p),
]
def __new__(cls, **kwargs):
"""
Parameters
----------
None
Returns
-------
qcirc : instance (QCirc)
"""
obj = qcirc_init()
qcirc = ctypes.cast(obj.value, ctypes.POINTER(cls)).contents
return qcirc
def __str__(self):
return self.to_string()
def __add__(self, qc):
qcirc = self.merge(qc)
return qcirc
def __iadd__(self, qc):
self.merge_mutable(qc)
return self
def __mul__(self, other):
return self.multiple(other)
__rmul__ = __mul__
def __eq__(self, qc):
ans = self.is_equal(qc)
return ans
def __ne__(self, qc):
ans = not self.is_equal(qc)
return ans
[docs] def to_string(self):
"""
get string of the circuit (qlazy format).
Parameters
----------
None
Returns
-------
qcirc_str : str
"""
qc = self.clone()
qcirc_str = ""
while True:
kind = qc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qc.pop_gate()
term_num = get_qgate_qubit_num(kind)
if kind in (cfg.MEASURE, cfg.RESET):
term_num = 1
para_num = get_qgate_param_num(kind)
gate_str = cfg.GATE_STRING[kind]
qid_str = " ".join(map(str, [qid[i] for i in range(term_num)]))
qid_str.strip()
if para_num == 0:
para_str = ""
else:
para_str = "{}".format(para[0]*para[2])
para_str = "(" + para_str+ ")"
if c is None:
c_str = ""
else:
c_str = "-> {}".format(c)
if ctrl is None:
ctrl_str = ""
else:
ctrl_str = ", ctrl = {}".format(ctrl)
if tag is None or tag == "":
tag_str = ""
else:
tag_str = " #" + tag
qcirc_str += ("{0:}{2:} {1:} {3:}{4:}{5:}\n"
.format(gate_str, qid_str, para_str, c_str, ctrl_str, tag_str))
return qcirc_str.strip()
[docs] @classmethod
def from_qasm(cls, string):
"""
get QCirc instance from OpenQASM 2.0 string.
Parameters
----------
None
Returns
-------
qcirc : instance of QCirc
Notes
-----
Non-unitary gates (measure, reset) and user customized
gates are not supported. Supported gates are
'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'cx', 'cz',
'ch', 'rx', 'rz', 'crz'.
"""
line_list = string.split('\n')
# line #0
line_count = 0
args = string_to_args(line_list[line_count])
if args[0] == 'OPENQASM' and args[1] == '2.0;':
line_count += 1
else:
raise ValueError("line #0 must be 'OPENQASM 2.0;'")
# line #1
args = string_to_args(line_list[line_count])
if args[0] == 'include' and args[1] == '"qelib1.inc";':
line_count += 1
else:
raise ValueError("""line #1 must be 'include "qelib1.inc;"' """)
# line #2 (#3)
args = string_to_args(line_list[line_count])
if args[0] == 'qreg' and re.match('q', args[1]).group() == 'q':
line_count += 1
else:
raise ValueError("""line #2 must be 'qreg q[<int>];"' """)
# line (#3) #4 ...
qcirc = cls()
for i in range(line_count, len(line_list)):
args = string_to_args(line_list[i])
if args[0] == '':
continue
if args[0] in ('measure', 'reset'):
raise ValueError("sorry, 'measure', 'reset' is not supported.")
if args[0] in ('x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg'):
res = re.search(r"q\[|\];", args[1])
if res is not None:
q = int(re.sub(r"q\[|\];", "", args[1]))
else:
raise ValueError("argument '{}' is not valid for '{}' gate."
.format(args[1], args[0]))
if args[0] == 'x':
qcirc.x(q)
elif args[0] == 'z':
qcirc.z(q)
elif args[0] == 'h':
qcirc.h(q)
elif args[0] == 's':
qcirc.s(q)
elif args[0] == 'sdg':
qcirc.s_dg(q)
elif args[0] == 't':
qcirc.t(q)
elif args[0] == 'tdg':
qcirc.t_dg(q)
elif args[0] in ('cx', 'cz', 'ch'):
res = re.search(r"q\[[0-9]+\],\s*q\[[0-9]+\];", args[1])
if res is not None:
qubits = args[1].split(',')
q0 = int(re.sub(r"q\[|\]", "", qubits[0]))
q1 = int(re.sub(r"q\[|\];", "", qubits[1]))
else:
raise ValueError("argument '{}' is not valid for '{}' gate."
.format(args[1], args[0]))
if args[0] == 'cx':
qcirc.cx(q0, q1)
elif args[0] == 'cz':
qcirc.cz(q0, q1)
elif args[0] == 'ch':
qcirc.ch(q0, q1)
elif (re.match('rx', args[0]) is not None or
re.match('rz', args[0]) is not None):
res = re.search(r"q\[|\];", args[1])
if res is not None:
q = int(re.sub(r"q\[|\];", "", args[1]))
else:
raise ValueError(("argument '{}' is not valid for '{}' gate."
.format(args[1], args[0])))
para_str = [s.strip('*').strip('/') for s in re.sub(r".+\(|\)", "",
args[0]).split("pi")]
if para_str[0] == '0':
para = 0.
else:
denominator = 1.
if para_str[0] == '':
numerator = 1.
else:
numerator = float(para_str[0])
if para_str[1] == '':
denominator = 1.
else:
denominator = float(para_str[1])
para = numerator / denominator
if re.match('rx', args[0]) is not None:
qcirc.rx(q, phase=para)
elif re.match('rz', args[0]) is not None:
qcirc.rz(q, phase=para)
elif re.match('crz', args[0]) is not None:
res = re.search(r"q\[[0-9]+\],\s*q\[[0-9]+\];", args[1])
if res is not None:
qubits = args[1].split(',')
q0 = int(re.sub(r"q\[|\]", "", qubits[0]))
q1 = int(re.sub(r"q\[|\];", "", qubits[1]))
else:
raise ValueError(("argument '{}' is not valid for '{}' gate."
.format(args[1], args[0])))
para_str = [s.strip('*').strip('/') for s in re.sub(r".+\(|\)", "",
args[0]).split("pi")]
if para_str[0] == '0':
para = 0.
else:
denominator = 1.
if para_str[0] == '':
numerator = 1.
else:
numerator = float(para_str[0])
if para_str[1] == '':
denominator = 1.
else:
denominator = float(para_str[1])
para = numerator / denominator
if re.match('crz', args[0]) is not None:
qcirc.crz(q0, q1, phase=para)
else:
raise ValueError("{} gate is not supported".format(args[0]))
return qcirc
[docs] @classmethod
def from_qasm_file(cls, file_path):
"""
get QCirc instance from OpenQASM 2.0 file.
Parameters
----------
file_path: str
file path name of OpenQASM 2.0 file
Returns
-------
qcirc : instance of QCirc
Notes
-----
Non-unitary gates (measure, reset) and user customized
gates are not supported. Supported gates are
'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg',
'cx', 'cz', 'ch', 'rx', 'rz', 'crz'.
"""
with open(file_path, mode='r') as f:
s = f.read()
qcirc = cls.from_qasm(s)
return qcirc
[docs] def to_qasm(self):
"""
get OpenQASM 2.0 string of the circuit.
Parameters
----------
None
Returns
-------
qcirc_str : str
"""
qc = self.clone()
# header and include file
qcirc_str = """OPENQASM 2.0;\n"""
qcirc_str += """include "qelib1.inc";\n"""
# definition of qreg, creg
qcirc_str += """qreg q[{}];\n""".format(self.qubit_num)
for i in range(self.cmem_num):
qcirc_str += """creg c{}[1];\n""".format(i)
# description of each gate operation
while True:
kind = qc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qc.pop_gate()
term_num = get_qgate_qubit_num(kind)
if kind in (cfg.MEASURE, cfg.RESET):
term_num = 1
para_num = get_qgate_param_num(kind)
para[0] *= para[2]
para_frac = [Fraction(str(p)) for p in para]
gate_str = cfg.GATE_STRING_QASM[kind]
qid_str = ",".join(["q[" + str(qid[i]) + "]" for i in range(term_num)])
qid_str.strip()
if para_num == 0:
if kind == cfg.CONTROLLED_S:
para_str = "(pi/2)"
elif kind == cfg.CONTROLLED_S_:
para_str = "(-pi/2)"
elif kind == cfg.CONTROLLED_T:
para_str = "(pi/4)"
elif kind == cfg.CONTROLLED_T_:
para_str = "(-pi/4)"
else:
para_str = ""
else:
para_str_list = []
for i, p in enumerate(para_frac):
if i >= para_num:
break
if p.numerator == 0:
para_str_list.append("0")
elif p.numerator == 1:
para_str_list.append("pi/"+ str(p.denominator))
else:
para_str_list.append(str(p.numerator) + "*pi/" + str(p.denominator))
para_str = ",".join(para_str_list)
para_str = "(" + para_str+ ")"
if c is None:
c_str = ""
else:
c_str = " -> c{}[0]".format(c)
if ctrl is None:
ctrl_str = ""
else:
ctrl_str = "if(c{}==1) ".format(ctrl)
qcirc_str += ("{4:}{0:}{2:} {1:}{3:};\n"
.format(gate_str, qid_str, para_str, c_str, ctrl_str))
return qcirc_str.strip()
[docs] def to_qasm_file(self, file_path):
"""
write to OpenQASM 2.0 file.
Parameters
----------
file_path: str
file path name of OpenQASM 2.0 file
Returns
-------
None
"""
s = self.to_qasm()
with open(file_path, mode='w') as f:
f.write(s)
[docs] def show(self, width=100):
"""
show the circuit
Parameters
----------
width : int, default - 100
width for line breaks and display long quantum circuit
Returns
-------
None
"""
qc = self.clone()
qubit_num = self.qubit_num
cmem_num = self.cmem_num
gate_num = self.gate_num
qc_canvas = init_qc_canvas(qubit_num, cmem_num)
gates = []
qids = []
while True:
kind = qc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qc.pop_gate()
gate = {'kind': kind, 'qid': qid, 'para': para, 'c': c, 'ctrl': ctrl}
if ((get_qgate_qubit_num(kind) == 1 or is_reset_gate(kind)) and ctrl is None):
if qid[0] in qids:
append_qc_canvas(qc_canvas, gates, qubit_num, cmem_num)
gates = [gate]
qids = [qid[0]]
else:
gates.append(gate)
qids.append(qid[0])
else:
if gates != []:
append_qc_canvas(qc_canvas, gates, qubit_num, cmem_num)
gates = []
qids = []
append_qc_canvas(qc_canvas, [gate], qubit_num, cmem_num)
if gates != []:
append_qc_canvas(qc_canvas, gates, qubit_num, cmem_num)
pos_start = 0
pos_end = width
while True:
if pos_start >= len(qc_canvas[0]):
break
for line in qc_canvas:
print(line[pos_start:pos_end])
print()
pos_start += width
pos_end +=width
[docs] def clone(self):
"""
clone quantum circuit.
Parameters
----------
None
Returns
-------
qcirc : instance of QCirc
quantum circuit
"""
obj = qcirc_copy(self)
qcirc = ctypes.cast(obj.value, ctypes.POINTER(self.__class__)).contents
return qcirc
[docs] def merge(self, qc):
"""
merge two quantum circuits.
Parameters
----------
qc : instance of QCirc
quantum circuit (merged)
Returns
-------
qcirc : instance of QCirc
new quantum circuit (merge result)
"""
obj = qcirc_merge(self, qc)
qcirc = ctypes.cast(obj.value, ctypes.POINTER(self.__class__)).contents
return qcirc
[docs] def merge_mutable(self, qc):
"""
merge a quantum circuit with another one.
Parameters
----------
qc : instance of QCirc
quantum circuit (merged)
Returns
-------
self : instance of QCirc
quantum circuit (merge result)
Notes
-----
This method changes original quantum circuit.
"""
qcirc_merge_mutable(self, qc)
return self
[docs] def multiple(self, other):
"""
integer multiple for quantum circuit
Parameters
----------
other : instance of QCirc / int
quantum circuit / number
Returns
-------
qc : instance of QCirc
quantum circuit after multiplication
"""
if isinstance(other, int) and other > 0:
qc = self.clone()
qc_out = self.clone()
for i in range(other-1):
qc_out = qc_out.merge(qc)
else:
raise TypeError("Can't multiple QCirc with {} (type:{})".format(other, type(other)))
return qc_out
[docs] def is_equal(self, qc):
"""
eaual or not.
Parameters
----------
qc : instance of QCirc
quantum circuit (merged)
Returns
-------
qcirc : instance of QCirc
quantum circuit (result)
"""
ans = qcirc_is_equal(self, qc)
return ans
[docs] def kind_first(self):
"""
get kind of first gate of the circuit.
Parameters
----------
None
Returns
-------
kind_list : int
kind of first quantum gate of quantum circuit
Note
----
return None if none of gates included
"""
kind = qcirc_kind_first(self)
return kind
[docs] def kind_list(self):
"""
get list of kinds from the circuit.
Parameters
----------
None
Returns
-------
kinds : list of kind
kinds of the quantum circuit
Note
----
return None if none of gates included
"""
qc = self.clone()
kind_list = []
while True:
kind = qc.kind_first()
if kind is None:
break
kind_list.append(kind)
qc.pop_gate()
return kind_list
[docs] def get_gates(self):
"""
get list of gates from the circuit.
Parameters
----------
None
Returns
-------
gates : list of dict
gates of the quantum circuit
gates = [{'kind':kind, 'qid':qid, 'phase':phase, 'cid':cid, 'ctrl':ctrl}, ...]
- kind: gate name
- qid: qubit id
- phase: phase parameter (non-zero only for rotation gates)
- para: [phase, gphase, factor]
* gphase means global phase used only when adding control to p gate
- cid: classical register id (set only for measurement gate)
- ctrl: classical register id to control gate operation
"""
qc = self.clone()
gates = []
while True:
kind = qc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qc.pop_gate()
qid = [q for q in qid if q >= 0]
if c is None:
cid = None
else:
cid = [c]
gates.append({'kind': cfg.GATE_STRING[kind], 'qid': qid,
'para': para, 'cid': cid, 'ctrl': ctrl})
return gates
[docs] def add_gates(self, gates=None):
"""
add list of gates to the circuit.
Parameters
----------
gates : list of dict
gates of the quantum circuit
gates = [{'kind':kind, 'qid':qid, 'phase':phase, 'cid':cid, 'ctrl':ctrl}, ...]
- kind: gate name
- qid: qubit id
- phase: phase parameter (non-zero only for rotation gates)
- cid: classical register id (set only for measurement gate)
- ctrl: classical register id for controlling gate operation
Returns
-------
self: instance of QCirc
circuit after adding gates
Notes
-----
This method changes original quantum circuit.
"""
if gates is None:
raise ValueError("gates must be specified.")
for g in gates:
kind = cfg.GATE_KIND[g['kind']]
qid = g['qid']
para = g['para']
if g['cid'] is None:
c = None
else:
c = g['cid'][0]
ctrl = g['ctrl']
self.append_gate(kind, qid, para, c, ctrl)
return self
[docs] def dump(self, file_path):
"""
dump the circuit
Parameters
----------
file_path: str
file path of dump file
Returns
-------
None
"""
gates = self.get_gates()
with open(file_path, mode='wb') as f:
pickle.dump(gates, f)
[docs] def save(self, file_path):
"""
save the circuit
Parameters
----------
file_path: str
file path of dump file
Returns
-------
None
"""
self.dump(file_path)
[docs] @classmethod
def load(cls, file_path):
"""
load the circuit
Parameters
----------
file_path: str
file path of dump file
Returns
-------
qcirc: instance of QCirc
loaded circuit
"""
with open(file_path, mode='rb') as f:
gates = pickle.load(f)
qcirc = cls().add_gates(gates)
return qcirc
[docs] def get_stats(self):
"""
get statistics of the circuit.
Parameters
----------
None
Returns
-------
stats : dict
{'qubit_num':qubit_num, 'cmem_num':cmem_num, 'gate_num':gate_num, 'gate_freq':gate_freq}
- qubit_num: number of qubits
- cmem_num: number of classical bits
- gate_num: number of gates
- gate_freq: frequency of gates (Counter)
"""
gate_list = [cfg.GATE_STRING[kind] for kind in self.kind_list()]
gate_freq = Counter(gate_list)
gatetype_list = []
for kind in self.kind_list():
if is_clifford_gate(kind) is True or is_non_clifford_gate(kind) is True:
gatetype_list.append('unitary')
if is_clifford_gate(kind) is True:
gatetype_list.append('clifford')
elif is_non_clifford_gate(kind) is True:
gatetype_list.append('non-clifford')
elif is_measurement_gate(kind) is True or is_reset_gate(kind) is True:
gatetype_list.append('non-unitary')
else:
raise ValueError("unknown gate kind:{}".format(kind))
gatetype_freq = Counter(gatetype_list)
stats = {'qubit_num': self.qubit_num, 'cmem_num':self.cmem_num, 'gate_num': len(gate_list),
'gate_freq': gate_freq, 'gatetype_freq': gatetype_freq}
return stats
[docs] @classmethod
def generate_random_gates(cls, qubit_num=0, gate_num=0, phase=None, prob=None, **kwargs):
"""
generate circuit including random gates.
Parameters
----------
qubit_num: int
number of qubits
gate_num: int
numbner of gates
phase: tupple of float
phases selected randomly
prob: dict
{'x':prob_x, 'z':prob_z, 'h':prob_h, 's':prob_s, 's_dg':prob_s_dg,
't':prob_t, 't_dg':prob_t_dg, 'rx':prob_rx, 'rz':prob_rz, 'cx':prob_cx,
'cz':prob_cz, 'ch':prob_ch, 'crz':prob_crz}
- prob_x: probability of x
- prob_z: probability of z
- prob_h: probability of h
- prob_s: probability of s
- prob_s_dg: probability of s_dg
- prob_t: probability of t
- prob_t_dg: probability of t_dg
- prob_rx: probability of rx
- prob_rz: probability of rz
- prob_cx: probability of cx
- prob_cz: probability of cz
- prob_ch: probability of ch
- prob_crz: probability of crz
Returns
-------
qcirc: instance of QCirc
generated circuit
Examples
--------
>>> qc = QCirc.generate_random_gates(qubit_num=5, gate_num=100,
phase_unit=0.25, prob={'h':3, 'cx':7, 'rz':1})
Notes
-----
* each probability values are normalized so that the sum of the probabilities is 1.0.
* Phase parameters of rotation gates are selected randomly in the element of 'phase'.
"""
if ((isinstance(qubit_num, int) is not True or
isinstance(gate_num, int) is not True or qubit_num < 1 or gate_num < 1)):
raise ValueError("qubit_num and/or gate_num must be positive integer.")
if phase is not None:
if ((isinstance(phase, float) is not True and
isinstance(phase, int) is not True and
isinstance(phase, tuple) is not True)):
raise ValueError("phase value(s) must be int/float of tuple.")
total_prob = 0.0
for p in prob.values():
total_prob += p
glist = []
plist = []
p = 0.0
for k, v in prob.items():
if v < 0.0:
raise ValueError("probability must be positive value.")
if k in ('x', 'z', 'h', 's', 's_dg', 't', 't_dg', 'rx', 'rz', 'cx', 'cz', 'ch', 'crz'):
glist.append(k)
p += v / total_prob
plist.append(p)
else:
raise ValueError("gate '{}' is not supported.".format(k))
if plist[-1] != 1.0:
plist[-1] = 1.0
qcirc = cls()
TRY_MAX = 10 # for random generation
max_q = -1
gate_count = 0
while gate_count < gate_num or max_q < qubit_num - 1:
r = random.random()
kind = None
for i, p in enumerate(plist):
if r <= p:
kind = cfg.GATE_KIND[glist[i]]
break
term_num = get_qgate_qubit_num(kind)
para_num = get_qgate_param_num(kind)
if term_num == 1 and para_num == 0: # 1-qubit gate
q0 = random.randint(0, qubit_num - 1)
qcirc.append_gate(kind, [q0])
max_q = max(max_q, q0)
gate_count += 1
elif term_num == 1 and para_num == 1: # 1-qubit and 1-parameter gate
q0 = random.randint(0, qubit_num - 1)
if phase is None:
p = 0.0
elif isinstance(phase, (float, int)):
p = phase
else:
p = random.choice(phase)
qcirc.append_gate(kind, [q0], para=[p, 0., 0.])
max_q = max(max_q, q0)
gate_count += 1
elif term_num == 2 and para_num == 0: # 2-qubit gate
q0 = random.randint(0, qubit_num - 1)
q1 = random.randint(0, qubit_num - 1)
cnt = 0
while q0 == q1 and cnt < TRY_MAX:
q1 = random.randint(0, qubit_num - 1)
cnt += 1
if cnt >= TRY_MAX:
raise ValueError(("can't generate qubit id for '{}' gate."
.format(cfg.GATE_STRING[kind])))
qcirc.append_gate(kind, [q0, q1])
max_q = max(max_q, q0, q1)
gate_count += 1
elif term_num == 2 and para_num == 1: # 2-qubit and 1-parameter gate
q0 = random.randint(0, qubit_num - 1)
q1 = random.randint(0, qubit_num - 1)
cnt = 0
while q0 == q1 and cnt < TRY_MAX:
q1 = random.randint(0, qubit_num - 1)
cnt += 1
if cnt >= TRY_MAX:
raise ValueError(("can't generate qubit id for '{}' gate."
.format(cfg.GATE_STRING[kind])))
if phase is None:
p = 0.0
elif isinstance(phase, (float, int)):
p = phase
else:
p = random.choice(phase)
qcirc.append_gate(kind, [q0, q1], para=[p, 0., 0.])
max_q = max(max_q, q0, q1)
gate_count += 1
else:
raise ValueError(("gate of term_num={}, param_num={} is not supported"
.format(term_num, para_num)))
# delete extra gates
for _ in range(gate_count - gate_num):
qcirc.pop_gate()
return qcirc
[docs] def to_pyzx(self):
"""
get pyzx's Circuit instance.
Parameters
----------
None
Returns
-------
zxqc: instance of pyzx's Circuit
quantum circuit
Notes
-----
Non-unitary gates: measure, reset are not supported.
"""
from pyzx import Circuit
zxqc = Circuit.from_qasm(self.to_qasm())
return zxqc
[docs] @classmethod
def from_pyzx(cls, zxqc):
"""
get pyzx's Circuit instance.
Parameters
----------
zxqc: instance of pyzx's Circuit
quantum circuit
Returns
-------
qc: instance of QCirc
quantum circuit
Notes
-----
Non-unitary gates: measure, reset are not supported.
"""
qc = cls.from_qasm(zxqc.to_qasm())
return qc
[docs] def optimize(self, *args, **kwargs):
"""
optimize the quantum circuit (using pyzx's full_optimize method).
Parameters
----------
None
Returns
-------
qc: instance of QCirc
optimized circuit
Notes
-----
Non-unitary gates: measure, reset are not supported.
"""
import pyzx as zx
zxqc = self.to_pyzx()
zxqc_opt = zx.optimize.full_optimize(zxqc)
qc = self.__class__.from_pyzx(zxqc_opt)
return qc
[docs] def equivalent(self, qc, *args, **kwargs):
"""
two quantum circuits are equivalent or not (using pyzx's verify_equality method).
Parameters
----------
None
Returns
-------
ans: bool
Notes
-----
Non-unitary gates: measure, reset are not supported.
"""
from pyzx import Circuit
zxqc_A = Circuit.from_qasm(self.to_qasm())
zxqc_B = Circuit.from_qasm(qc.to_qasm())
ans = zxqc_A.verify_equality(zxqc_B)
return ans
[docs] def pop_gate(self):
"""
pop first gate of the circuit.
Parameters
----------
None
Returns
-------
gate : tupple of (int, [int,int], [float,float,float], int, int)
tupple of (kind, qid, para, c, ctrl)
- kind ... kind of gate
- qid ... qubit id list
- para ... parameters for rotation
- c ... classical register ID to store measured data (only for measurement gate)
- ctrl ... classical register id to controll the gate
Notes
-----
This method changes original quantum circuit.
"""
(kind, qid, para, c, ctrl, tag) = qcirc_pop_gate(self)
return (kind, qid, para, c, ctrl, tag)
[docs] def append_gate(self, kind=None, qid=None, para=None, c=None, ctrl=None, tag=None):
"""
append gate to the end of the circuit.
Parameters
----------
kind : int
kind of gate
qid : list (int)
list of qubit id
para : list (float), default None
list of parameters
c : int, default None
classical register id to store measured data
ctrl : int, default None
classical register id to controll the gate
Returns
-------
None
"""
# kind must be int
if isinstance(kind, int) is not True:
raise TypeError("kind must be int.")
# qid must be list of int
if isinstance(qid, list) is not True:
raise TypeError("qid must be list.")
for q in qid:
if isinstance(q, int) is not True:
raise TypeError("qid must be list of int.")
# para is None or para must be list of float
if para is not None:
if isinstance(para, list) is True:
for p in para:
if isinstance(p, float) is not True:
raise TypeError("para must be a list of float.")
else:
raise TypeError("para must be a list of float.")
# c is None or c must be int
if c is not None and isinstance(c, int) is not True:
raise TypeError("c must be int.")
# ctrl is None or ctrl must be int
if ctrl is not None and isinstance(ctrl, int) is not True:
raise TypeError("ctrl must be int.")
# qcirc_append_gate(self, kind, qid, para, c, ctrl)
qcirc_append_gate(self, kind, qid, para, c, ctrl, tag)
[docs] def split_unitary_non_unitary(self):
"""
split two part of the gate.
Parameters
----------
None
Returns
-------
qc_pair : tupple of (QCirc, Qcirc)
former part includes only unitary gates and later part
includes non-unitary gate (measure or reset) first
"""
qc_unitary = self.__class__()
qc_non_unitary = self.clone()
while True:
kind_ori = qc_non_unitary.kind_first()
if kind_ori is None or kind_ori is cfg.MEASURE or kind_ori is cfg.RESET:
break
(kind, qid, para, c, ctrl, tag) = qc_non_unitary.pop_gate()
qc_unitary.append_gate(kind, qid, para, c, ctrl, tag)
qc_pair = (qc_unitary, qc_non_unitary)
return qc_pair
[docs] def is_unitary(self):
"""
the quantum circuit is unitary or not
Parameters
----------
None
Returns
-------
ans : bool
True if the quantum circuit unitary, False if otherwise
"""
ans = True
for kind in self.kind_list():
if is_unitary_gate(kind) is False:
ans = False
break
return ans
[docs] def is_clifford(self):
"""
the quantum circuit is clifford or not
Parameters
----------
None
Returns
-------
ans : bool
True if the quantum circuit unitary, False if otherwise
"""
ans = True
for kind in self.kind_list():
if is_clifford_gate(kind) is False:
ans = False
break
return ans
[docs] def all_gates_measurement(self):
"""
gates of the qcirc are all measurement
Parameters
----------
None
Returns
-------
ans : bool
True if all gates are measurement, False if otherwise
"""
if self.kind_first() is None:
return False
ans = True
qcirc = self.clone()
while True:
kind = qcirc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qcirc.pop_gate()
if kind is not cfg.MEASURE:
ans = False
break
return ans
def __del__(self):
qcirc_free(self)
[docs] def remap(self, qid=None, cid=None):
"""
remap qubit id and cmem id of quantum circuit
Parameters
----------
qid : list (int)
list of qubit id (quantum register)
cid : list (int)
list of cmem id (classical memory or classical register)
Returns
-------
qcirc : instance of QCirc
new quantum circuit after remapping
Examples
--------
>>> qc = QCirc().h(0).cx(0,1).measure(qid=[0,1], cid=[0,1])
>>> qc.show()
q[0] -H-*-M---
q[1] ---X-|-M-
c =/=====v=v=
2 0 1
>>> qc_new1 = qc.remap(qid=[1,0], cid=[1,0])
>>> qc_new1.show()
q[0] ---X---M-
q[1] -H-*-M-|-
c =/=====v=v=
2 1 0
>>> qc_new2 = qc.remap(qid=[2,1], cid=[1,0])
>>> qc_new2.show()
q[0] ---------
q[1] ---X---M-
q[2] -H-*-M-|-
c =/=====v=v=
2 1 0
Notes
-----
Length of the qid must be equal to qubit_num of the original quantum circut.
Length of cid must be equal to cmem_num of the original quantum circut.
Elements of the qid and the cid must not be duplicated.
"""
if qid is None and cid is None:
return self.clone()
elif qid is None:
qid = list(range(self.qubit_num))
elif cid is None:
cid = list(range(self.cmem_num))
# check qid
if all([isinstance(q, int) and q>=0 for q in qid]):
pass
else:
raise TypeError("qid must be a list of zero or more integer.")
if len(qid) != self.qubit_num:
raise ValueError("length of qid must be equal to the qubit number of the quantum circuit:{}.".format(self.qubit_num))
if len(set(qid)) != len(qid):
raise ValueError("elements of qid must not be duplicated.")
# check cid
if all([isinstance(c, int) and c>=0 for c in cid]):
pass
else:
raise TypeError("cid must be a list of zero or more integer.")
if len(cid) != self.cmem_num:
raise ValueError("length of cid must be equal to the cmem number of the quantum circuit:{}.".fomat(self.cmem_num))
if len(set(cid)) != len(cid):
raise ValueError("elements of cid must not be duplicated.")
qcirc = QCirc()
gates = self.get_gates()
for g in gates:
if g['qid'] is not None:
g['qid'] = [qid[q] for i,q in enumerate(g['qid'])]
if g['cid'] is not None:
g['cid'] = [cid[c] for i,c in enumerate(g['cid'])]
if g['ctrl'] is not None:
g['ctrl'] = cid[g['ctrl']]
qcirc.add_gates(gates)
return qcirc
[docs] def set_params(self, params):
"""
set parameters for each tag
Parameters
----------
params : dict
tag and phase dictionary
ex) {'tag1': phase1, 'tag2': phase2, ...}
Returns
-------
None
Examples
--------
>>> qc = QCirc().h(0).rz(0, tag='foo').rx(0, tag='bar')
>>> qc.set_params(params={'foo': 0.2, 'bar': 0.4})
>>> print(qc)
h 0
rz(0.2) 0
rx(0.4) 0
>>> qc.set_params(params={'foo': 0.3, 'bar': 0.5})
>>> print(qc)
h 0
rz(0.3) 0
rx(0.5) 0
"""
if not isinstance(params, dict):
raise TypeError("params must be dict.")
qcirc_set_params(self, params)
[docs] def get_tag_phase(self, tag):
"""
get parameter (= phase) for the tag
Parameters
----------
tag : str
tag of phase parameter for parametric quantum circuit.
Returns
-------
phase : float
rotation angle (unit of angle is PI radian) for the tag.
Examples
--------
>>> qc = QCirc().h(0).rz(0, tag='foo').rx(0, tag='bar')
>>> qc.set_params(params={'foo': 0.2, 'bar': 0.4})
>>> print(qc.get_tag_phase('foo'))
0.2
"""
if not isinstance(tag, str):
raise TypeError("tag must be str.")
phase = qcirc_get_tag_phase(self, tag)
return phase
[docs] def get_params(self):
"""
get parameters for each tag
Parameters
----------
None
Returns
-------
params : dict
tag and phase dictionary
ex) {'tag1': phase1, 'tag2': phase2, ...}
Examples
--------
>>> qc = QCirc().h(0).rz(0, tag='foo').rx(0, tag='bar')
>>> qc.set_params(params={'foo': 0.2, 'bar': 0.4})
>>> print(qc.get_params())
{'foo': 0.2, 'bar': 0.4}
"""
tag_list = qcirc_get_tag_list(self)
if len(tag_list) == 0:
params = None
else:
params = {tag:self.get_tag_phase(tag) for tag in tag_list}
return params
[docs] def add_control(self, qctrl=None):
"""
add control qubit to quantum circuit
Parameters
----------
qctrl : int
control qubit id
Returns
-------
qc_out : instance of QCirc
quantum circuit after adding control qubit
"""
gates = self.get_gates()
for g in gates:
if qctrl in g['qid']:
raise ValueError("qctrl={} is not allowed because it is already used.".format(qctrl))
qc = self.clone()
qc_out = QCirc()
gid = 0
while True:
kind = qc.kind_first()
if kind is None:
break
(kind, qid, para, c, ctrl, tag) = qc.pop_gate()
self.__add_controll_gate(qc_out, kind, qid, para, c, ctrl, qctrl, tag)
gid += 1
return qc_out
def __add_controll_gate(self, qc, kind, qid, para, c, ctrl, qctrl, tag):
# 1-qubit gate
if kind == cfg.PAULI_X:
qc.cx(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.PAULI_Z:
qc.cz(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.HADAMARD:
qc.ch(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.PHASE_SHIFT_S:
qc.cs(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.PHASE_SHIFT_S_:
qc.cs_dg(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.PHASE_SHIFT_T:
qc.ct(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.PHASE_SHIFT_T_:
qc.ct_dg(qctrl, qid[0], ctrl=ctrl)
elif kind == cfg.ROTATION_X:
qc.crx(qctrl, qid[0], phase=para[0], ctrl=ctrl, tag=tag, fac=para[2])
elif kind == cfg.ROTATION_Z:
qc.crz(qctrl, qid[0], phase=para[0], ctrl=ctrl, tag=tag, fac=para[2])
if para[1] != 0.0: # for the p gate
qc.rz(qctrl, phase=para[1])
# 2-qubit gate
elif kind == cfg.CONTROLLED_X:
qc.ccx(qctrl, qid[0], qid[1], ctrl=ctrl)
elif kind == cfg.CONTROLLED_Z:
qc.h(qid[1], ctrl=ctrl)
qc.ccx(qctrl, qid[0], qid[1], ctrl=ctrl)
qc.h(qid[1], ctrl=ctrl)
elif kind == cfg.CONTROLLED_H:
q0, q1 = qid[0], qid[1]
qc.cry(qctrl, q1, phase=-0.25, ctrl=ctrl).ccx(qctrl, q0, q1, ctrl=ctrl).crz(qctrl, q1, phase=-0.5, ctrl=ctrl)
qc.ccx(qctrl, q0, q1, ctrl=ctrl).crz(qctrl, q1, phase=0.5, ctrl=ctrl).cry(qctrl, q1, phase=0.25, ctrl=ctrl)
qc.crz(qctrl, q0, phase=0.5, ctrl=ctrl)
elif kind == cfg.CONTROLLED_RZ:
qc.crz(qctrl, qid[1], phase=para[0], ctrl=ctrl, tag=tag, fac=0.5*para[2])
qc.ccx(qctrl, qid[0], qid[1], ctrl=ctrl)
qc.crz(qctrl, qid[1], phase=para[0], ctrl=ctrl, tag=tag, fac=-0.5*para[2])
qc.ccx(qctrl, qid[0], qid[1], ctrl=ctrl)
# non-unitary gate
elif kind == cfg.MEASURE:
qc.measure(qid=[qid[0]], cid=[c])
elif kind == cfg.RESET:
qc.reset(qid=[qid[0]])
else:
raise ValueError("not supported quantum gate.")
# operate gate
[docs] def operate_gate(self, kind=None, qid=None, cid=None,
phase=0.0, gphase=0.0, fac=1.0, tag=None, ctrl=None):
"""
operate gate
Parameters
----------
kind : int
kind of the gate
qid : list
quantum id list
cid : list
classical register (memory) id list
phase : float
phase for rotation gate
gphase : float
global phase for rotation gate
fac : float
factor of phase value
Returns
-------
self : instance of QCirc
quantum circuit
"""
if kind == cfg.RESET:
for q in qid:
qid = [q, -1]
self.append_gate(kind=cfg.RESET, qid=qid)
elif kind == cfg.MEASURE:
if qid is None:
raise ValueError("qid must be specified.")
if cid is None:
raise ValueError("cid must be specified.")
if len(qid) != len(cid):
raise ValueError("length of qid and cid must be same.")
for q, c in zip(qid, cid):
qid = [q, -1]
self.append_gate(kind=cfg.MEASURE, qid=qid, c=c)
elif (kind in (cfg.PAULI_X, cfg.PAULI_Z, cfg.HADAMARD, cfg.PHASE_SHIFT_S, cfg.PHASE_SHIFT_S_,
cfg.PHASE_SHIFT_T, cfg.PHASE_SHIFT_T_)):
self.append_gate(kind=kind, qid=qid, ctrl=ctrl)
elif (kind in (cfg.ROTATION_X, cfg.ROTATION_Z)):
para = [phase, 0.0, fac]
self.append_gate(kind=kind, qid=qid, para=para, ctrl=ctrl, tag=tag)
elif kind == cfg.CONTROLLED_RZ:
para = [phase, 0.0, fac]
self.append_gate(kind=kind, qid=qid, para=para, ctrl=ctrl, tag=tag)
elif (kind in (cfg.CONTROLLED_X, cfg.CONTROLLED_Z, cfg.CONTROLLED_H, )):
self.append_gate(kind=kind, qid=qid, ctrl=ctrl)
elif kind ==cfg.PAULI_Y:
self.append_gate(kind=cfg.PAULI_Z, qid=qid, ctrl=ctrl)
self.append_gate(kind=cfg.PAULI_X, qid=qid, ctrl=ctrl)
elif kind == cfg.ROOT_PAULI_X:
para = [0.5, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_X, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.ROOT_PAULI_X_:
para = [-0.5, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_X, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.ROTATION_Y:
para = [phase, 0.0, fac]
self.append_gate(kind=cfg.PHASE_SHIFT_S_, qid=qid, ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_X, qid=qid, para=para, ctrl=ctrl, tag=tag)
self.append_gate(kind=cfg.PHASE_SHIFT_S, qid=qid, ctrl=ctrl)
elif kind == cfg.PHASE_SHIFT:
para = [phase, phase/2.0, fac]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl, tag=tag)
elif kind == cfg.CONTROLLED_Y:
self.append_gate(kind=cfg.CONTROLLED_Z, qid=qid, ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_X, qid=qid, ctrl=ctrl)
self.append_gate(kind=cfg.PHASE_SHIFT_S, qid=qid, ctrl=ctrl)
elif kind == cfg.CONTROLLED_XR:
q0, q1 = qid[0], qid[1]
para = [0.5, 0.0, 1.0]
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl)
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
para = [0.25, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=[q0, q1], para=para, ctrl=ctrl)
elif kind == cfg.CONTROLLED_XR_:
q0, q1 = qid[0], qid[1]
para = [-0.5, 0.0, 1.0]
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl)
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
para = [-0.25, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=[q0, q1], para=para, ctrl=ctrl)
elif kind == cfg.CONTROLLED_S:
para = [0.5, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=qid, para=para, ctrl=ctrl)
para = [0.25, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.CONTROLLED_S_:
para = [-0.5, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=qid, para=para, ctrl=ctrl)
para = [-0.25, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.CONTROLLED_T:
para = [0.25, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=qid, para=para, ctrl=ctrl)
para = [0.125, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.CONTROLLED_T_:
para = [-0.25, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=qid, para=para, ctrl=ctrl)
para = [-0.125, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl)
elif kind == cfg.SWAP_QUBITS:
q0, q1 = qid[0], qid[1]
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q1, q0], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
elif kind == cfg.CONTROLLED_P:
para = [phase, 0.0, fac]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=qid, para=para, ctrl=ctrl, tag=tag)
para = [phase, 0.0, 0.5*fac]
self.append_gate(kind=cfg.ROTATION_Z, qid=qid, para=para, ctrl=ctrl, tag=tag)
elif kind == cfg.CONTROLLED_RX:
q0, q1 = qid[0], qid[1]
para = [phase, 0.0, fac]
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl, tag=tag)
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
elif kind == cfg.CONTROLLED_RY:
q0, q1 = qid[0], qid[1]
# cs_dg gate
para = [-0.5, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl)
para = [-0.25, 0.0, 1.0]
self.append_gate(cfg.ROTATION_Z, qid=[q0, q1], para=para, ctrl=ctrl)
para = [phase, 0.0, fac]
self.append_gate(cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
self.append_gate(cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl, tag=tag)
self.append_gate(cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
# cs gate
para = [0.5, 0.0, 1.0]
self.append_gate(kind=cfg.CONTROLLED_RZ, qid=[q0, q1], para=para, ctrl=ctrl)
para = [0.25, 0.0, 1.0]
self.append_gate(kind=cfg.ROTATION_Z, qid=[q0, q1], para=para, ctrl=ctrl)
elif kind == cfg.ROTATION_XX:
q0, q1 = qid[0], qid[1]
para=[phase, 0.0, fac]
self.append_gate(kind=cfg.HADAMARD, qid=[q0, -1], ctrl=ctrl)
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_Z, qid=[q1, -1], para=para, ctrl=ctrl, tag=tag)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.HADAMARD, qid=[q0, -1], ctrl=ctrl)
self.append_gate(kind=cfg.HADAMARD, qid=[q1, -1], ctrl=ctrl)
elif kind == cfg.ROTATION_YY:
q0, q1 = qid[0], qid[1]
para = [phase, 0.0, fac]
self.append_gate(kind=cfg.ROTATION_X, qid=[q0, -1], para=[0.5, 0.0, 1.0], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_X, qid=[q1, -1], para=[0.5, 0.0, 1.0], ctrl=ctrl)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_Z, qid=[q1, -1], para=para, ctrl=ctrl, tag=tag)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_X, qid=[q0, -1], para=[-0.5, 0.0, 1.0], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_X, qid=[q1, -1], para=[-0.5, 0.0, 1.0], ctrl=ctrl)
elif kind == cfg.ROTATION_ZZ:
q0, q1 = qid[0], qid[1]
para = [phase, 0.0, fac]
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
self.append_gate(kind=cfg.ROTATION_Z, qid=[q1, -1], para=para, ctrl=ctrl, tag=tag)
self.append_gate(kind=cfg.CONTROLLED_X, qid=[q0, q1], ctrl=ctrl)
else:
raise ValueError("gate: {} is not supported.".format(cfg.GATE_STRING[kind]))
return self
# operate quantum circuit
[docs] def operate_qcirc(self, qcirc, qctrl=None):
"""
operate quantum circuit
Parameters
----------
qcirc : instance of QCirc
quantum circuit
qctrl : int
control qubit id
Returns
-------
self : instance of QState
quantum state after executing the quantum circuit
"""
if qctrl is None:
self.merge_mutable(qcirc)
else:
qc_qctrl = qcirc.add_control(qctrl=qctrl)
self.merge_mutable(qc_qctrl)
return self
# c-library for qstate
from qlazy.lib.qcirc_c import (qcirc_init, qcirc_copy, qcirc_merge,
qcirc_merge_mutable, qcirc_is_equal,
qcirc_append_gate, qcirc_kind_first,
qcirc_pop_gate, qcirc_set_params,
qcirc_get_tag_phase, qcirc_get_tag_list,
qcirc_free)