from qubiter.CktEmbedder import *
from qubiter.Controls import *
from qubiter.OneQubitGate import *
import re
import qubiter.utilities_gen as utg
from qubiter.PlaceholderManager import *
import sys
import os
from IPython.display import HTML, display
if 'autograd.numpy' not in sys.modules:
import numpy as np
[docs]class SEO_writer:
"""
The constructor of this class opens an English and a Picture file. Every
other method of the class writes, each time it is called, a single line
in each of those 2 files.
Note SEO stands for Sequence of Elementary Operations.
So what are English and Picture files?
We use 3 types of files to characterize a single quantum circuit (in the
gate model): (1) an English file (2) a Picture file (3) a Log file.
Log files are written by class SEO_reader, whereas English and Picture
files are written by this class, SEO_writer.
A log file just contains useful information like the number of lines of
the English file (same as that of Picture file) and their number of
operations.
The lines of an English and Picture file are in 1-1 correspondence,
each line representing a single operation (e.g., a multi-controlled one
qubit gate or a multi-controlled 2 qubit swap).
The English file (resp., Picture file) contains complete (resp.,
partial, in the form of an ASCII picture) info about the operation
specified by each line.
In English and Picture files, time flows downward.
The class SEO_writer gives the bool option ZL. When this argument is set
to True (resp., False), the Picture file shows the zero qubit last (
resp., first), and the remaining qubits in consecutive order. Picture
files written with zero bit last (resp., first) are labelled prefix +
'_ZLpict.text' (resp., prefix + '_ZFpict.txt'). The ZL choice does not
affect the English file.
See the following and earlier arXiv papers by R.R.Tucci for more info on
English and Picture files.
http://arxiv.org/abs/1004.2205
"Quibbs, a Code Generator for Quantum Gibbs Sampling"
The following pdf is stored in the same folder as this python file.
qubiter_rosetta_stone.pdf
This pdf gives examples of lines in analytic/Picture/English formats.
The Picture file examples follow the ZL convention.
3 kinds (called 0, 1, 2) of measurements MEAS are allowed. A type 0
measurement inserts a projector ``|0><0| = n = P_0`` at the target bit.
A type 1 measurement inserts a projector ``|1><1| = nbar = P_1`` at the
target bit. A type 2 measurement stores a copy of the state vector after
``|0><0|`` has been applied, and another copy after ``|1><1|`` has been
applied.
If a vertical wire hasn't been measured as type 2 measurement,
it is drawn in pic file as "|"; otherwise, it is drawn as ":".
Attributes
----------
emb : CktEmbedder
english_out : _io.TextIOWrapper
file object for output text file that stores English description of
circuit
file_prefix : str
beginning of the name of both English and Picture files
gate_line_counter : int
indentation : int
Starts at 0, Grows by 4 at end of each write_LOOP and decrease by 4
at beginning of each write_NEXT
measured_bits : list(int)
list of bits that have been measured with type 2 measurement and
haven't been reset to ``|0>`` or ``|1>``
picture_out : _io.TextIOWrapper
file object for output text file that stores ASCII Picture
description of circuit
ZL : bool
"""
[docs] def __init__(self, file_prefix, emb, ZL=True,
english_out=None, picture_out=None):
"""
Constructor
Parameters
----------
file_prefix : str
emb : CktEmbedder
ZL : bool
english_out : _io.TextIOWrapper
picture_out : _io.TextIOWrapper
Returns
-------
"""
self.gate_line_counter = 0
self.file_prefix = file_prefix
self.emb = emb
self.ZL = ZL
self.measured_bits = []
if english_out is None and file_prefix:
self.english_out = open(utg.preface(self.get_eng_file_path(
rel=True)), 'wt')
else:
self.english_out = english_out
if picture_out is None and file_prefix:
self.picture_out = open(utg.preface(self.get_pic_file_path(
rel=True)), 'wt')
else:
self.picture_out = picture_out
self.indentation = 0
[docs] def get_eng_file_path(self, rel=False):
"""
Returns path (relative if rel is True, absolute if rel is False) of
English file.
Attributes
----------
rel : bool
Returns
-------
str
"""
rel_path = utg.get_eng_file_rel_path(self.file_prefix,
self.emb.num_qbits_aft)
# print("..,,mmmm", rel_path)
return rel_path if rel else utg.preface(rel_path)
[docs] def get_pic_file_path(self, rel=False):
"""
Returns path (relative if rel is True, absolute if rel is False) of
Picture file.
Attributes
----------
rel : bool
Returns
-------
str
"""
rel_path = utg.get_pic_file_rel_path(self.file_prefix,
self.emb.num_qbits_aft,
ZL=self.ZL)
return rel_path if rel else utg.preface(rel_path)
[docs] def close_files(self):
"""
Closes English and Picture files that were opened by the constructor.
Returns
-------
None
"""
# print("bbnnnvvv", 'closing files')
self.english_out.close()
self.picture_out.close()
[docs] def delete_files(self):
"""
This method will delete the English and Picture files. The files are
closed before being deleted in case that hasn't been done yet.
Closing a file a second time does nothing.
Returns
-------
None
"""
self.close_files()
import os
os.remove(self.get_eng_file_path())
os.remove(self.get_pic_file_path())
[docs] @staticmethod
def gen_html_from_eng_or_pic_file(f):
"""
This function returns a string for an html table generated from an
English or Picture file. The table has two columns.
The first column gives the line numbers starting from 1.
Parameters
----------
f : file
f is file object returned by open()
Returns
-------
str
"""
k = 1
line = f.readline()
all_lines = ''
td0 = "<td style='border-right:1px solid red;'>"
td1 = "<td style='text-align:left;'>"
while line:
all_lines += td0 + str(k) + "</td>" + td1 +\
"<pre>" + line.strip() + "</pre>" +\
'</td></tr>'
k += 1
line = f.readline()
table = "<table style='font-family:monospace'><tr>" +\
all_lines + '</table>'
# print(table)
return table
[docs] def print_eng_file(self, jup=False):
"""
Prints English file.
Parameters
----------
jup : bool
If jup=False, it prints text. Otherwise, it draws in a jupyter
notebook a table with line numbers starting at 1
Returns
-------
None
"""
with open(utg.preface(self.get_eng_file_path(rel=True)), 'r') as f:
if not jup:
print(f.read())
else:
dis_obj = HTML(SEO_writer.gen_html_from_eng_or_pic_file(f))
display(dis_obj)
[docs] def print_pic_file(self, jup=False):
"""
Prints Picture file.
Parameters
----------
jup : bool
If jup=False, it prints text. Otherwise, it draws in a jupyter
notebook a table with line numbers starting at 1
Returns
-------
None
"""
with open(utg.preface(self.get_pic_file_path(rel=True)), 'r') as f:
if not jup:
print(f.read())
else:
dis_obj = HTML(SEO_writer.gen_html_from_eng_or_pic_file(f))
display(dis_obj)
[docs] def colonize(self, pic_line):
"""
This function returns new version of pic_line. Every "|" wire is
replaced by ":" colon wire and vice versa iff the wire is at a bit
position that has been measured (type 2 measurement) in the past and
not reset. This function assumes pic_line in ZL convention so must
call this function before calling write_ZF_or_ZL_pic_line()
Parameters
----------
pic_line : str
Returns
-------
str
"""
num_qbits = self.emb.num_qbits_aft
li = list(pic_line)
for bit in range(0, num_qbits):
m = 4*(num_qbits - 1 - bit)
if bit in self.measured_bits:
if li[m] == "|":
li[m] = ":"
else:
if li[m] == ":":
li[m] = "|"
return "".join(li)
[docs] def write_ZF_or_ZL_pic_line(self, pic_line):
"""
Writes a line in the Picture file using either the ZF or ZL
conventions. pic_line is originally written in ZL format, so this
method does nothing to pic_line if ZL option is chosen but reverses
order of gates if ZF option chosen.
Parameters
----------
pic_line : str
Returns
-------
None
"""
# example:
# Ry--R---<--->---Rz
if not self.ZL:
pic_line = pic_line.strip()
# delimiter is -- or --- or 2 spaces or 3 spaces
nodes = re.split('-{2,3}| {2,3}', pic_line)
dash_or_space = [pic_line[k*4 + 3] for k in range(len(nodes)-1)]
nodes = list(reversed(nodes))
dash_or_space = list(reversed(dash_or_space))
for kk in range(len(nodes)):
if nodes[kk] == '<':
nodes[kk] = '>'
elif nodes[kk] == '<<':
nodes[kk] = '>>'
elif nodes[kk] == '>':
nodes[kk] = '<'
elif nodes[kk] == '>>':
nodes[kk] = '<<'
new_line = ''
k = 0
for nd in nodes:
if k < len(nodes) - 1:
if len(nd) == 1:
new_line += (nd + dash_or_space[k]*3)
elif len(nd) == 2:
new_line += (nd + dash_or_space[k]*2)
else: # k = len(nodes) - 1
new_line += nd
k += 1
else:
new_line = pic_line
self.picture_out.write(new_line)
[docs] def write_IF_M_beg(self, trols):
"""
Writes a 'IF_M( <controls> ){' line in eng & pic files.
Parameters
----------
trols : Controls
Returns
-------
None
"""
aft_trols = trols.new_embedded_self(self.emb)
s = "IF_M(\t"
num_controls = len(aft_trols.bit_pos)
for c in range(num_controls):
s += str(aft_trols.bit_pos[c]) + \
("T" if aft_trols.kinds[c] == True else "F") + \
("\t){\n" if (c == num_controls - 1) else "\t")
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
[docs] def write_IF_M_end(self):
"""
Writes an '}IF_M' line in eng and pic files.
Parameters
----------
Returns
-------
None
"""
s = "}IF_M\n"
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
[docs] def write_LOOP(self, loop_num, nreps):
"""
Writes a 'LOOP' line in eng & pic files. The gates between a LOOP
line and its partner NEXT line are to be repeated a number of times
called nreps.
Parameters
----------
loop_num : int
nreps : int
Returns
-------
None
"""
s = "LOOP\t" + str(loop_num) + "\tNREPS=\t" + str(nreps) + '\n'
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
self.indentation += 4
[docs] def write_MEAS(self, tar_bit_pos, kind):
"""
Writes a 'MEAS' line in eng & pic files. This denotes a measurement
step. We allow 3 kinds of measurements (0, 1, 2) at a target bit.
Parameters
----------
tar_bit_pos : int
kind : int
either 0, 1 or 2
Returns
-------
None
"""
# num_qbits_bef = self.emb.num_qbits_bef
num_qbits_aft = self.emb.num_qbits_aft
aft_tar_bit_pos = self.emb.aft(tar_bit_pos)
assert kind in [0, 1, 2], "unsupported measurement kind"
if kind == 2:
self.measured_bits.append(aft_tar_bit_pos)
# english file
s = 'MEAS\t' + str(kind) + '\tAT\t' + str(aft_tar_bit_pos) + '\n'
self.english_out.write(' '*self.indentation + s)
# picture file
pic_line = ""
biggest = aft_tar_bit_pos
smallest = aft_tar_bit_pos
# k a bit position
for k in range(num_qbits_aft-1, biggest, -1):
pic_line += "| "
if kind == 0:
pic_line += "M0 "
elif kind == 1:
pic_line += 'M1 '
else:
pic_line += 'M '
for k in range(smallest-1, -1, -1):
pic_line += "| "
pic_line = self.colonize(pic_line)
self.write_ZF_or_ZL_pic_line(pic_line)
self.picture_out.write("\n")
[docs] def write_NEXT(self, loop_num):
"""
Writes a 'NEXT' line in eng & pic files.
Parameters
----------
loop_num : int
Returns
-------
None
"""
self.indentation -= 4
s = "NEXT\t" + str(loop_num) + '\n'
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
[docs] def write_NOTA(self, bla_str, permission=True):
"""
Writes a 'NOTA' line in eng & pic files. As the name implies,
a NOTA is just a note or comment such as "I love you Mary". It is
not a gate.
Parameters
----------
bla_str : str
permission : bool
General permission, useful for turning off whole batches of NOTAs
Returns
-------
None
"""
if permission:
s = "NOTA\t" + bla_str.rstrip() + '\n'
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
[docs] def write_PRINT(self, style):
"""
Writes a 'PRINT' line in eng & pic files.
Parameters
----------
style : str
Returns
-------
None
"""
s = "PRINT\t" + style + '\n'
self.english_out.write(' '*self.indentation + s)
self.picture_out.write(' '*self.indentation + s)
[docs] def rads_to_degs_str(self, rads):
"""
This method returns
str(rads*180/pi) if isinstance(rads, float)
rads if is_legal_var_name(rads) (this implies rads is str)
aborts otherwise.
The method is only used inside this class so I am making it
non-static even though it doesn't use self.
Parameters
----------
rads : float | str
Returns
-------
str
"""
# print("--nnn", type(rads))
# np.float types are different from float!!!
if isinstance(rads, (int, float, np.floating)):
# print("--nnn", str(rads*180/np.pi))
return '{0:0.6f}'.format(rads*180/np.pi)
else:
assert PlaceholderManager.is_legal_var_name(rads), \
"attempting to write an illegal variable name: '" +\
str(rads) + "'"
return rads
[docs] def write_controlled_preamble(self, trols):
"""
This is an internal function, used as preamble to all methods that
are named write_controlled_...()
Parameters
----------
trols : Controls
Returns
-------
Controls
"""
self.gate_line_counter += 1
assert not self.english_out.closed
assert not self.picture_out.closed
aft_trols = trols.new_embedded_self(self.emb)
# add extra controls if there are any
extra_dict = self.emb.extra_controls.bit_pos_to_kind
if extra_dict:
aft_trols.bit_pos_to_kind.update(extra_dict)
aft_trols.refresh_lists()
return aft_trols
[docs] def write_controlled_one_qbit_gate(
self, tar_bit_pos, trols, one_qbit_gate_fun, fun_arg_list=None):
"""
Writes a line in eng & pic files for a one bit gate (from class
OneQubitGate) with >= 0 controls.
Parameters
----------
tar_bit_pos : int
trols : Controls
one_qbit_gate_fun : function
maps Any->np.ndarray
fun_arg_list : list
Returns
-------
None
"""
aft_trols = self.write_controlled_preamble(trols)
# num_qbits_bef = self.emb.num_qbits_bef
num_qbits_aft = self.emb.num_qbits_aft
aft_tar_bit_pos = self.emb.aft(tar_bit_pos)
# number of controls may be zero
num_controls = len(aft_trols.bit_pos)
assert aft_tar_bit_pos not in aft_trols.bit_pos,\
"target bit cannot be a control bit"
if fun_arg_list is None:
fun_arg_list = []
# english file
self.english_out.write(' '*self.indentation)
if one_qbit_gate_fun == OneQubitGate.had2:
self.english_out.write("HAD2")
elif one_qbit_gate_fun == OneQubitGate.phase_fac:
self.english_out.write("PHAS\t" +
self.rads_to_degs_str(fun_arg_list[0]))
elif one_qbit_gate_fun == OneQubitGate.P_0_phase_fac:
self.english_out.write("P0PH\t" +
self.rads_to_degs_str(fun_arg_list[0]))
elif one_qbit_gate_fun == OneQubitGate.P_1_phase_fac:
self.english_out.write("P1PH\t" +
self.rads_to_degs_str(fun_arg_list[0]))
elif one_qbit_gate_fun == OneQubitGate.rot_ax:
ang_rads = fun_arg_list[0]
axis = fun_arg_list[1]
degs_str = self.rads_to_degs_str(ang_rads)
if axis == 1:
self.english_out.write("ROTX\t" + degs_str)
elif axis == 2:
self.english_out.write("ROTY\t" + degs_str)
elif axis == 3:
self.english_out.write("ROTZ\t" + degs_str)
else:
assert False
elif one_qbit_gate_fun == OneQubitGate.rot:
x_degs = self.rads_to_degs_str(fun_arg_list[0])
y_degs = self.rads_to_degs_str(fun_arg_list[1])
z_degs = self.rads_to_degs_str(fun_arg_list[2])
self.english_out.write("ROTN\t" +
x_degs + "\t" + y_degs + "\t" + z_degs)
elif one_qbit_gate_fun == OneQubitGate.sigx:
self.english_out.write("SIGX")
elif one_qbit_gate_fun == OneQubitGate.sigy:
self.english_out.write("SIGY")
elif one_qbit_gate_fun == OneQubitGate.sigz:
self.english_out.write("SIGZ")
elif one_qbit_gate_fun == OneQubitGate.u2:
ph_degs = self.rads_to_degs_str(fun_arg_list[0])
x_degs = self.rads_to_degs_str(fun_arg_list[1])
y_degs = self.rads_to_degs_str(fun_arg_list[2])
z_degs = self.rads_to_degs_str(fun_arg_list[3])
self.english_out.write("U_2_\t" + ph_degs + "\t" +
x_degs + "\t" + y_degs + "\t" + z_degs)
else:
assert False, "writing an unsupported controlled gate\n" +\
one_qbit_gate_fun.__name__
self.english_out.write("\tAT\t" + str(aft_tar_bit_pos) +
("\tIF\t" if num_controls != 0 else "\n"))
# list bit-positions in decreasing order
for c in range(num_controls):
self.english_out.write(
str(aft_trols.bit_pos[c]) +
("T" if aft_trols.kinds[c] == True else "F") +
("\n" if c == num_controls - 1 else "\t"))
# picture file
pic_line = ""
biggest = aft_tar_bit_pos
smallest = aft_tar_bit_pos
if num_controls != 0:
biggest = max(aft_trols.bit_pos[0], aft_tar_bit_pos)
smallest = min(aft_trols.bit_pos[num_controls-1], aft_tar_bit_pos)
# k a bit position
for k in range(num_qbits_aft-1, biggest, -1):
pic_line += "| "
c_int = 0
for k in range(biggest, smallest-1, -1):
is_target = (k == aft_tar_bit_pos)
is_control = False
control_kind = False
tres = ' '*3 if (k == smallest) else "---"
dos = ' '*2 if (k == smallest) else "--"
# c_int starts at last value
for c in range(c_int, num_controls, +1):
if k == aft_trols.bit_pos[c]:
is_control = True
control_kind = aft_trols.kinds[c]
c_int += 1
break
if is_control:
pic_line += ("@" if control_kind else "O") + tres
else: # is not control
if not is_target: # is not control or target
pic_line += "+" + tres
else: # is target
if one_qbit_gate_fun == OneQubitGate.had2:
pic_line += "H" + tres
elif one_qbit_gate_fun == OneQubitGate.phase_fac:
pic_line += "Ph" + dos
elif one_qbit_gate_fun == OneQubitGate.P_0_phase_fac:
pic_line += "OP" + dos
elif one_qbit_gate_fun == OneQubitGate.P_1_phase_fac:
pic_line += "@P" + dos
elif one_qbit_gate_fun == OneQubitGate.rot_ax:
axis = fun_arg_list[1]
if axis == 1:
pic_line += "Rx" + dos
elif axis == 2:
pic_line += "Ry" + dos
elif axis == 3:
pic_line += "Rz" + dos
else:
assert False
elif one_qbit_gate_fun == OneQubitGate.rot:
pic_line += "R" + tres
elif one_qbit_gate_fun == OneQubitGate.sigx:
pic_line += "X" + tres
elif one_qbit_gate_fun == OneQubitGate.sigy:
pic_line += "Y" + tres
elif one_qbit_gate_fun == OneQubitGate.sigz:
pic_line += "Z" + tres
elif one_qbit_gate_fun == OneQubitGate.u2:
rads1 = fun_arg_list[1]
rads2 = fun_arg_list[2]
rads3 = fun_arg_list[3]
def both_are_small(r1, r2):
if isinstance(r1, str) or isinstance(r2, str):
return False
if abs(r1) > 1e-6 or abs(r2) > 1e-6:
return False
return True
small_12 = both_are_small(rads1, rads2)
small_23 = both_are_small(rads2, rads3)
small_13 = both_are_small(rads1, rads3)
if small_12 and small_23:
pic_line += "Ph" + dos
elif small_23:
pic_line += "Ux" + dos
elif small_13:
pic_line += "Uy" + dos
elif small_12:
pic_line += "Uz" + dos
else:
pic_line += "U" + tres
else:
assert False, \
"writing an unsupported controlled gate\n" +\
one_qbit_gate_fun.__name__
for k in range(smallest-1, -1, -1):
pic_line += "| "
pic_line = self.colonize(pic_line)
self.write_ZF_or_ZL_pic_line(pic_line)
self.picture_out.write("\n")
[docs] def write_controlled_qbit_swap(self, bit1, bit2, trols, rads_list=None):
"""
If rads_list=None, this method writes a line in eng & pic files for
a 'SWAP' with >= 0 controls
NOTE: SWAP is qbit symmetric: SWAP(0,1) = SWAP(1,0)
If rads_list is not None and equals a list of 2 angles, rads_list=[
rads0, rads1], this method writes a generalization of SWAP that I
call SWAY (just to have a verb that is close to swap)
SWAY =
[1 0 0]
[0 U2 0]
[0 0 1]
where U2 is the most general 2-dim unitary matrix satisfying
sigx U2 sigx = U2.
If U2 is parametrized as
U2 = exp(j*(rads0 + rads1*sigx + rads2*sigy + rads3*sigz))
then SWAY is qbit symmetric (SWAY(0,1)=SWAY(1,0)) iff sigx U2 sigx
= U2 iff rads2=rads3=0.
SWAY includes SWAP, sqrt(SWAP), iSWAP, sqrt(iSWAP), PWAP,
sqrt(PSWAP) etc.
Parameters
----------
bit1 : int
bit2 : int
bit1 and bit2 are the positions of the swapped bits.
trols : Controls
rads_list : list[float | str ] | None
Returns
-------
None
"""
aft_trols = self.write_controlled_preamble(trols)
num_qbits_bef = self.emb.num_qbits_bef
num_qbits_aft = self.emb.num_qbits_aft
# aft_tar_bit_pos = self.emb.aft(tar_bit_pos)
# number of controls may be zero
num_controls = len(aft_trols.bit_pos)
assert bit1 != bit2, "swapped bits must be different"
assert -1 < bit1 < num_qbits_bef
assert -1 < bit2 < num_qbits_bef
x = [self.emb.aft(bit1), self.emb.aft(bit2)]
big = max(x)
small = min(x)
use_sway = False
if rads_list is not None:
assert len(rads_list) == 2
use_sway = True
# english file
gate_name = 'SWAP'
if use_sway:
gate_name = 'SWAY'
self.english_out.write(' '*self.indentation + gate_name +
'\t' + str(big) + "\t" + str(small))
if use_sway:
self.english_out.write('\tBY')
for k in range(2):
deg_str = self.rads_to_degs_str(rads_list[k])
self.english_out.write('\t' + deg_str)
self.english_out.write("\tIF\t" if num_controls != 0 else "\n")
# list bit-positions in decreasing order
for c in range(num_controls):
self.english_out.write(
str(aft_trols.bit_pos[c]) +
("T" if aft_trols.kinds[c] == True else "F") +
("\n" if (c == num_controls - 1) else "\t"))
# picture file
pic_line = ""
biggest = big
smallest = small
if num_controls != 0:
biggest = max(aft_trols.bit_pos[0], big)
smallest = min(aft_trols.bit_pos[num_controls-1], small)
# k a bit position
for k in range(num_qbits_aft-1, biggest, -1):
pic_line += "| "
c_int = 0
for k in range(biggest, smallest-1, -1):
is_big = (k == big)
is_small = (k == small)
is_control = False
control_kind = False
tres = ' '*3 if (k == smallest) else "---"
dos = ' '*2 if (k == smallest) else "--"
for c in range(c_int, num_controls, +1):
if k == aft_trols.bit_pos[c]:
is_control = True
control_kind = aft_trols.kinds[c]
c_int += 1
break
if is_control:
pic_line += ('@' if control_kind else 'O') + tres
else: # control not found
if is_big:
if use_sway:
pic_line += "<<" + dos
else:
pic_line += "<" + tres
elif is_small:
if use_sway:
pic_line += ">>" + dos
else:
pic_line += ">" + tres
else:
pic_line += "+" + tres
for k in range(smallest-1, -1, -1):
pic_line += "| "
pic_line = self.colonize(pic_line)
self.write_ZF_or_ZL_pic_line(pic_line)
self.picture_out.write("\n")
[docs] def write_controlled_multiplexor_gate(self,
tar_bit_pos, trols, rad_angles):
"""
Writes a line in eng & pic files for a multiplexor 'MP_Y' with >= 0
controls of either int (integer, intrinsic) or True/False kind.
Parameters
----------
tar_bit_pos : int
trols : Controls
rad_angles : list[float]
Returns
-------
None
"""
aft_trols = self.write_controlled_preamble(trols)
# num_qbits_bef = self.emb.num_qbits_bef
num_qbits_aft = self.emb.num_qbits_aft
aft_tar_bit_pos = self.emb.aft(tar_bit_pos)
# number of controls may be zero
num_controls = len(aft_trols.bit_pos)
assert aft_tar_bit_pos not in aft_trols.bit_pos,\
"target bit cannot be a control bit"
num_int_controls = aft_trols.get_num_int_controls()
assert num_int_controls != 0, \
"multiplexor with no half-moon controls"
num_angles = len(rad_angles)
assert num_angles == (1 << num_int_controls),\
"wrong number of multiplexor angles"
# english file
self.english_out.write(' '*self.indentation +
"MP_Y\tAT\t" + str(aft_tar_bit_pos) + "\tIF\t")
# list bit-positions in decreasing order
for c in range(num_controls):
x = aft_trols.kinds[c]
kind_str = ""
# bool is subclass of int
# so isinstance(x, int) will be true if x is bool!
if not isinstance(x, bool):
kind_str = ":" + str(x)
elif not x:
kind_str = "F"
elif x:
kind_str = "T"
self.english_out.write(
str(aft_trols.bit_pos[c]) + kind_str + "\t")
# use BY to indicate end of controls
self.english_out.write("\tBY\t")
for k in range(num_angles):
self.english_out.write(
self.rads_to_degs_str(rad_angles[k]) +
("\n" if k == (num_angles-1) else "\t"))
# picture file
pic_line = ""
biggest = aft_tar_bit_pos
smallest = aft_tar_bit_pos
if num_controls != 0:
biggest = max(aft_trols.bit_pos[0], aft_tar_bit_pos)
smallest = min(aft_trols.bit_pos[num_controls-1], aft_tar_bit_pos)
# k a bit position
for k in range(num_qbits_aft-1, biggest, -1):
pic_line += "| "
c_int = 0
for k in range(biggest, smallest-1, -1):
is_target = (k == tar_bit_pos)
is_control = False
control_kind = False
tres = ' '*3 if (k == smallest) else "---"
dos = ' '*2 if (k == smallest) else "--"
# c_int starts at last value
for c in range(c_int, num_controls, +1):
if k == aft_trols.bit_pos[c]:
is_control = True
control_kind = aft_trols.kinds[c]
c_int += 1
break
if is_control:
if not isinstance(control_kind, bool):
pic_line += "%" + tres
elif not control_kind:
pic_line += "O" + tres
elif control_kind:
pic_line += "@" + tres
else: # is not control
if not is_target: # is not control nor target
pic_line += "+" + tres
else: # is target
pic_line += "Ry" + dos
for k in range(smallest-1, -1, -1):
pic_line += "| "
pic_line = self.colonize(pic_line)
self.write_ZF_or_ZL_pic_line(pic_line)
self.picture_out.write("\n")
[docs] def write_controlled_diag_unitary_gate(self, trols, rad_angles):
"""
Writes a line in eng & pic files for a diagonal unitary gate 'DIAG'
with >= 0 controls of either int (integer, intrinsic) or True/False
kind.
Parameters
----------
trols : Controls
rad_angles : list[float]
Returns
-------
None
"""
aft_trols = self.write_controlled_preamble(trols)
# num_qbits_bef = self.emb.num_qbits_bef
num_qbits_aft = self.emb.num_qbits_aft
# aft_tar_bit_pos = self.emb.aft(tar_bit_pos)
# number of controls may be zero
num_controls = len(aft_trols.bit_pos)
num_int_controls = aft_trols.get_num_int_controls()
assert num_int_controls != 0, \
"d-unitary with no half-moon controls"
num_angles = len(rad_angles)
assert num_angles == (1 << num_int_controls),\
"wrong number of d-unitary angles"
# english file
self.english_out.write(' '*self.indentation +
"DIAG\tIF\t")
# list bit-positions in decreasing order
for c in range(num_controls):
x = aft_trols.kinds[c]
kind_str = ""
# bool is subclass of int
# so isinstance(x, int) will be true if x is bool!
if not isinstance(x, bool):
kind_str = ":" + str(x)
elif not x:
kind_str = "F"
elif x:
kind_str = "T"
self.english_out.write(
str(aft_trols.bit_pos[c]) + kind_str + "\t")
# use BY to indicate end of controls
self.english_out.write("\tBY\t")
for k in range(num_angles):
# print('nmnmkkk', k, num_angles, rad_angles[k])
self.english_out.write(
self.rads_to_degs_str(rad_angles[k]) +
("\n" if k == (num_angles-1) else "\t"))
# picture file
pic_line = ""
biggest = aft_trols.bit_pos[0]
smallest = aft_trols.bit_pos[num_controls-1]
# k a bit position
for k in range(num_qbits_aft-1, biggest, -1):
pic_line += "| "
c_int = 0
for k in range(biggest, smallest-1, -1):
is_control = False
control_kind = False
tres = ' '*3 if (k == smallest) else "---"
# c_int starts at last value
for c in range(c_int, num_controls, +1):
if k == aft_trols.bit_pos[c]:
is_control = True
control_kind = aft_trols.kinds[c]
c_int += 1
break
if is_control:
if not isinstance(control_kind, bool):
pic_line += "%" + tres
elif not control_kind:
pic_line += "O" + tres
elif control_kind:
pic_line += "@" + tres
else: # is not control
pic_line += "+" + tres
for k in range(smallest-1, -1, -1):
pic_line += "| "
pic_line = self.colonize(pic_line)
self.write_ZF_or_ZL_pic_line(pic_line)
self.picture_out.write("\n")
[docs] def write_one_qbit_gate(
self, tar_bit_pos, one_qbit_gate_fun, fun_arg_list=None):
"""
Write a line in eng & pic files for a one qubit gate (from class
OneQubitGate) with no controls.
Parameters
----------
tar_bit_pos : int
one_qbit_gate_fun : function
fun_arg_list : list
Returns
-------
None
"""
trols = Controls(2) # dummy with zero controls
self.write_controlled_one_qbit_gate(
tar_bit_pos, trols, one_qbit_gate_fun, fun_arg_list)
[docs] def write_qbit_swap(self, bit1, bit2, rads_list=None):
"""
Write a line in eng & pic files for a 'SWAP' if rads_list=None or
'SWAY' if rads_list!=None, with no controls.
Parameters
----------
bit1 : int
bit2 : int
rads_list : list[float | str] | None
Returns
-------
None
"""
trols = Controls(2) # dummy with zero controls
self.write_controlled_qbit_swap(bit1, bit2, trols, rads_list)
[docs] def write_H(self, tar_bit_pos):
"""
Writes HAD2 with no controls.
Parameters
----------
tar_bit_pos : int
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.had2)
[docs] def write_Rx(self, tar_bit_pos, rads):
"""
writes ROTX = exp(1j*rads*sig_x) with no controls.
Parameters
----------
tar_bit_pos : int
rads : float
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.rot_ax, [rads, 1])
[docs] def write_Ry(self, tar_bit_pos, rads):
"""
Writes ROTY = exp(1j*rads*sig_y) with no controls.
Parameters
----------
tar_bit_pos : int
rads : float
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.rot_ax, [rads, 2])
[docs] def write_Rz(self, tar_bit_pos, rads):
"""
Writes ROTZ = exp(1j*rads*sig_z) with no controls.
Parameters
----------
tar_bit_pos : int
rads : float
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.rot_ax, [rads, 3])
[docs] def write_Rn(self, tar_bit_pos, rads_list):
"""
Writes
ROTN = exp(1j*(rads_x*sig_x + rads_y*sig_y + rads_z*sig_z))
with no controls.
Parameters
----------
tar_bit_pos : int
rads_list : list[float]
[rads_x, rads_y, rads_z]
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.rot, rads_list)
[docs] def write_S(self, tar_bit_pos, herm=False):
"""
Writes P1PH = exp(1j*P_1*pi/2) or its Hermitian with no controls.
Parameters
----------
tar_bit_pos : int
herm : bool
Returns
-------
None
"""
sign = +1
if herm:
sign = -1
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.P_1_phase_fac,
[sign*np.pi/2])
[docs] def write_T(self, tar_bit_pos, herm=False):
"""
Writes P1PH = exp(1j*P_1*pi/4) or its Hermitian with no controls.
Parameters
----------
tar_bit_pos : int
herm : bool
Returns
-------
None
"""
sign = +1
if herm:
sign = -1
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.P_1_phase_fac,
[sign*np.pi/4])
[docs] def write_U2(self, tar_bit_pos, rads_list):
"""
Writes
UN_2= exp(1j*(rads0 + rads1*sig_x + rads2*sig_y + rads3*sig_z))
with no controls.
Parameters
----------
tar_bit_pos : int
rads_list : list[float]
[rads0, rads1, rads2, rads3]
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.u2, rads_list)
[docs] def write_X(self, tar_bit_pos):
"""
Writes SIGX with no controls.
Parameters
----------
tar_bit_pos : int
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.sigx)
[docs] def write_Y(self, tar_bit_pos):
"""
Writes SIGY with no controls.
Parameters
----------
tar_bit_pos : int
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.sigy)
[docs] def write_Z(self, tar_bit_pos):
"""
Writes SIGZ with no controls.
Parameters
----------
tar_bit_pos : int
Returns
-------
None
"""
self.write_one_qbit_gate(tar_bit_pos, OneQubitGate.sigz)
[docs] def write_cnot(self, control_bit, target_bit, kind=True):
"""
Writes a simple singly controlled not. If kind=True (resp. False),
cnot fires when control is ``|1>`` (resp. ``|0>``).
Parameters
----------
control_bit : int
target_bit : int
kind : bool
Returns
-------
None
"""
num_qbits = self.emb.num_qbits_aft
trols = Controls.new_single_trol(num_qbits, control_bit, kind)
self.write_controlled_one_qbit_gate(target_bit, trols,
OneQubitGate.sigx)
[docs] def write_cz(self, control_bit, target_bit, kind=True):
"""
Writes a simple singly controlled Z. If kind=True (resp. False),
cz fires when control is ``|1>`` (resp. ``|0>``).
Parameters
----------
control_bit : int
target_bit : int
kind : bool
Returns
-------
None
"""
num_qbits = self.emb.num_qbits_aft
trols = Controls.new_single_trol(num_qbits, control_bit, kind)
self.write_controlled_one_qbit_gate(target_bit, trols,
OneQubitGate.sigz)
[docs] def write_c_P1PH(self, control_bit, target_bit, rads=np.pi, kind=True):
"""
Writes a simple singly controlled P1PH. If kind=True (resp. False),
c_P1PH fires when control is ``|1>`` (resp. ``|0>``). When kind=
True and rads=p1, c_P1PH equals ``(-1)^{n(t)n(c)} = sigz(t)^{n(c)}``
where c is the control and t is the target. This is often called a
controlled Z, and denoted by Cz.
Parameters
----------
control_bit : int
target_bit : int
rads : float
kind : bool
Returns
-------
None
"""
num_qbits = self.emb.num_qbits_aft
trols = Controls.new_single_trol(num_qbits, control_bit, kind)
self.write_controlled_one_qbit_gate(target_bit, trols,
OneQubitGate.P_1_phase_fac, [rads])
[docs] def write_global_phase_fac(self, ang_rads):
"""
Write a line in eng & pic files for a global phase factor 'PHAS'
with no controls.
Parameters
----------
ang_rads : float
Returns
-------
None
"""
tar_bit_pos = 0 # anyone will do
trols = Controls(2) # dummy with zero controls
gate_fun = OneQubitGate.phase_fac
self.write_controlled_one_qbit_gate(
tar_bit_pos, trols, gate_fun, [ang_rads])
[docs] def write_multiplexor_gate(self, tar_bit_pos, controls, rad_angles):
"""
Write a line in eng & pic files for a multiplexor 'MP_Y' with
no T/F controls.
Parameters
----------
tar_bit_pos : int
controls : Controls
rad_angles : list[float]
Returns
-------
None
"""
num_controls = len(controls.bit_pos)
assert num_controls == controls.get_num_int_controls(),\
"some of the controls of this multiplexor are not half-moons"
self.write_controlled_multiplexor_gate(
tar_bit_pos, controls, rad_angles)
[docs] def write_diag_unitary_gate(self, controls, rad_angles):
"""
Write a line in eng & pic files for a diagonal unitary 'DIAG' with
no T/F controls.
Parameters
----------
controls : Controls
rad_angles : list[float]
Returns
-------
None
"""
num_controls = len(controls.bit_pos)
assert num_controls == controls.get_num_int_controls(),\
"some of the controls of this diagonal unitary are not half-moons"
self.write_controlled_diag_unitary_gate(controls, rad_angles)
if __name__ == "__main__":
def main():
num_qbits = 5
emb = CktEmbedder(num_qbits, num_qbits)
trols = Controls(num_qbits)
trols.bit_pos_to_kind = {3: True, 4: False}
trols.refresh_lists()
ang_rads = 30*np.pi/180
for ZL in [False, True]:
wr = SEO_writer('wr_test', emb, ZL=ZL)
wr.write_NOTA('zero bit last = ' + str(ZL))
wr.write_IF_M_beg(trols)
wr.write_IF_M_end()
wr.write_LOOP(10, 15)
wr.write_NEXT(10)
tar_bit_pos = 1
for kind in [0, 1, 2]:
wr.write_MEAS(tar_bit_pos, kind)
wr.write_PRINT('F2')
wr.write_controlled_qbit_swap(0, 2, trols)
wr.write_qbit_swap(1, 2)
gate = OneQubitGate.phase_fac
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads])
wr.write_global_phase_fac(30*np.pi/180)
gate = OneQubitGate.P_0_phase_fac
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads])
gate = OneQubitGate.P_1_phase_fac
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads])
gate = OneQubitGate.sigx
wr.write_controlled_one_qbit_gate(2, trols, gate)
gate = OneQubitGate.sigy
wr.write_controlled_one_qbit_gate(2, trols, gate)
gate = OneQubitGate.sigz
wr.write_controlled_one_qbit_gate(2, trols, gate)
gate = OneQubitGate.had2
wr.write_controlled_one_qbit_gate(2, trols, gate)
gate = OneQubitGate.rot_ax
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads, 1])
gate = OneQubitGate.rot_ax
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads, 2])
gate = OneQubitGate.rot_ax
wr.write_controlled_one_qbit_gate(2, trols, gate, [ang_rads, 3])
gate = OneQubitGate.rot
wr.write_controlled_one_qbit_gate(2, trols, gate,
[ang_rads/3, ang_rads*2/3, ang_rads])
gate = OneQubitGate.sigx
wr.write_one_qbit_gate(2, gate)
wr.write_cnot(2, 1)
tar_bit_pos = 0
trols1 = Controls(num_qbits)
trols1.bit_pos_to_kind = {1: 0, 2: 1, 3: True, 4: False}
trols1.refresh_lists()
wr.write_controlled_multiplexor_gate(tar_bit_pos, trols1,
[ang_rads/3, ang_rads*2/3, ang_rads, ang_rads*4/3])
trols2 = Controls(num_qbits)
trols2.bit_pos_to_kind = {1: 0, 2: 1}
trols2.refresh_lists()
wr.write_multiplexor_gate(tar_bit_pos, trols2,
[ang_rads/3, ang_rads*2/3, ang_rads, ang_rads*4/3])
wr.close_files()
main()