interferometer 1.0__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interferometer-1.0 → interferometer-1.1.1}/PKG-INFO +3 -1
- interferometer-1.1.1/interferometer/__init__.py +1 -0
- interferometer-1.1.1/interferometer/main.py +295 -0
- {interferometer-1.0 → interferometer-1.1.1}/interferometer.egg-info/PKG-INFO +3 -1
- {interferometer-1.0 → interferometer-1.1.1}/interferometer.egg-info/SOURCES.txt +4 -1
- {interferometer-1.0 → interferometer-1.1.1}/pyproject.toml +2 -2
- interferometer-1.1.1/tests/test_interferometer.py +17 -0
- {interferometer-1.0 → interferometer-1.1.1}/LICENSE +0 -0
- {interferometer-1.0 → interferometer-1.1.1}/README.md +0 -0
- {interferometer-1.0 → interferometer-1.1.1}/interferometer.egg-info/dependency_links.txt +0 -0
- {interferometer-1.0 → interferometer-1.1.1}/interferometer.egg-info/requires.txt +0 -0
- {interferometer-1.0 → interferometer-1.1.1}/interferometer.egg-info/top_level.txt +0 -0
- {interferometer-1.0 → interferometer-1.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: interferometer
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Algorithms for universal interferometers
|
|
5
5
|
Author-email: "William R. Clements" <mail@william-clements.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/clementsw/interferometer
|
|
@@ -13,6 +13,8 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
13
13
|
Requires-Python: >=3.7
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: matplotlib
|
|
16
18
|
|
|
17
19
|
# Interferometer package
|
|
18
20
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from interferometer.main import Interferometer, Beamsplitter, triangle_decomposition, square_decomposition, random_unitary
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Beamsplitter:
|
|
5
|
+
"""This class defines a beam splitter
|
|
6
|
+
|
|
7
|
+
The matrix describing the mode transformation is:
|
|
8
|
+
|
|
9
|
+
e^{iphi}*cos(theta) -sin(theta)
|
|
10
|
+
e^{iphi}*sin(theta) cos(theta)
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
mode1 (int): the index of the first mode (the first mode is mode 1)
|
|
14
|
+
mode2 (int): the index of the second mode
|
|
15
|
+
theta (float): the beam splitter angle
|
|
16
|
+
phi (float): the beam splitter phase
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, mode1, mode2, theta, phi):
|
|
20
|
+
self.mode1 = mode1
|
|
21
|
+
self.mode2 = mode2
|
|
22
|
+
self.theta = theta
|
|
23
|
+
self.phi = phi
|
|
24
|
+
|
|
25
|
+
def __repr__(self):
|
|
26
|
+
repr = "\n Beam splitter between modes {} and {}: \n Theta angle: {:.2f} \n Phase: {:.2f}".format(
|
|
27
|
+
self.mode1,
|
|
28
|
+
self.mode2,
|
|
29
|
+
self.theta,
|
|
30
|
+
self.phi
|
|
31
|
+
)
|
|
32
|
+
return repr
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Interferometer:
|
|
36
|
+
"""This class defines an interferometer.
|
|
37
|
+
|
|
38
|
+
An interferometer contains an ordered list of variable beam splitters,
|
|
39
|
+
represented here by BS_list. For BS in BS_list, BS[0] and BS[1] correspond to the labels of the two modes
|
|
40
|
+
being interfered (which start at 1). The beam splitters implement the optical transformation defined in equation 1 of:
|
|
41
|
+
Clements, William R., et al. "Optimal design for universal multiport interferometers." Optica 3.12 (2016): 1460-1465.
|
|
42
|
+
This transformation is parametrized by BS[2] (theta) which determines the beam splitter reflectivity,
|
|
43
|
+
and by BS[3] (phi). The interferometer also contains a list of output phases described by output_phases.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self.BS_list = []
|
|
48
|
+
self.output_phases = []
|
|
49
|
+
|
|
50
|
+
def add_BS(self, BS):
|
|
51
|
+
"""Adds a beam splitter at the output of the current interferometer
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
BS (Beamsplitter): a Beamsplitter instance
|
|
55
|
+
"""
|
|
56
|
+
self.BS_list.append(BS)
|
|
57
|
+
|
|
58
|
+
def add_phase(self, mode, phase):
|
|
59
|
+
"""Use this to manually add a phase shift to a selected mode at the output of the interferometer
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
mode (int): the mode index. The first mode is mode 1
|
|
63
|
+
phase (float): the real-valued phase to add
|
|
64
|
+
"""
|
|
65
|
+
while mode > np.size(self.output_phases):
|
|
66
|
+
self.output_phases.append(0)
|
|
67
|
+
self.output_phases[mode-1] = phase
|
|
68
|
+
|
|
69
|
+
def count_modes(self):
|
|
70
|
+
"""Calculate number of modes involved in the transformation.
|
|
71
|
+
|
|
72
|
+
This is required for calculate_transformation and draw
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
the number of modes in the transformation
|
|
76
|
+
"""
|
|
77
|
+
highest_index = max([max([BS.mode1, BS.mode2]) for BS in self.BS_list])
|
|
78
|
+
return highest_index
|
|
79
|
+
|
|
80
|
+
def calculate_transformation(self):
|
|
81
|
+
"""Calculate unitary matrix describing the transformation implemented by the interferometer
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
complex-valued 2D numpy array representing the interferometer
|
|
85
|
+
"""
|
|
86
|
+
N = int(self.count_modes())
|
|
87
|
+
U = np.eye(N, dtype=np.complex_)
|
|
88
|
+
|
|
89
|
+
for BS in self.BS_list:
|
|
90
|
+
T = np.eye(N, dtype=np.complex_)
|
|
91
|
+
T[BS.mode1 - 1, BS.mode1 - 1] = np.exp(1j * BS.phi) * np.cos(BS.theta)
|
|
92
|
+
T[BS.mode1 - 1, BS.mode2 - 1] = -np.sin(BS.theta)
|
|
93
|
+
T[BS.mode2 - 1, BS.mode1 - 1] = np.exp(1j * BS.phi) * np.sin(BS.theta)
|
|
94
|
+
T[BS.mode2 - 1, BS.mode2 - 1] = np.cos(BS.theta)
|
|
95
|
+
U = np.matmul(T,U)
|
|
96
|
+
|
|
97
|
+
while np.size(self.output_phases) < N: # Autofill for users who don't want to bother with output phases
|
|
98
|
+
self.output_phases.append(0)
|
|
99
|
+
|
|
100
|
+
D = np.diag(np.exp([1j * phase for phase in self.output_phases]))
|
|
101
|
+
U = np.matmul(D,U)
|
|
102
|
+
return U
|
|
103
|
+
|
|
104
|
+
def draw(self, show_plot=True):
|
|
105
|
+
"""Use matplotlib to make a drawing of the interferometer
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
show_plot (bool): whether to show the generated plot
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
import matplotlib.pyplot as plt
|
|
112
|
+
plt.figure()
|
|
113
|
+
N = self.count_modes()
|
|
114
|
+
mode_tracker = np.zeros(N)
|
|
115
|
+
|
|
116
|
+
for ii in range(N):
|
|
117
|
+
plt.plot((-1, 0), (ii, ii), lw=1, color="blue")
|
|
118
|
+
|
|
119
|
+
for BS in self.BS_list:
|
|
120
|
+
x = np.max([mode_tracker[BS.mode1 - 1], mode_tracker[BS.mode2 - 1]])
|
|
121
|
+
plt.plot((x+0.3, x+1), (N - BS.mode1, N - BS.mode2), lw=1, color="blue")
|
|
122
|
+
plt.plot((x, x+0.3), (N - BS.mode1, N - BS.mode1), lw=1, color="blue")
|
|
123
|
+
plt.plot((x, x+0.3), (N - BS.mode2, N - BS.mode2), lw=1, color="blue")
|
|
124
|
+
plt.plot((x+0.3, x+1), (N - BS.mode2, N - BS.mode1), lw=1, color="blue")
|
|
125
|
+
plt.plot((x+0.4, x+0.9), (N - (BS.mode2 + BS.mode1)/2, N - (BS.mode2 + BS.mode1)/2), lw=1, color="blue")
|
|
126
|
+
reflectivity = "{:2f}".format(np.cos(BS.theta)**2)
|
|
127
|
+
plt.text(x+0.9, N + 0.05 - (BS.mode2 + BS.mode1)/2, reflectivity[0:3], color="green", fontsize=7)
|
|
128
|
+
|
|
129
|
+
plt.plot((x+0.15, x+0.15), (N+0.3-(BS.mode2 + BS.mode1)/2., N+0.7-(BS.mode2 + BS.mode1)/2.), lw=1, color="blue")
|
|
130
|
+
circle = plt.Circle((x+0.15, N+0.5-(BS.mode2 + BS.mode1)/2.), 0.1, fill=False)
|
|
131
|
+
plt.gca().add_patch(circle)
|
|
132
|
+
phase = "{:2f}".format(BS.phi)
|
|
133
|
+
if BS.phi > 0:
|
|
134
|
+
plt.text(x+0.2, N+0.7-(BS.mode2 + BS.mode1)/2., phase[0:3], color="red", fontsize=7)
|
|
135
|
+
else:
|
|
136
|
+
plt.text(x+0.2, N+0.7-(BS.mode2 + BS.mode1)/2., phase[0:4], color="red", fontsize=7)
|
|
137
|
+
if x > mode_tracker[BS.mode1-1]:
|
|
138
|
+
plt.plot((mode_tracker[BS.mode1-1], x), (N-BS.mode1, N-BS.mode1), lw=1, color="blue")
|
|
139
|
+
if x > mode_tracker[BS.mode2-1]:
|
|
140
|
+
plt.plot((mode_tracker[BS.mode2-1], x), (N-BS.mode2, N-BS.mode2), lw=1, color="blue")
|
|
141
|
+
mode_tracker[BS.mode1-1] = x+1
|
|
142
|
+
mode_tracker[BS.mode2-1] = x+1
|
|
143
|
+
|
|
144
|
+
max_x = np.max(mode_tracker)
|
|
145
|
+
for ii in range(N):
|
|
146
|
+
plt.plot((mode_tracker[ii], max_x+1), (N-ii-1, N-ii-1), lw=1, color="blue")
|
|
147
|
+
while np.size(self.output_phases) < N: # Autofill for users who don't want to bother with output phases
|
|
148
|
+
self.output_phases.append(0)
|
|
149
|
+
if self.output_phases[ii] != 0:
|
|
150
|
+
plt.plot((max_x+0.5, max_x+0.5), (N-ii-1.2, N-ii-0.8), lw=1, color="blue")
|
|
151
|
+
circle = plt.Circle((max_x+0.5, N-ii-1), 0.1, fill=False)
|
|
152
|
+
plt.gca().add_patch(circle)
|
|
153
|
+
phase = str(self.output_phases[ii])
|
|
154
|
+
if BS.phi > 0:
|
|
155
|
+
plt.text(max_x+0.6, N-ii-0.8, phase[0:3], color="red", fontsize=7)
|
|
156
|
+
else:
|
|
157
|
+
plt.text(max_x+0.6, N-ii-0.8, phase[0:4], color="red", fontsize=7)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
plt.text(max_x/2, -0.7, "green: BS reflectivity", color="green", fontsize=10)
|
|
161
|
+
plt.text(max_x/2, -1.4, "red: phase shift", color="red", fontsize=10)
|
|
162
|
+
plt.text(-1, N-0.3, "Light in", fontsize=10)
|
|
163
|
+
plt.text(max_x+0.5, N-0.3, "Light out", fontsize=10)
|
|
164
|
+
plt.gca().axes.set_ylim([-1.8, N+0.2])
|
|
165
|
+
plt.axis("off")
|
|
166
|
+
if show_plot:
|
|
167
|
+
plt.show()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def triangle_decomposition(U):
|
|
171
|
+
"""Returns a triangular mesh of beam splitters implementing matrix U
|
|
172
|
+
|
|
173
|
+
This code implements the decomposition algorithm in:
|
|
174
|
+
Reck, Michael, et al. "Experimental realization of any discrete unitary operator."
|
|
175
|
+
Physical review letters 73.1 (1994): 58.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
U (np.ndarray): complex-valued 2D numpy array representing the interferometer
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
an Interferometer instance
|
|
182
|
+
"""
|
|
183
|
+
I = Interferometer()
|
|
184
|
+
N = int(np.sqrt(U.size))
|
|
185
|
+
for ii in range(N-1):
|
|
186
|
+
for jj in range(N-1-ii):
|
|
187
|
+
modes = [N - jj - 1, N - jj]
|
|
188
|
+
theta = custom_arctan(U[ii, N - 1 - jj], U[ii, N - 2 - jj])
|
|
189
|
+
phi = -custom_angle(-U[ii, N - 1 - jj], U[ii, N - 2 - jj])
|
|
190
|
+
invT = np.eye(N, dtype=np.complex_)
|
|
191
|
+
invT[modes[0]-1, modes[0]-1] = np.exp(-1j * phi) * np.cos(theta)
|
|
192
|
+
invT[modes[0]-1, modes[1]-1] = np.exp(-1j * phi) * np.sin(theta)
|
|
193
|
+
invT[modes[1]-1, modes[0]-1] = -np.sin(theta)
|
|
194
|
+
invT[modes[1]-1, modes[1]-1] = np.cos(theta)
|
|
195
|
+
U = np.matmul(U, invT)
|
|
196
|
+
I.BS_list.append(Beamsplitter(modes[0], modes[1], theta, phi))
|
|
197
|
+
phases = np.diag(U)
|
|
198
|
+
I.output_phases = [np.angle(i) for i in phases]
|
|
199
|
+
return I
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def square_decomposition(U):
|
|
203
|
+
"""Returns a rectangular mesh of beam splitters implementing matrix U
|
|
204
|
+
|
|
205
|
+
This code implements the decomposition algorithm in:
|
|
206
|
+
Clements, William R., et al. "Optimal design for universal multiport interferometers."
|
|
207
|
+
Optica 3.12 (2016): 1460-1465.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
an Interferometer instance
|
|
211
|
+
"""
|
|
212
|
+
I = Interferometer()
|
|
213
|
+
N = int(np.sqrt(U.size))
|
|
214
|
+
left_T = []
|
|
215
|
+
for ii in range(N-1):
|
|
216
|
+
if np.mod(ii, 2) == 0:
|
|
217
|
+
for jj in range(ii+1):
|
|
218
|
+
modes = [ii - jj + 1, ii + 2 - jj]
|
|
219
|
+
theta = custom_arctan(U[N-1-jj, ii-jj], U[N-1-jj, ii-jj+1])
|
|
220
|
+
phi = custom_angle(U[N-1-jj, ii-jj], U[N-1-jj, ii-jj+1])
|
|
221
|
+
invT = np.eye(N, dtype=np.complex_)
|
|
222
|
+
invT[modes[0]-1, modes[0]-1] = np.exp(-1j * phi) * np.cos(theta)
|
|
223
|
+
invT[modes[0]-1, modes[1]-1] = np.exp(-1j * phi) * np.sin(theta)
|
|
224
|
+
invT[modes[1]-1, modes[0]-1] = -np.sin(theta)
|
|
225
|
+
invT[modes[1]-1, modes[1]-1] = np.cos(theta)
|
|
226
|
+
U = np.matmul(U, invT)
|
|
227
|
+
I.BS_list.append(Beamsplitter(modes[0], modes[1], theta, phi))
|
|
228
|
+
else:
|
|
229
|
+
for jj in range(ii+1):
|
|
230
|
+
modes = [N+jj-ii-1, N+jj-ii]
|
|
231
|
+
theta = custom_arctan(U[N+jj-ii-1, jj], U[N+jj-ii-2, jj])
|
|
232
|
+
phi = custom_angle(-U[N+jj-ii-1, jj], U[N+jj-ii-2, jj])
|
|
233
|
+
T = np.eye(N, dtype=np.complex_)
|
|
234
|
+
T[modes[0]-1, modes[0]-1] = np.exp(1j * phi) * np.cos(theta)
|
|
235
|
+
T[modes[0]-1, modes[1]-1] = -np.sin(theta)
|
|
236
|
+
T[modes[1]-1, modes[0]-1] = np.exp(1j * phi) * np.sin(theta)
|
|
237
|
+
T[modes[1]-1, modes[1]-1] = np.cos(theta)
|
|
238
|
+
U = np.matmul(T, U)
|
|
239
|
+
left_T.append(Beamsplitter(modes[0], modes[1], theta, phi))
|
|
240
|
+
|
|
241
|
+
for BS in np.flip(left_T, 0):
|
|
242
|
+
modes = [int(BS.mode1), int(BS.mode2)]
|
|
243
|
+
invT = np.eye(N, dtype=np.complex_)
|
|
244
|
+
invT[modes[0]-1, modes[0]-1] = np.exp(-1j * BS.phi) * np.cos(BS.theta)
|
|
245
|
+
invT[modes[0]-1, modes[1]-1] = np.exp(-1j * BS.phi) * np.sin(BS.theta)
|
|
246
|
+
invT[modes[1]-1, modes[0]-1] = -np.sin(BS.theta)
|
|
247
|
+
invT[modes[1]-1, modes[1]-1] = np.cos(BS.theta)
|
|
248
|
+
U = np.matmul(invT, U)
|
|
249
|
+
theta = custom_arctan(U[modes[1]-1, modes[0]-1], U[modes[1]-1, modes[1]-1])
|
|
250
|
+
phi = custom_angle(U[modes[1]-1, modes[0]-1], U[modes[1]-1, modes[1]-1])
|
|
251
|
+
invT[modes[0]-1, modes[0]-1] = np.exp(-1j * phi) * np.cos(theta)
|
|
252
|
+
invT[modes[0]-1, modes[1]-1] = np.exp(-1j * phi) * np.sin(theta)
|
|
253
|
+
invT[modes[1]-1, modes[0]-1] = -np.sin(theta)
|
|
254
|
+
invT[modes[1]-1, modes[1]-1] = np.cos(theta)
|
|
255
|
+
U = np.matmul(U, invT)
|
|
256
|
+
I.BS_list.append(Beamsplitter(modes[0], modes[1], theta, phi))
|
|
257
|
+
phases = np.diag(U)
|
|
258
|
+
I.output_phases = [np.angle(i) for i in phases]
|
|
259
|
+
return I
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def random_unitary(N):
|
|
263
|
+
"""Returns a random NxN unitary matrix
|
|
264
|
+
|
|
265
|
+
This code is inspired by Matlab code written by Toby Cubitt:
|
|
266
|
+
http://www.dr-qubit.org/matlab/randU.m
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
N (int): dimension of the NxN unitary matrix to generate
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
complex-valued 2D numpy array representing the interferometer
|
|
273
|
+
"""
|
|
274
|
+
X = np.zeros([N, N], dtype=np.complex_)
|
|
275
|
+
for ii in range(N):
|
|
276
|
+
for jj in range(N):
|
|
277
|
+
X[ii, jj] = (np.random.normal() + 1j * np.random.normal()) / np.sqrt(2)
|
|
278
|
+
|
|
279
|
+
q, r = np.linalg.qr(X)
|
|
280
|
+
r = np.diag(np.divide(np.diag(r), abs(np.diag(r))))
|
|
281
|
+
U = np.matmul(q, r)
|
|
282
|
+
|
|
283
|
+
return U
|
|
284
|
+
|
|
285
|
+
def custom_arctan(x1, x2):
|
|
286
|
+
if x2 != 0:
|
|
287
|
+
return np.arctan(abs(x1/x2))
|
|
288
|
+
else:
|
|
289
|
+
return np.pi/2
|
|
290
|
+
|
|
291
|
+
def custom_angle(x1, x2):
|
|
292
|
+
if x2 != 0:
|
|
293
|
+
return np.angle(x1/x2)
|
|
294
|
+
else:
|
|
295
|
+
return 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: interferometer
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Algorithms for universal interferometers
|
|
5
5
|
Author-email: "William R. Clements" <mail@william-clements.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/clementsw/interferometer
|
|
@@ -13,6 +13,8 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
13
13
|
Requires-Python: >=3.7
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: matplotlib
|
|
16
18
|
|
|
17
19
|
# Interferometer package
|
|
18
20
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
+
interferometer/__init__.py
|
|
5
|
+
interferometer/main.py
|
|
4
6
|
interferometer.egg-info/PKG-INFO
|
|
5
7
|
interferometer.egg-info/SOURCES.txt
|
|
6
8
|
interferometer.egg-info/dependency_links.txt
|
|
7
9
|
interferometer.egg-info/requires.txt
|
|
8
|
-
interferometer.egg-info/top_level.txt
|
|
10
|
+
interferometer.egg-info/top_level.txt
|
|
11
|
+
tests/test_interferometer.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "interferometer"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.1.1"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="William R. Clements", email="mail@william-clements.com" },
|
|
10
10
|
]
|
|
@@ -28,5 +28,5 @@ dependencies = [
|
|
|
28
28
|
"Bug Tracker" = "https://github.com/clementsw/interferometer/issues"
|
|
29
29
|
|
|
30
30
|
[tool.setuptools]
|
|
31
|
-
|
|
31
|
+
packages = ["interferometer"]
|
|
32
32
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from unittest import TestCase
|
|
3
|
+
|
|
4
|
+
from interferometer import random_unitary, triangle_decomposition, square_decomposition
|
|
5
|
+
|
|
6
|
+
class TestInterferometer(TestCase):
|
|
7
|
+
|
|
8
|
+
def test_triangle_interferometer(self):
|
|
9
|
+
U = random_unitary(5)
|
|
10
|
+
I = triangle_decomposition(U)
|
|
11
|
+
self.assertTrue(abs(np.max(I.calculate_transformation() - U)) < 1e-14)
|
|
12
|
+
|
|
13
|
+
def test_square_interferometer(self):
|
|
14
|
+
U = random_unitary(5)
|
|
15
|
+
I = square_decomposition(U)
|
|
16
|
+
self.assertTrue(abs(np.max(I.calculate_transformation() - U)) < 1e-14)
|
|
17
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|