-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOperations.py
More file actions
742 lines (642 loc) · 25.1 KB
/
Operations.py
File metadata and controls
742 lines (642 loc) · 25.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
from numpy import zeros, dot, savetxt, array, kron, pi, exp, conjugate
from numpy.linalg import eig
from numpy import genfromtxt
import matplotlib.pyplot as plt
import qutip as qt
import Gates as g
from numpy import trace, cos, sin, arcsin, random, sqrt
# from quantum_CSD_compiler.DiagUnitaryExpander import *
# from quantum_CSD_compiler.MultiplexorExpander import *
# from quantum_CSD_compiler.Tree import *
# from for_IBM_devices.Qubiter_to_IBMqasm2_5q import *
import numpy as np
import os
import glob
def checknormkraus(k, n):
"""
Makes sure that the kraus matrices are properly normalized and therefore preserve probability
:param k: A 3 dimensional array of shape(m,2^n,2^n). m is the number of kraus matrices
:param n: The number of qubits
:return: Should return identity
"""
out = zeros((pow(2, n), pow(2, n)), dtype=complex)
for x in range(len(k)):
out += dot(ctranspose(k[x]), k[x])
return out
def blockshaped(arr, nrows, ncols):
"""
Return an array of shape (n, nrows, ncols) where
n * nrows * ncols = arr.size
If arr is a 2D array, the returned array looks like n subblocks with
each subblock preserving the "physical" layout of arr.
"""
h, w = arr.shape
return (arr.reshape(h//nrows, nrows, -1, ncols)
.swapaxes(1,2)
.reshape(-1, nrows, ncols))
def unblockshaped(arr, h, w):
"""
Return an array of shape (h, w) where
h * w = arr.size
If arr is of shape (n, nrows, ncols), n sublocks of shape (nrows, ncols),
then the returned array preserves the "physical" layout of the sublocks.
"""
n, nrows, ncols = arr.shape
return (arr.reshape(h//nrows, -1, nrows, ncols)
.swapaxes(1,2)
.reshape(h, w))
def dec2base(n, base):
"""
Gets gives you a string in base less than 20
:param n: The number you have
:param base: The base you want
:return: The string containing giving the base m representation
"""
convertstring = "0123456789ABCDEF"
if n < base:
return convertstring[n]
else:
return dec2base(n // base, base) + convertstring[n % base]
def createlabel(q, n):
"""
Create a string of labels for making kraus matrices
:param q: Number of qubits also the length of the label string
:param n: Number of distinct kraus matrices for one qubit
:return: A list of labels
"""
# When using dec2base function make sure to pad the string with the right number of zeros e.g for base 3 dec2base
# gives 1 rather than 01 if we were dealing with 2 qubits.
# The number of kraus matrices or labels is n^q
label = []
for i in range(pow(n, q)):
label.append(dec2base(i, n))
# Next we make sure that each element in the label list has length the number of qubits if not add a zero
for x in range(len(label)):
if len(label[x]) < q:
label[x] = label[x].zfill(q)
else:
break
return label
def padded_dec2base(n, q, base):
"""
Gets gives you a string in base less than 20
:param n: The number you have
:param q: The number of qubits in system or determines the number of zeros to pad i.e
if n=3 and q=3 the function will return 011 rather than 11
:param base: The base you want
:return: The string containing giving the base m representation
"""
convertstring = "0123456789ABCDEF"
if n < base:
return convertstring[n].zfill(q)
else:
return (dec2base(n // base, base) + convertstring[n % base]).zfill(q)
def ctranspose(A):
"""
:param A: Matrix A
:return: Conjugate transpose of A
"""
out = A.conjugate().transpose()
return out
def frange(start, end, step):
tmp = start
while tmp < end:
yield tmp
tmp += step
def write_to_file(name, *args):
"""
:param name: A string that is the name of the file to which the data will be written to
:param args: list of arguments with entries being data e.g arg[0] = time where time is a list of times
:return:
"""
data = array(args).T
file = open(name, 'w+')
savetxt(name, data, fmt=['%.5f', '%.5f'])
file.close()
def load_data(f='', use_cols=[], xlabel='', ylabel='',scatter=False,contour=False, connect=False,
errorbar=False, std_col=None, data_domain=None, labo=[], title='',
return_data=False, graph=False, multiple_files=None, file_stem='', folder=None,
file_list = None, color_list=None, combine=False):
"""
This function has two uses, it either loads the data and plots it if val=1 or it just gets the data from
the data file and puts it into arrays which it returns
:param f: String varible containing name of the file
:param scatter: Boolean variable , make True if scatter plot is desired
:param connect: Boolean variable, make True if connected plot is desired
:param errorbar: Boolean variable, make True if verticle error bars are desired
:param std_col: Should be integer. Specifies which column in file will represent
the errorbars
:param contour: Boolean variable, make True if contour plot is desired
:param use_cols: Which columns in the file do you want to work with. Must
be a list
:param data_domain: This is filled in when the x axis data is not part of
the file i.e defined externally
:param labo : This is the label for the graph
:param title: The title of the graph
:param graph: Boolean expression, make True if you want to graph data.
:param return_data: Boolean expression, make True if you want to simply
return specific columns from file. Columns will be in a list
:param xlabel: xlabel for the graph
:param ylabel: ylabel for the graph
:param multiple_files: Boolean expression, make True if data is going to
be retrieved from many data files in a folder
:param file_stem: Used if multiples_files is True, contains a string
that the set of files all have
:param folder: Folder which contains the files. Used if multiple files
is True
:param color_list: List of colors to use for graphs
:param combine: Boolean expression, if true data from data files is combined
:return:
"""
def combine_data(data_files_dict):
"""
This combines data from different files so that the first column of file 1 is concatenated with first column
oof file 2 and so on
:param data_files_dict:
:return:
"""
key_list = list(data_files_dict.keys())
no_col = len(data_files_dict[key_list[0]])
combined = []
for n in range(0, no_col):
d = np.empty(shape=[0, 1])
for k in data_files_dict:
d = np.append(d, data_files_dict[k][n])
combined.append(d)
return combined
def data_graph(graph=False):
"""
Gets data from folder or list of files or file and graphs it in
some manner
:param graph:
:return:
"""
def axes_data(use_cols1, data1, domain=None):
if domain is not None:
axis = [0] * (2 * len(use_cols1))
for k in range(len(use_cols1)):
axis[2 * k] = domain
axis[2 * k + 1] = data1[k]
return axis
else:
axis = [data1[k] for k in use_cols1]
return axis
if graph:
axes = axes_data(use_cols, data, domain=data_domain)
if scatter:
for i in range(int(len(axes)/2)):
if i % 2 == 0:
plt.scatter(axes[2*i], axes[2*i+1], color_list[i],
label=labo[i])
else:
plt.scatter(axes[2 * i], axes[2 * i + 1], color_list[i]
,label=labo[i])
if connect:
for i in range(int(len(axes)/2)):
if i % 2 == 0:
plt.plot(axes[2*i], axes[2*i+1], color_list[i],
label=labo[i])
else:
plt.plot(axes[2 * i], axes[2 * i + 1], color_list[i]
, label=labo[i])
if errorbar:
plt.errorbar(*axes, yerr=data[std_col], fmt='o', label=labo)
if contour:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
fig = plt.figure()
ax = fig.gca(projection='3d')
z = axes[2][:100]
len_z = len(z)
n = np.sqrt(len_z)
x = np.arange(0, n)
y = x
x, y = np.meshgrid(x,y)
two_dz = z.reshape((int(n), int(n)))
surf = ax.plot_surface(x,y,two_dz,cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(0, 0.18)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
# heat_map = plt.pcolor(two_dz)
# heat_color = plt.colorbar(heat_map, orientation = 'horizontal')
# heat_color.set_label('Average Fidelity')
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.title(title)
plt.legend()
plt.show()
if multiple_files and folder is not None:
os.chdir(folder)
files = sorted(glob.glob(file_stem))
for g in files:
data = genfromtxt(g, dtype=float, unpack=True)
data_graph(graph)
elif multiple_files and file_list:
if combine:
file_dictionary = {g: genfromtxt(g, dtype=float, unpack=True) for g in file_list}
data = combine_data(file_dictionary)
data_graph(graph)
else:
for g in file_list:
data = genfromtxt(g, dtype=float, unpack=True)
data_graph(graph)
else:
data = genfromtxt(f, dtype=float, unpack=True)
data_graph(graph)
if return_data:
retrieved = [data[k] for k in use_cols]
return retrieved
def generateDictKeys(string, n,step=1):
"""
This was written as a quick and easy way of producing keys for a dictionary.
:param string: Base String on which to attach numerals
:param n: Number of strings to generate with '{}{}'.format(string,n) being the last string generated
:return: yields strings like "a1" or "a2" if string= 'a' and n>2
"""
if type(string) != str or type(n) != int:
raise ValueError('Please input string and integer for first and second argument')
elif step == 1:
keylist = [string+str(i) for i in range(n)]
return keylist
else:
keylist = [string+str(i) for i in range(0, n*step, step)]
return keylist
def generatehamiltoniantring(n, s, onestring=None, pos=None, pad=None):
"""
This generates a string that is used to construct tensor products terms in Hamiltonian with a certain kind of
interaction term. For example suppose we have an ising Hamiltonian with 4 qubits. We generate strings in a
list 1100, 0110,0011. The zero will be used to tensor an identity and the 1 will be used to tensor the pauli
matrix.
:param n: The number of qubits we have in our system
:param s: This is a string of 1's that determines how local our hamiltonian is. So a three body interaction
means we need '111' or a two body interaction will be '11'
:param onestring: If you would rather generate one string than a list of strings
:param pos: The position where the string s should start (only if onestring is not none)
:param pad: Rather than padding with zeros you could pad with some other number (only if onestring
is not none)
:return: Returns a list of strings that will be used to create the Hamiltonian
"""
label = []
if onestring is None:
if isinstance(s, str):
for i in range(0, n):
strs = s
strs = strs.ljust(n-i, '0')
strs = strs.rjust(n, '0')
label.append(strs)
else:
print('Please enter string for second variable and integer for first')
return label
else:
strs = s
strs = strs.ljust(n - pos, pad)
strs = strs.rjust(n, pad)
return strs
def controlgatestring(n, control_pos1, target_pos2, additionaloptstring = ''):
"""
:param n: The length of the string
:param control_pos1: This must be a tuple that specifies which qubit is control and its position
(string,number)
:param target_pos2: This must be a tuple that specifies which qubit is target and its position
:param additionaloptstring: Gives the user freedom to add string unique string after second position
:return:
"""
out = ''
if isinstance(control_pos1, tuple) and isinstance(target_pos2, tuple):
control, pos1 = control_pos1
target, pos2 = target_pos2
for i in range(0, n):
if i == pos1-1:
out += control
elif i == pos2-1:
out += target
elif additionaloptstring != '' and i > pos2-1:
out += additionaloptstring
else:
out += '0'
else:
print("Please make sure that you have provided a tuple with the 1st arg being a string and 2nd being a number")
return out
def generatetensorstring(n, *args):
"""
This is a function like generatehamiltonianstring except it allows for the possibility that we could have
different operators in the interaction placed at arbitrary place. Plus this does not generate a list
but just one string. IZIIXIY needs string '0100203'. Can't handle a term like 'IZZZ' because we need
string '0111' because for each operator other than identity label is incremented.
:param n: The number of qubits in the system
:param args: list of arguments stating positions for where numbers other than 0 should go.
:return: returns a string
"""
out = ''
label = 0
arg = array(args) - 1
for i in range(0, n):
if i in arg:
label += 1
out += str(label)
else:
out += '0'
return out
def partial_trace(n, m, k):
"""
This is the partial trace operator
:param n: Number of qubits in the system
:param m: The position of the qubit to trace
:param k: label for the kth basis for the traced out qubit, label begins from 1
:return: Returns the matrix that helps trace out the mth qubit
"""
out = 1
tensor_label = generatetensorstring(n, m)
terms = {"0": g.id(), "1": g.e_ij((2, 1), k, 1)}
out = superkron(terms, string=tensor_label, val=1)
return out
def trace_qubits(n, rho, qubit_list):
"""
:param n: Number of qubits in the system
:param rho: The system from which trace out qubits
:param qubit_list: List of qubits to trace out. Labels should begin from 1
:return: Returns a reduced density matrix
"""
basis_labels = [1, 2]
ops ={}
red_dim = n - len(qubit_list)
reduced_matrix = zeros((pow(2, red_dim), pow(2, red_dim)))
# Initialize the tracing operator dictionary
for q in qubit_list:
ops[q] = []
# Put tracing out operators in dictionary
for q in qubit_list:
for b in basis_labels:
ops[q].append(partial_trace(n, q, b))
# Perform the tracing out operation
for q in ops:
for b in range(0, len(ops[q])):
reduced_matrix += dot(ops[q][b].T, dot(rho, ops[q][b]))
return reduced_matrix
def subblock(u, p1, p2):
"""
:param u: In put matrix
:param p1: this is a tuple that determines the top left element of sub-block
:param p2: this is a tuple that determines the bottom right element of sub-block
:return: Returns the sub-block
"""
if isinstance(p1, tuple) and isinstance(p2, tuple):
r, c = p1
r1, c1 = p2
out = u[r:r1, c:c1]
else:
print("Please enter tuple for second and third argument")
return out
def generateUnitary():
"""
:return: Returns a random 2 by 2 unitary matrix
"""
u = zeros((2,2), dtype=complex)
zeta = random.random() # result after calculating sine and cosine
theta = arcsin(zeta)
phi = random.uniform(0, 2*pi) # angle for first phase
chi = random.uniform(0, 2*pi) # angle for second phase
rho = random.uniform(0, 2*pi)
a = exp(1j*phi)*cos(theta)
b = exp(1j*chi)*sin(theta)
u[0, 0] = a
u[0, 1] = b
u[1, 0] = -conjugate(b)
u[1, 1] = conjugate(a)
return dot(exp(1j*rho), u)
def gram_schmidt(A):
"""
:param A: Matrix that does not have orthornormal column
:param row_vecs:
:param norm:
:return:
"""
for i in range(0, len(A)):
for j in range(0, i):
A[:, i] = A[:, i] - (dot(A[:, i].T, A[:, j])) / (dot(A[:, j].T, A[:, j]))*A[:, j]
A[:, i] = A[:, i] / sqrt(dot(A[:, i].T, A[:, i]))
return A
def superkron(*args, val=0, string=''):
"""
This generalization of kron can be used in 2 ways. It can straight forwardly take the tensor product of
operators given the arguments i.e superkron(I,Z,X) will return the tensor product of I, Z and X.
It can also do something more general. It can accept a dictionary of operators and a string variable
that specifies in which order the operators in the dictionary should be tensored. E.G
operdict = {'0': I, '1': X} and string = '010. Then superkron(operdict, val=1,string)
produces tensor product superkron(I,X,I)
:param args: List of operators to calculate tensor product of
:param val: A val=0 makes function calculate tensor product of operators given, val=1 adds extra
bells and whistles described in doc string above
:param string: The order in which operators given in the operdict dictionary will be tensored
:return: The tensor product of operators
"""
out = 1
if val == 0:
for i in range(len(args)):
out = kron(out, args[i])
else:
for digit in string:
out = kron(out, args[0][digit])
return out
def computational_basis(q, n, dict=False):
"""
:param n: type of qudit. Set n=2 for qubit
:param q: Number of qubits
:return: List of one dimensional arrays corresponding to the computational basis states
"""
zero = np.array([1, 0])
one = np.array([0, 1])
basis_labels = createlabel(q, n)
operators = {'0': zero, '1': one}
basis_list = [np.array([superkron(operators, val=1, string=i)])for i in basis_labels]
return basis_list
def realify(vec):
"""
Gets a complex vector in C^d and produces a real vector in R^2d
:param vec: This should be a row vector
:return:
"""
realified = []
a = vec.tolist()
for i in a[0]:
real_part = i.real
realified.append(real_part)
for i in a[0]:
imag_part = i.imag
realified.append(imag_part)
realified = np.array(realified)
return realified
def construct_preunitary(arr, array_list):
"""
Useful when constructing unitaries to make quantum states. It needs to be
input into gram schmidt orthogonalization method
:param arr:
:param array_list:
:return:
"""
s = array_list[1].shape
preunitary = np.array([])
preunitary = np.append(preunitary, arr)
preunitary = preunitary.reshape(1, s[1])
for i in range(1, len(array_list)):
preunitary = np.hstack((preunitary, array_list[i]))
preunitary = preunitary.reshape(s[1], s[1]).T
return preunitary
def makeQobj(*args):
"""
:param args: This is a list of matrices than must be turned into Qobj in order that they can be used in the
qutip package
:return: returns list of Qobj in the order in which they were created. E.g makeQobj(A,B) returns a list
l = [Qobj(A),Qobj(B)]
"""
try:
if isinstance(*args, list):
l = [qt.Qobj(args[0][i]) for i in range(len(args[0]))]
elif isinstance(*args, dict):
l = {name: qt.Qobj(args[0][name])for name in args[0]}
else:
l = qt.Qobj(args[0])
return l
except TypeError:
print("The input must be a list or dictionary of operators or a single operator")
def direct_sum(matrices):
"""
:param matrices: List of matrices
:return: The direct sum of the matrices
"""
temp = []
for m in matrices:
temp.append(m.shape)
M = zeros(tuple(map(sum, zip(*temp))), dtype=complex)
M[:matrices[0].shape[0], :matrices[0].shape[1]] = matrices[0]
for l in range(0, len(matrices), 2):
if l != len(matrices)-1:
M[matrices[l].shape[0]:matrices[l].shape[0] + matrices[l+1].shape[0],
matrices[l].shape[1]:matrices[l].shape[1] + matrices[l+1].shape[1]] = matrices[l+1]
else:
M[M.shape[0]-matrices[l].shape[0]:, M.shape[1]-matrices[l].shape[1]:] = matrices[l]
return M
def fidelity(rho, rho_1, error=False):
"""
:param rho: First density matrix
:param rho_1: Second density matrix
:param error: If true returns 1 - fidelity otherwise simply returns fidelity
:return: Return the fidelity of two matrices in variable out
"""
out = 0
if error is False:
out = trace(dot(rho, rho_1)).real
else:
out = 1 - trace(dot(rho, rho_1)).real
return out
#
# def gatecompiler(file_prefix, unitary, num_bits):
# """
# :param file_prefix:
# :param unitary
# :param num_bits:
# :return:
# """
#
# emb = CktEmbedder(num_bits, num_bits)
# t = Tree(True, file_prefix, emb, unitary, verbose=False)
# t.close_files()
# style = 'exact'
# MultiplexorExpander(file_prefix, num_bits, style)
# DiagUnitaryExpander(file_prefix + '_X1', num_bits, style)
# SEO_reader(file_prefix + '_X2', num_bits)
def eigen(A):
"""
:param A: This is matrix
:return: Returns the eigenvalues with corresponding eigen vectors
"""
v, w = eig(A)
shape_size = w.shape
eigenvector_list = [array([w[:, i]]) for i in range(0, shape_size[1])]
return v, eigenvector_list
def read_file(file, line_count=1, readline=None):
"""
:param file: file name
:param line_count: The number of lines in the file
Note first line is indexed as 0
:param readline: The line to read
line by line
:return:
"""
with open(file, 'r') as f:
if readline is None:
line = f.read()
return line
else:
for i, lines in enumerate(f):
if i == readline-1:
return lines
def file_linecount(file):
"""
:param file: file name
:return: Number of lines in a file
"""
linecount = sum(1 for line in open(file))
return linecount
def remove_line(file, string=[]):
"""
Removes specific words from files and returns the file with same name but bad lines
are removed
:param file:
:param string:
:return:
"""
with open(file, 'r') as f, open('tmp.txt', '+a') as new_f:
for line in f:
clean = True
for word in string:
if word in line:
clean = False
if clean is True:
new_f.write(line)
os.remove(file)
os.rename('tmp.txt', file)
# def unitary_to_qasm(file_prefix1, u, n, unwanted_words):
# """
# :param file_prefix1:
# :param u:
# :param n:
# :param unwanted_words: remove lines which have unwanted words
# :return: name of file with qasm code
# """
# gatecompiler(file_prefix1, u, n)
# remove_line(file_prefix1 + '_X2_' + str(n) + '_eng.txt', unwanted_words)
# Qubiter_to_IBMqasm2_5q(file_prefix1 + '_X2', n, write_qubiter_files=True)
# return file_prefix1 + '_X2_' + 'qasm2.txt'
def save_to_file(name='', **kwargs):
"""
Write data to file in column format. Each keyword is a column
:param name: A string that is the name of the file to which the data will be written to
"""
string = ''
for k in kwargs:
string += '{' + k + '}' + ' '
string += '\n'
file = open(name, 'a')
file.write(string.format(**kwargs))
file.close()
if __name__ == '__main__':
# a = read_file('/home/amara/Dropbox/Python Files/BQP2.5/projects/qvector_files/zero_state_ibm.qasm')
# print(a)
# unitary_to_qasm('/home/amara/Dropbox/Python Files/qComputing/Gate Compiler Files/x',
# g.cnot(),2, 'PHAS')
# file = '/home/amara/Dropbox/Python Files/Data/Qvector/average_fidelity/average_fidelity_5_17.txt'
# load_data(f=file, use_cols=[0, 1, 2], graph=True, contour=True, xlabel="units of $\pi/10$",
# ylabel='units of $\pi/10$')
# print('List of computational basis state: ', computational_basis(3, 2))
# print('Labels for classical states: ', createlabel(3, 2))
from SpecialStates import ghz_state, clusterstate
ghz = ghz_state(3)
cluster = clusterstate(4)
reduce_ghz = trace_qubits(3, ghz.state, [1, 2])
print(reduce_ghz)