# -*- coding: utf-8 -*-
""" Stabilizer State """
import ctypes
import random
from collections import Counter
import qlazy.config as cfg
from qlazy.util import is_clifford_gate, get_qgate_qubit_num
from qlazy.QObject import QObject
[docs]class MDataStabilizer:
""" Measured Data for Stabilizer
Attributes
----------
frequency : Counter
frequencies of measured value.
last : str
last measured value.
qid : list of int
qubit id's list.
qubit_num : int
qubit number of the quantum state (= log(state_num)).
"""
def __init__(self, frequency=None, last=None, qid=None, qubit_num=0):
self.frequency = frequency
self.last = last
self.qid = qid
self.qubit_num = qubit_num
[docs]class Stabilizer(ctypes.Structure, QObject):
""" Stabilizer State
Attributes
----------
qubit_num : int
number of qubits.
gene_num : int
number of generators.
"""
_fields_ = [
('gene_num', ctypes.c_int),
('qubit_num', ctypes.c_int),
('pauli_factor', ctypes.c_void_p),
('check_matrix', ctypes.c_void_p),
]
def __new__(cls, qubit_num=None, gene_num=None, pp_list=None, seed=None, **kwargs):
"""
Parameters
----------
gene_num : int
number of generators.
qubit_num : int
number of qubit.
Notes
-----
You must specify 'qubit_num'. If 'gene_num' is not specified,
'gene_num' is equal to 'qubit_num.'
"""
if seed is None:
seed = random.randint(0, 1000000)
if pp_list is not None:
gene_num = len(pp_list)
qubit_num = 0
for pp in pp_list:
qubit_num = max([qubit_num] + pp.qid)
qubit_num += 1
obj = stabilizer_init(gene_num, qubit_num, seed)
sb = ctypes.cast(obj.value, ctypes.POINTER(cls)).contents
for i, pp in enumerate(pp_list):
for j, q in enumerate(pp.qid):
sb.set_pauli_op(i, q, pp.pauli_list[j])
else:
if qubit_num is None:
raise ValueError("qubit number must be set.")
if gene_num is None:
gene_num = qubit_num
if qubit_num < 1 or gene_num < 1:
raise ValueError("qubit_num and gene_num must be positive integer.")
obj = stabilizer_init(gene_num, qubit_num, seed)
sb = ctypes.cast(obj.value, ctypes.POINTER(cls)).contents
return sb
def __str__(self):
return self.get_str()
[docs] @classmethod
def add_method(cls, method):
"""
add method (custum gate).
Parameters
----------
method : func
method (custum gate) to add.
"""
setattr(cls, method.__name__, method)
[docs] @classmethod
def add_methods(cls, *methods):
"""
add methods (custum gates).
Parameters
----------
methods : func, func, ...
arguments of methods (custum gates) to add.
"""
for method in methods:
if callable(method):
setattr(cls, method.__name__, method)
else:
raise ValueError("can't add method.")
[docs] def get_str(self):
""" get string of the stabilzer """
str_out = ""
for i in range(self.gene_num):
pauli_fac_complex = self.get_pauli_fac(i)
gene_str = ""
for j in range(self.qubit_num):
s = self.get_pauli_op(i, j)
gene_str += s
if pauli_fac_complex == 1+0j:
pauli_fac_str = " "
elif pauli_fac_complex == 1j:
pauli_fac_str = " i"
elif pauli_fac_complex == -1+0j:
pauli_fac_str = " -"
elif pauli_fac_complex == -1j:
pauli_fac_str = "-i"
else:
raise ValueError("can't get string.")
str_out += "{0:}{1:}\n".format(pauli_fac_str, gene_str)
return str_out
[docs] def show(self):
"""
show the generators of stabilizer.
Parameters
----------
None
Returns
-------
None
"""
digits = len(str(self.gene_num - 1))
s = self.get_str()
gene_list = s.rstrip().split('\n')
for i, gene_str in enumerate(gene_list):
print("g[{0:{digits}d}]:{1:}".format(i, gene_str, digits=digits))
[docs] def clone(self):
"""
get the copy of the quantum state.
Parameters
----------
None
Returns
-------
stab : instance of Stabilizer
copy of the original stabilizer.
"""
obj = stabilizer_copy(self)
sb = ctypes.cast(obj.value, ctypes.POINTER(self.__class__)).contents
return sb
[docs] def get_rank(self):
""" get rank of the stabilizer """
rank = stabilizer_get_rank(self)
return rank
[docs] def set_all(self, pauli_op_str):
"""
set all of the qubits same pauli operators.
Parameters
----------
pauli_op_str : str
string of pauli operator ('X','Y','Z').
Returns
-------
self : instance of Stabilizer
Examples
--------
>>> sb = Stabilizer(qubit_num=3)
>>> sb.set_all('Z')
>>> sb.show()
g[0]: ZII
g[1]: IZI
g[2]: IIZ
"""
self.reset()
length = min(self.gene_num, self.qubit_num)
[self.set_pauli_op(i, i, pauli_op_str) for i in range(length)]
return self
[docs] def set_pauli_fac(self, gene_id, pauli_fac_str):
"""
set pauli factor of generator ('+1','-1','+i','-i').
Parameters
----------
gene_id : int
generator id to set.
Returns
-------
self : instance of Stabilizer
"""
if pauli_fac_str in ("+1", "1"):
pauli_fac = cfg.REAL_PLUS
elif pauli_fac_str == "+i":
pauli_fac = cfg.IMAG_PLUS
elif pauli_fac_str == "-1":
pauli_fac = cfg.REAL_MINUS
elif pauli_fac_str == "-i":
pauli_fac = cfg.IMAG_MINUS
else:
raise ValueError("can't set pauli factor.")
stabilizer_set_pauli_fac(self, gene_id, pauli_fac)
return self
[docs] def get_pauli_fac(self, gene_id):
"""
get pauli factor of generator ('+1','-1','+i','-i').
Parameters
----------
gene_id : int
generator id to get.
Returns
-------
pauli_fac_complex : complex
complex facto of the generator (1+0j, 1j, -1+0j, -1j)
"""
pauli_fac = stabilizer_get_pauli_fac(self, gene_id)
if pauli_fac == cfg.REAL_PLUS:
pauli_fac_complex = 1+0j
elif pauli_fac == cfg.IMAG_PLUS:
pauli_fac_complex = 1j
elif pauli_fac == cfg.REAL_MINUS:
pauli_fac_complex = -1+0j
elif pauli_fac == cfg.IMAG_MINUS:
pauli_fac_complex = -1j
else:
raise ValueError("can't get pauli factor.")
return pauli_fac_complex
[docs] def set_pauli_op(self, gene_id, qubit_id, pauli_op_str):
"""
set pauli operator ('I','X','Y','Z').
Parameters
----------
gene_id : int
generator id to set.
qubit_id : int
qubit id to set.
Returns
-------
self : instance of Stabilizer
"""
if pauli_op_str == 'X':
pauli_op = cfg.PAULI_X
elif pauli_op_str == 'Y':
pauli_op = cfg.PAULI_Y
elif pauli_op_str == 'Z':
pauli_op = cfg.PAULI_Z
elif pauli_op_str == 'I':
pauli_op = cfg.IDENTITY
else:
raise ValueError("can't set pauli operator.")
stabilizer_set_pauli_op(self, gene_id, qubit_id, pauli_op)
return self
[docs] def get_pauli_op(self, gene_id, qubit_id):
"""
get pauli operator ('I','X','Y','Z').
Parameters
----------
gene_id : int
generator id to get.
qubit_id : int
qubit id to get.
Returns
-------
pauli_op_str : str
pauli operator ('I','X','Y','Z')
"""
pauli_op = stabilizer_get_pauli_op(self, gene_id, qubit_id)
if pauli_op == cfg.PAULI_X:
pauli_op_str = 'X'
elif pauli_op == cfg.PAULI_Y:
pauli_op_str = 'Y'
elif pauli_op == cfg.PAULI_Z:
pauli_op_str = 'Z'
elif pauli_op == cfg.IDENTITY:
pauli_op_str = 'I'
else:
raise ValueError("can't get pauli operator.")
return pauli_op_str
# measurement
[docs] def measure(self, qid=None):
"""
one shot measurement in Z-direction.
Parameters
----------
qid : list of int
qubit id list to measure.
Returns
-------
mval : str
measurement value.
Examples
--------
>>> sb = Stabilizer(qubit_num=2).set_all('Z').h(0).cx(0,1)
>>> sb.show()
>>> print(sb.measure(qid=[0,1]))
>>> sb.show()
g[0]: XX
g[1]: ZZ
00
g[0]: ZI
g[1]: ZZ
"""
mval = self.m(qid=qid, shots=1).last
return mval
[docs] def m(self, qid=None, shots=cfg.DEF_SHOTS):
"""
measurement in Z-direction.
Parameters
----------
qid : list of int
qubit id list to measure.
shots : int, default 1
number of measurements.
Returns
-------
md : instance of MDataStabilizer
measurement data.
Examples
--------
>>> sb = Stabilizer(qubit_num=2).set_all('Z').h(0).cx(0,1)
>>> md = qs.m(qid=[0,1], shots=100)
>>> print(md.freauency)
>>> print(md.last)
Counter({'00':55,'11':45})
11
See Also
--------
MDataStabilizer class
"""
md = self.mz(qid=qid, shots=shots)
return md
[docs] def mx(self, qid=None, shots=cfg.DEF_SHOTS):
"""
measurement in X-direction.
Parameters
----------
qid : list of int
qubit id list to measure.
shots : int, default 1
number of measurements.
Returns
-------
md : instance of MDataStabilizer
measurement data.
"""
[self.h(q) for q in qid]
md = self.mz(qid=qid, shots=shots)
[self.h(q) for q in qid]
return md
[docs] def my(self, qid=None, shots=cfg.DEF_SHOTS):
"""
measurement in Y-direction.
Parameters
----------
qid : list of int
qubit id list to measure.
shots : int, default 1
number of measurements.
Returns
-------
md : instance of MDataStabilizer
measurement data.
"""
[self.s_dg(q).h(q) for q in qid]
md = self.mz(qid=qid, shots=shots)
[self.h(q).s(q) for q in qid]
return md
[docs] def mz(self, qid=None, shots=cfg.DEF_SHOTS):
"""
measurement in Z-direction.
Parameters
----------
qid : list of int
qubit id list to measure.
shots : int, default 1
number of measurements.
Returns
-------
md : instance of MDataStabilizer
measurement data.
Examples
--------
>>> sb = Stabilizer(2).set_all('Z').h(0).cx(0,1)
>>> md = qs.mz(qid=[0,1], shots=100)
>>> print(md.freauency)
>>> print(md.last)
Counter({'00':55,'11':45})
11
See Also
--------
MDataStabilizer class
"""
frequency = Counter()
# 1st to last-1 measurement
for _ in range(shots-1):
st = self.clone()
mval_str = ''
for q in qid:
mval = stabilizer_measure(st, q)
mval_str += str(mval)
frequency += {mval_str:1}
# last measurement
mval_str = ''
for q in qid:
m = stabilizer_measure(self, q)
mval_str += str(m)
frequency += {mval_str:1}
last = mval_str
md = MDataStabilizer(frequency=frequency, last=last, qid=qid,
qubit_num=self.qubit_num)
return md
[docs] @classmethod
def del_all(cls, *stabs):
"""
free memory of the all stabilizer.
Parameters
----------
stabs : instance of Stabilizer,instance of Stabilizer,...
set of Stabilizer instances
Returns
-------
None
"""
for sb in stabs:
if isinstance(sb, (list, tuple)):
cls.del_all(*sb)
elif isinstance(sb, cls):
del sb
else:
raise ValueError("can't free stabilizer.")
[docs] def operate(self, pp=None, ctrl=None, qctrl=None):
"""
operate unitary operator to stabilizer.
Parameters
----------
pp : instance of PauliProduct
pauli product to operate
ctrl : int
contoroll qubit id for controlled pauli product
(this option will be removed near future)
qctrl : int
contoroll qubit id for controlled pauli product
Returns
-------
self : instance of Stabilizer
stabilizer after operation
"""
pauli_list = pp.pauli_list
qid = pp.qid
factor = pp.factor
if ctrl is None:
ctrl = qctrl
if ctrl is None:
for q, pauli in zip(qid, pauli_list):
if pauli == 'X':
self.x(q)
elif pauli == 'Y':
self.y(q)
elif pauli == 'Z':
self.z(q)
else:
continue
else:
if ctrl in qid:
raise ValueError("controll and target qubit id conflict")
for q, pauli in zip(qid, pauli_list):
if pauli == 'X':
self.cx(ctrl, q)
elif pauli == 'Y':
self.cy(ctrl, q)
elif pauli == 'Z':
self.cz(ctrl, q)
else:
continue
if factor == -1.+0.j:
self.z(ctrl)
elif factor == 0.+1.j:
self.s(ctrl)
elif factor == 0.-1.j:
self.s_dg(ctrl)
return self
# operate gate
[docs] def operate_gate(self, kind=None, qid=None, **kwargs):
"""
operate gate
Parameters
----------
kind : int
kind of the gate
qid : list
quantum id list
Returns
-------
self : instance of QState
quantum state
"""
if kind == cfg.RESET:
if qid is not None:
raise ValueError("qid is not supported.")
for i in range(self.gene_num):
for j in range(self.qubit_num):
self.set_pauli_op(i, j, 'I')
self.set_pauli_fac(i, '+1')
elif kind == cfg.CONTROLLED_Y:
q0, q1 = qid[0], qid[1]
self.cz(q0, q1).cx(q0, q1).s(q0)
elif is_clifford_gate(kind):
if get_qgate_qubit_num(kind) == 1:
q0 = qid[0]
stabilizer_operate_qgate(self, kind, q0, 0)
elif get_qgate_qubit_num(kind) == 2:
q0, q1 = qid[0], qid[1]
stabilizer_operate_qgate(self, kind, q0, q1)
else:
raise ValueError("length of qid must be 1 or 2.")
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
-------
qs : instance of QState
quantum state after executing the quantum circuit
Notes
-----
The quantum circut must be clifford.
"""
if qcirc.is_clifford() is False:
raise ValueError("qcirc must be clifford quantum circuit.")
if self.qubit_num < qcirc.qubit_num:
raise ValueError("qubit number of quantum state must be equal or larger than the quantum circuit size.")
if qctrl is None:
stabilizer_operate_qcirc(self, cmem=None, qcirc=qcirc, shots=1, cid=None)
else:
qc_qctrl = qcirc.add_control(qctrl=qctrl)
stabilizer_operate_qcirc(self, cmem=None, qcirc=qc_qctrl, shots=1, cid=None)
return self
def __del__(self):
stabilizer_free(self)
# c-library for stabilizer
from qlazy.lib.stabilizer_c import (stabilizer_init, stabilizer_copy,
stabilizer_set_pauli_fac, stabilizer_get_pauli_fac,
stabilizer_set_pauli_op, stabilizer_get_pauli_op,
stabilizer_operate_qgate, stabilizer_get_rank,
stabilizer_measure, stabilizer_operate_qcirc,
stabilizer_free)