waveforms 2.2.0__tar.gz → 2.2.2__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.
- {waveforms-2.2.0 → waveforms-2.2.2}/PKG-INFO +1 -1
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/_waveform.pyi +11 -6
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/_waveform.pyx +17 -3
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/distortion.py +39 -25
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/multy_drag.py +0 -16
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/version.py +1 -1
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/waveform.py +136 -77
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/waveform_parser.py +1 -1
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/PKG-INFO +1 -1
- {waveforms-2.2.0 → waveforms-2.2.2}/LICENSE +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/MANIFEST.in +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/README.md +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/pyproject.toml +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/setup.cfg +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/setup.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/src/waveform.h +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_multi_drag.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_waveform.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_wavevstack.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformLexer.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformListener.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformParser.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/__init__.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/__main__.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/utils.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/SOURCES.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/entry_points.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/requires.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@ from typing import Callable
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from numpy import e, inf, pi
|
|
5
|
+
from numpy.typing import NDArray
|
|
5
6
|
|
|
6
7
|
NDIGITS: int = ...
|
|
7
8
|
__TypeIndex: int = ...
|
|
@@ -48,12 +49,15 @@ def pow(x: tuple[tuple, tuple], n: int) -> tuple[tuple, tuple]:
|
|
|
48
49
|
pass
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def calc_parts(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
def calc_parts(
|
|
53
|
+
bounds: tuple,
|
|
54
|
+
seq: tuple,
|
|
55
|
+
x: np.ndarray,
|
|
56
|
+
function_lib: dict,
|
|
57
|
+
min=-inf,
|
|
58
|
+
max=inf
|
|
59
|
+
) -> tuple[list[tuple[int, int, NDArray[np.float64 | np.complex128] | int
|
|
60
|
+
| float | complex]], type]:
|
|
57
61
|
pass
|
|
58
62
|
|
|
59
63
|
|
|
@@ -104,6 +108,7 @@ COSH: int = ...
|
|
|
104
108
|
SINH: int = ...
|
|
105
109
|
DRAG: int = ...
|
|
106
110
|
MOLLIFIER: int = ...
|
|
111
|
+
D_GAUSSIAN: int = ...
|
|
107
112
|
|
|
108
113
|
|
|
109
114
|
def simplify(expr: tuple[tuple, tuple], eps: float) -> tuple[tuple, tuple]:
|
|
@@ -295,6 +295,11 @@ def _GAUSSIAN(t, std_sq2):
|
|
|
295
295
|
return np.exp(-(t / std_sq2)**2)
|
|
296
296
|
|
|
297
297
|
|
|
298
|
+
def _D_GAUSSIAN(t, std_sq2, n):
|
|
299
|
+
return (-1)**n / std_sq2**n * special.hermite(n)(
|
|
300
|
+
t / std_sq2) * np.exp(-(t / std_sq2)**2)
|
|
301
|
+
|
|
302
|
+
|
|
298
303
|
def _ERF(t, std_sq2):
|
|
299
304
|
return special.erf(t / std_sq2)
|
|
300
305
|
|
|
@@ -353,14 +358,17 @@ def _drag(t: np.ndarray, t0: float, freq: float, width: float, delta: float,
|
|
|
353
358
|
|
|
354
359
|
def _mollifier(t: np.ndarray, r: float, d: int):
|
|
355
360
|
x = t / r
|
|
361
|
+
xx_1 = np.abs(x)**2 - 1
|
|
356
362
|
if d == 0:
|
|
357
|
-
return np.
|
|
363
|
+
return np.where(xx_1 == 0, 0, np.exp(1 / xx_1 + 1))
|
|
358
364
|
else:
|
|
359
365
|
p = np.poly1d([-2, 0])
|
|
360
366
|
for n in range(1, d):
|
|
361
367
|
p = np.poly1d([1, 0, -2, 0, 1]) * p.deriv() + np.poly1d(
|
|
362
368
|
[-4 * n, 0, 4 * n - 2, 0]) * p
|
|
363
|
-
return np.
|
|
369
|
+
return np.where(xx_1 == 0, 0,
|
|
370
|
+
np.exp(1 / xx_1 + 1) /
|
|
371
|
+
(-xx_1)**(2 * d)) * p(x) / r**d
|
|
364
372
|
|
|
365
373
|
|
|
366
374
|
LINEAR = registerBaseFunc(_LINEAR)
|
|
@@ -377,6 +385,7 @@ COSH = registerBaseFunc(_COSH)
|
|
|
377
385
|
SINH = registerBaseFunc(_SINH)
|
|
378
386
|
DRAG = registerBaseFunc(_drag)
|
|
379
387
|
MOLLIFIER = registerBaseFunc(_mollifier)
|
|
388
|
+
D_GAUSSIAN = registerBaseFunc(_D_GAUSSIAN)
|
|
380
389
|
|
|
381
390
|
|
|
382
391
|
def _d_LINEAR(shift, *args):
|
|
@@ -447,7 +456,11 @@ def _d_HYPERBOLICCHIRP(shift, f0, k, phi0):
|
|
|
447
456
|
|
|
448
457
|
|
|
449
458
|
def _d_MOLLIFIER(shift, r, d):
|
|
450
|
-
return (((((MOLLIFIER, r, d+1, shift), ), (1, )), ), (1, ))
|
|
459
|
+
return (((((MOLLIFIER, r, d + 1, shift), ), (1, )), ), (1, ))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _d_D_GAUSSIAN(shift, std_sq2, n):
|
|
463
|
+
return (((((D_GAUSSIAN, std_sq2, n + 1, shift), ), (1, )), ), (1, ))
|
|
451
464
|
|
|
452
465
|
|
|
453
466
|
# register derivative
|
|
@@ -464,6 +477,7 @@ registerDerivative(LINEARCHIRP, _d_LINEARCHIRP)
|
|
|
464
477
|
registerDerivative(EXPONENTIALCHIRP, _d_EXPONENTIALCHIRP)
|
|
465
478
|
registerDerivative(HYPERBOLICCHIRP, _d_HYPERBOLICCHIRP)
|
|
466
479
|
registerDerivative(MOLLIFIER, _d_MOLLIFIER)
|
|
480
|
+
registerDerivative(D_GAUSSIAN, _d_D_GAUSSIAN)
|
|
467
481
|
|
|
468
482
|
|
|
469
483
|
def _cos_power_n(x, n):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import warnings
|
|
2
|
-
from itertools import
|
|
3
|
-
from typing import Sequence
|
|
2
|
+
from itertools import zip_longest
|
|
3
|
+
from typing import Sequence, cast
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
+
from numpy.typing import NDArray
|
|
6
7
|
from scipy.fftpack import fft, fftfreq, ifft, ifftshift
|
|
7
8
|
from scipy.optimize import curve_fit
|
|
8
9
|
from scipy.signal import fftconvolve, lfilter, lfiltic, tf2zpk, zpk2sos, zpk2tf
|
|
@@ -55,7 +56,7 @@ def zDistortKernel(dt: float, params: Sequence[tuple]) -> np.ndarray:
|
|
|
55
56
|
for tau, A in params:
|
|
56
57
|
H += (1j * A * omega * tau) / (1j * omega * tau + 1)
|
|
57
58
|
|
|
58
|
-
ker = ifftshift(ifft(1 / H)).real
|
|
59
|
+
ker = cast(NDArray[np.complex128], ifftshift(ifft(1 / H))).real
|
|
59
60
|
return ker
|
|
60
61
|
|
|
61
62
|
|
|
@@ -98,11 +99,14 @@ def exp_decay_filter_old(amp, tau, sample_rate):
|
|
|
98
99
|
return b, a
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
def exp_decay_filter(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
def exp_decay_filter(
|
|
103
|
+
amp: float | Sequence[float],
|
|
104
|
+
tau: float | Sequence[float],
|
|
105
|
+
sample_rate: float,
|
|
106
|
+
inv: bool = False,
|
|
107
|
+
output='ba'
|
|
108
|
+
) -> NDArray[np.float64] | tuple[NDArray[np.float64], NDArray[
|
|
109
|
+
np.float64]] | tuple[NDArray[np.float64], NDArray[np.float64], float]:
|
|
106
110
|
"""
|
|
107
111
|
exp decay filter
|
|
108
112
|
|
|
@@ -137,7 +141,9 @@ def exp_decay_filter(amp: float | Sequence[float],
|
|
|
137
141
|
|
|
138
142
|
if isinstance(amp, (int, float, complex)):
|
|
139
143
|
amp = [amp]
|
|
140
|
-
tau = [tau]
|
|
144
|
+
tau = [cast(float, tau)]
|
|
145
|
+
amp = cast(Sequence[float], amp)
|
|
146
|
+
tau = cast(Sequence[float], tau)
|
|
141
147
|
numerator, denominator = np.poly1d([0.0]), np.poly1d([1.0])
|
|
142
148
|
for i, (A, t) in enumerate(zip(amp, tau)):
|
|
143
149
|
denominator = denominator * np.poly1d([1, -1 / t])
|
|
@@ -148,18 +154,22 @@ def exp_decay_filter(amp: float | Sequence[float],
|
|
|
148
154
|
numerator = numerator + n
|
|
149
155
|
numerator = numerator + denominator
|
|
150
156
|
|
|
151
|
-
z = np.exp(-numerator.roots / sample_rate)
|
|
152
|
-
p = np.exp(-denominator.roots / sample_rate)
|
|
157
|
+
z = cast(NDArray[np.float64], np.exp(-numerator.roots / sample_rate))
|
|
158
|
+
p = cast(NDArray[np.float64], np.exp(-denominator.roots / sample_rate))
|
|
153
159
|
if inv:
|
|
154
160
|
z, p = p, z
|
|
155
|
-
k =
|
|
161
|
+
k = cast(float,
|
|
162
|
+
numerator(0) / denominator(0) * np.prod(1 - p) / np.prod(1 - z))
|
|
156
163
|
|
|
157
164
|
if output == 'sos':
|
|
158
|
-
return zpk2sos(z, p, k)
|
|
165
|
+
return cast(NDArray[np.float64], zpk2sos(z, p, k))
|
|
159
166
|
elif output == 'ba':
|
|
160
|
-
return
|
|
167
|
+
return cast(tuple[NDArray[np.float64], NDArray[np.float64]],
|
|
168
|
+
zpk2tf(z, p, k))
|
|
161
169
|
elif output == 'zpk':
|
|
162
170
|
return z, p, k
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError(f"Invalid output type: {output}")
|
|
163
173
|
|
|
164
174
|
|
|
165
175
|
def reflection_filter(f, A, tau):
|
|
@@ -251,7 +261,8 @@ def stable_filter(exp_decay_filters: list, sample_rate: float):
|
|
|
251
261
|
"""
|
|
252
262
|
filters = []
|
|
253
263
|
for amp, tau in exp_decay_filters:
|
|
254
|
-
a, b =
|
|
264
|
+
a, b = cast(tuple[NDArray[np.float64], NDArray[np.float64]],
|
|
265
|
+
exp_decay_filter(amp, tau, sample_rate))
|
|
255
266
|
filters.append((b, a))
|
|
256
267
|
|
|
257
268
|
b, a = combine_filters(filters)
|
|
@@ -262,14 +273,15 @@ def stable_filter(exp_decay_filters: list, sample_rate: float):
|
|
|
262
273
|
return False
|
|
263
274
|
|
|
264
275
|
|
|
265
|
-
def predistort(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
276
|
+
def predistort(
|
|
277
|
+
sig: np.ndarray,
|
|
278
|
+
filters: list | None = None,
|
|
279
|
+
ker: np.ndarray | None = None,
|
|
280
|
+
initial: float = 0.0,
|
|
281
|
+
initial_x: np.ndarray | None = None,
|
|
282
|
+
initial_y: np.ndarray | None = None,
|
|
283
|
+
zi: np.ndarray | None = None,
|
|
284
|
+
return_zf: bool = False) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
|
|
273
285
|
if filters is not None:
|
|
274
286
|
b, a = combine_filters(filters)
|
|
275
287
|
z, p, k = tf2zpk(b, a)
|
|
@@ -315,7 +327,8 @@ def predistort(sig: np.ndarray,
|
|
|
315
327
|
def distort(points, params, sample_rate, initial=0.0):
|
|
316
328
|
filters = []
|
|
317
329
|
for amp, tau in np.asarray(params).reshape(-1, 2):
|
|
318
|
-
b, a =
|
|
330
|
+
b, a = cast(tuple[NDArray[np.float64], NDArray[np.float64]],
|
|
331
|
+
exp_decay_filter(amp, abs(tau), sample_rate))
|
|
319
332
|
filters.append((b, a))
|
|
320
333
|
return predistort(points, filters, initial=initial)
|
|
321
334
|
|
|
@@ -342,6 +355,7 @@ def phase_curve(t, params, df_dphi, pulse_width, start, wav, sample_rate):
|
|
|
342
355
|
|
|
343
356
|
if __name__ == '__main__':
|
|
344
357
|
import matplotlib.pyplot as plt
|
|
358
|
+
|
|
345
359
|
from waveforms import square
|
|
346
360
|
|
|
347
361
|
data = np.load('Z_distortion.npz')
|
|
@@ -354,7 +368,7 @@ if __name__ == '__main__':
|
|
|
354
368
|
wav = 0.1 * (square(2e-6) << 1e-6)
|
|
355
369
|
|
|
356
370
|
def f(t, *params):
|
|
357
|
-
return phase_curve(t, params, df_dphi, 10e-9, 25e-9)
|
|
371
|
+
return phase_curve(t, params, df_dphi, 10e-9, 25e-9, wav, sample_rate)
|
|
358
372
|
|
|
359
373
|
params = [-0.03, 0.1e-6, 0.02, 0.3e-6]
|
|
360
374
|
popt, pcov = curve_fit(f, x, y, p0=params)
|
|
@@ -230,19 +230,3 @@ def drag_sinx(freq,
|
|
|
230
230
|
bounds=(round(t0,
|
|
231
231
|
NDIGITS), round(t0 + width + plateau,
|
|
232
232
|
NDIGITS), +inf))
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def _mollifier(x, width):
|
|
236
|
-
return np.exp(1 / ((x / width)**2 - 1))
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def _format_MOLLIFIER(shift, *args):
|
|
240
|
-
width, *_ = args
|
|
241
|
-
return f'\\exp\\frac{{1}}{{\\left(\\frac{{t-{shift:.{NDIGITS}f}}}{{{width:.{NDIGITS}f}}}\\right)^2-1}}'
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
MOLLIFIER = registerBaseFunc(_mollifier)
|
|
245
|
-
registerDerivative(
|
|
246
|
-
MOLLIFIER,
|
|
247
|
-
lambda x, width: 2 * x / width**2 * _mollifier(x, width),
|
|
248
|
-
)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Define version number here and read it from setup.py automatically"""
|
|
2
|
-
__version__ = "2.2.
|
|
2
|
+
__version__ = "2.2.2"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from fractions import Fraction
|
|
2
4
|
from typing import Generator, Iterable, cast
|
|
3
5
|
|
|
@@ -6,12 +8,14 @@ from numpy import e, inf, pi
|
|
|
6
8
|
from numpy.typing import NDArray
|
|
7
9
|
from scipy.signal import sosfilt
|
|
8
10
|
|
|
9
|
-
from ._waveform import (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
from ._waveform import (_D, COS, COSH, D_GAUSSIAN, DRAG, ERF, EXP,
|
|
12
|
+
EXPONENTIALCHIRP, GAUSSIAN, HYPERBOLICCHIRP, INTERP,
|
|
13
|
+
LINEAR, LINEARCHIRP, MOLLIFIER, NDIGITS, SINC, SINH,
|
|
14
|
+
_baseFunc, _baseFunc_latex, _const, _half, _one, _zero,
|
|
15
|
+
add, basic_wave, calc_parts, filter, is_const,
|
|
16
|
+
merge_waveform, mul, pow, registerBaseFunc,
|
|
17
|
+
registerBaseFuncLatex, registerDerivative, shift,
|
|
18
|
+
simplify, wave_sum)
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
def _test_spec_num(num, spec):
|
|
@@ -131,7 +135,7 @@ class Waveform:
|
|
|
131
135
|
@staticmethod
|
|
132
136
|
def _begin(bounds, seq):
|
|
133
137
|
for i, s in enumerate(seq):
|
|
134
|
-
if s
|
|
138
|
+
if s != _zero:
|
|
135
139
|
if i == 0:
|
|
136
140
|
return -inf
|
|
137
141
|
return bounds[i - 1]
|
|
@@ -141,7 +145,7 @@ class Waveform:
|
|
|
141
145
|
def _end(bounds, seq):
|
|
142
146
|
N = len(bounds)
|
|
143
147
|
for i, s in enumerate(seq[::-1]):
|
|
144
|
-
if s
|
|
148
|
+
if s != _zero:
|
|
145
149
|
if i == 0:
|
|
146
150
|
return inf
|
|
147
151
|
return bounds[N - i - 1]
|
|
@@ -179,7 +183,8 @@ class Waveform:
|
|
|
179
183
|
filters = self.filters
|
|
180
184
|
if chunk_size is None:
|
|
181
185
|
x = np.arange(self.start, self.stop, 1 / sample_rate)
|
|
182
|
-
sig =
|
|
186
|
+
sig = cast(np.ndarray,
|
|
187
|
+
self.__call__(x, out=out, function_lib=function_lib))
|
|
183
188
|
if filters is not None:
|
|
184
189
|
sos, initial = filters
|
|
185
190
|
if not isinstance(sos, np.ndarray):
|
|
@@ -393,22 +398,22 @@ class Waveform:
|
|
|
393
398
|
return Waveform(*merge_waveform(self.bounds, self.seq, other.bounds,
|
|
394
399
|
other.seq, oper))
|
|
395
400
|
|
|
396
|
-
def __pow__(self, n):
|
|
401
|
+
def __pow__(self, n) -> Waveform:
|
|
397
402
|
return Waveform(self.bounds, tuple(pow(w, n) for w in self.seq))
|
|
398
403
|
|
|
399
|
-
def __add__(self, other):
|
|
404
|
+
def __add__(self, other) -> Waveform:
|
|
400
405
|
if isinstance(other, Waveform):
|
|
401
406
|
return self._comb(other, add)
|
|
402
407
|
else:
|
|
403
408
|
return self + const(other)
|
|
404
409
|
|
|
405
|
-
def __radd__(self, v):
|
|
410
|
+
def __radd__(self, v) -> Waveform:
|
|
406
411
|
return const(v) + self
|
|
407
412
|
|
|
408
|
-
def __ior__(self, other):
|
|
413
|
+
def __ior__(self, other) -> Waveform:
|
|
409
414
|
return self | other
|
|
410
415
|
|
|
411
|
-
def __or__(self, other):
|
|
416
|
+
def __or__(self, other) -> Waveform:
|
|
412
417
|
if isinstance(other, (int, float, complex)):
|
|
413
418
|
other = const(other)
|
|
414
419
|
w = self.marker + other.marker
|
|
@@ -421,10 +426,10 @@ class Waveform:
|
|
|
421
426
|
|
|
422
427
|
return self._comb(other, _or)
|
|
423
428
|
|
|
424
|
-
def __iand__(self, other):
|
|
429
|
+
def __iand__(self, other) -> Waveform:
|
|
425
430
|
return self & other
|
|
426
431
|
|
|
427
|
-
def __and__(self, other):
|
|
432
|
+
def __and__(self, other) -> Waveform:
|
|
428
433
|
if isinstance(other, (int, float, complex)):
|
|
429
434
|
other = const(other)
|
|
430
435
|
w = self.marker + other.marker
|
|
@@ -443,7 +448,7 @@ class Waveform:
|
|
|
443
448
|
return Waveform(w.bounds,
|
|
444
449
|
tuple(_zero if s == _zero else _one for s in w.seq))
|
|
445
450
|
|
|
446
|
-
def mask(self, edge=0):
|
|
451
|
+
def mask(self, edge: float = 0) -> Waveform:
|
|
447
452
|
w = self.marker
|
|
448
453
|
in_wave = w.seq[0] == _zero
|
|
449
454
|
bounds = []
|
|
@@ -471,31 +476,31 @@ class Waveform:
|
|
|
471
476
|
bounds.append(b)
|
|
472
477
|
return Waveform(tuple(bounds), tuple(seq))
|
|
473
478
|
|
|
474
|
-
def __mul__(self, other):
|
|
479
|
+
def __mul__(self, other) -> Waveform:
|
|
475
480
|
if isinstance(other, Waveform):
|
|
476
481
|
return self._comb(other, mul)
|
|
477
482
|
else:
|
|
478
483
|
return self * const(other)
|
|
479
484
|
|
|
480
|
-
def __rmul__(self, v):
|
|
485
|
+
def __rmul__(self, v) -> Waveform:
|
|
481
486
|
return const(v) * self
|
|
482
487
|
|
|
483
|
-
def __truediv__(self, other):
|
|
488
|
+
def __truediv__(self, other) -> Waveform:
|
|
484
489
|
if isinstance(other, Waveform):
|
|
485
490
|
raise TypeError('division by waveform')
|
|
486
491
|
else:
|
|
487
492
|
return self * const(1 / other)
|
|
488
493
|
|
|
489
|
-
def __neg__(self):
|
|
494
|
+
def __neg__(self) -> Waveform:
|
|
490
495
|
return -1 * self
|
|
491
496
|
|
|
492
|
-
def __sub__(self, other):
|
|
497
|
+
def __sub__(self, other) -> Waveform:
|
|
493
498
|
return self + (-other)
|
|
494
499
|
|
|
495
|
-
def __rsub__(self, v):
|
|
500
|
+
def __rsub__(self, v) -> Waveform:
|
|
496
501
|
return v + (-self)
|
|
497
502
|
|
|
498
|
-
def __rshift__(self, time):
|
|
503
|
+
def __rshift__(self, time) -> Waveform:
|
|
499
504
|
return Waveform(
|
|
500
505
|
tuple(round(bound + time, NDIGITS) for bound in self.bounds),
|
|
501
506
|
tuple(shift(expr, time) for expr in self.seq))
|
|
@@ -520,11 +525,12 @@ class Waveform:
|
|
|
520
525
|
self,
|
|
521
526
|
x,
|
|
522
527
|
frag=False,
|
|
523
|
-
out: np.ndarray | None = None,
|
|
528
|
+
out: np.ndarray | list | None = None,
|
|
524
529
|
accumulate=False,
|
|
525
530
|
function_lib=None
|
|
526
|
-
) -> NDArray[np.float64] | list[
|
|
527
|
-
|
|
531
|
+
) -> NDArray[np.float64 | np.complex128] | list[
|
|
532
|
+
tuple[int, int, NDArray[np.float64 | np.complex128]] | int
|
|
533
|
+
| float | complex] | np.float64:
|
|
528
534
|
if function_lib is None:
|
|
529
535
|
function_lib = _baseFunc
|
|
530
536
|
if isinstance(x, (int, float, complex)):
|
|
@@ -541,8 +547,9 @@ class Waveform:
|
|
|
541
547
|
self._fill_parts(parts, out)
|
|
542
548
|
else:
|
|
543
549
|
if out is None:
|
|
544
|
-
return parts
|
|
550
|
+
return cast(list, parts)
|
|
545
551
|
else:
|
|
552
|
+
out = cast(list, out)
|
|
546
553
|
if not accumulate:
|
|
547
554
|
out.clear()
|
|
548
555
|
out.extend(parts)
|
|
@@ -666,7 +673,8 @@ class WaveVStack(Waveform):
|
|
|
666
673
|
|
|
667
674
|
def __call__(self, x, frag=False, out=None, function_lib=None):
|
|
668
675
|
assert frag is False, 'WaveVStack does not support frag mode'
|
|
669
|
-
out = np.full_like(x, self.offset, dtype=
|
|
676
|
+
out = np.full_like(x, self.offset, dtype=np.complex128)
|
|
677
|
+
out = cast(NDArray[np.complex128], out)
|
|
670
678
|
if self.shift != 0:
|
|
671
679
|
x = x - self.shift
|
|
672
680
|
if function_lib is None:
|
|
@@ -731,6 +739,8 @@ class WaveVStack(Waveform):
|
|
|
731
739
|
wav.start = self.start
|
|
732
740
|
wav.stop = self.stop
|
|
733
741
|
wav.sample_rate = self.sample_rate
|
|
742
|
+
wav.filters = self.filters
|
|
743
|
+
wav.label = self.label
|
|
734
744
|
return wav
|
|
735
745
|
|
|
736
746
|
@staticmethod
|
|
@@ -749,9 +759,11 @@ class WaveVStack(Waveform):
|
|
|
749
759
|
ret.stop = self.stop
|
|
750
760
|
ret.shift = self.shift + time
|
|
751
761
|
ret.offset = self.offset
|
|
762
|
+
ret.filters = self.filters
|
|
763
|
+
ret.label = self.label
|
|
752
764
|
return ret
|
|
753
765
|
|
|
754
|
-
def __add__(self, other):
|
|
766
|
+
def __add__(self, other) -> WaveVStack:
|
|
755
767
|
ret = WaveVStack()
|
|
756
768
|
ret.wlist.extend(self.wlist)
|
|
757
769
|
if isinstance(other, WaveVStack):
|
|
@@ -767,28 +779,34 @@ class WaveVStack(Waveform):
|
|
|
767
779
|
else:
|
|
768
780
|
# ret.wlist.append(((+inf, ), (_const(1.0 * other), )))
|
|
769
781
|
ret.offset += other
|
|
782
|
+
ret.filters = self.filters
|
|
783
|
+
ret.label = self.label
|
|
770
784
|
return ret
|
|
771
785
|
|
|
772
|
-
def __radd__(self, v):
|
|
786
|
+
def __radd__(self, v) -> WaveVStack:
|
|
773
787
|
return self + v
|
|
774
788
|
|
|
775
|
-
def __mul__(self, other):
|
|
789
|
+
def __mul__(self, other) -> WaveVStack:
|
|
776
790
|
if isinstance(other, Waveform):
|
|
777
791
|
other = other.simplify() << self.shift
|
|
778
792
|
ret = WaveVStack([Waveform(*w) * other for w in self.wlist])
|
|
779
793
|
if self.offset != 0:
|
|
780
794
|
w = other * self.offset
|
|
781
795
|
ret.wlist.append((w.bounds, w.seq))
|
|
796
|
+
ret.filters = self.filters
|
|
797
|
+
ret.label = self.label
|
|
782
798
|
return ret
|
|
783
799
|
else:
|
|
784
800
|
ret = WaveVStack([Waveform(*w) * other for w in self.wlist])
|
|
785
801
|
ret.offset = self.offset * other
|
|
802
|
+
ret.filters = self.filters
|
|
803
|
+
ret.label = self.label
|
|
786
804
|
return ret
|
|
787
805
|
|
|
788
|
-
def __rmul__(self, v):
|
|
806
|
+
def __rmul__(self, v) -> WaveVStack:
|
|
789
807
|
return self * v
|
|
790
808
|
|
|
791
|
-
def __eq__(self, other):
|
|
809
|
+
def __eq__(self, other) -> bool:
|
|
792
810
|
if self.wlist:
|
|
793
811
|
return False
|
|
794
812
|
else:
|
|
@@ -999,6 +1017,23 @@ def _format_MOLLIFIER(shift, *args):
|
|
|
999
1017
|
return f"\\mathrm{{Mollifier}}^{{({d})}}\\left(t{shift_str}, r={r}\\right)"
|
|
1000
1018
|
|
|
1001
1019
|
|
|
1020
|
+
def _format_D_GAUSSIAN(shift, *args):
|
|
1021
|
+
sigma = _num_latex(args[0] / np.sqrt(2))
|
|
1022
|
+
d = args[1]
|
|
1023
|
+
shift_str = _num_latex(-shift)
|
|
1024
|
+
if shift_str == '0':
|
|
1025
|
+
shift_str = ''
|
|
1026
|
+
elif shift_str[0] != '-':
|
|
1027
|
+
shift_str = '+' + shift_str
|
|
1028
|
+
|
|
1029
|
+
if d == 0:
|
|
1030
|
+
return f"\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1031
|
+
elif d == 1:
|
|
1032
|
+
return f"\\frac{{\\mathrm{{d}}}}{{\\mathrm{{d}}t}}\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1033
|
+
else:
|
|
1034
|
+
return f"\\frac{{\\mathrm{{d}}^{{{d}}}}}{{\\mathrm{{d}}t^{{{d}}}}}\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1035
|
+
|
|
1036
|
+
|
|
1002
1037
|
registerBaseFuncLatex(LINEAR, _format_LINEAR)
|
|
1003
1038
|
registerBaseFuncLatex(GAUSSIAN, _format_GAUSSIAN)
|
|
1004
1039
|
registerBaseFuncLatex(ERF, _format_ERF)
|
|
@@ -1009,6 +1044,7 @@ registerBaseFuncLatex(COSH, _format_COSH)
|
|
|
1009
1044
|
registerBaseFuncLatex(SINH, _format_SINH)
|
|
1010
1045
|
registerBaseFuncLatex(DRAG, _format_DRAG)
|
|
1011
1046
|
registerBaseFuncLatex(MOLLIFIER, _format_MOLLIFIER)
|
|
1047
|
+
registerBaseFuncLatex(D_GAUSSIAN, _format_D_GAUSSIAN)
|
|
1012
1048
|
|
|
1013
1049
|
|
|
1014
1050
|
def D(wav: Waveform, d: int = 1) -> Waveform:
|
|
@@ -1066,7 +1102,7 @@ def step(edge, type='erf'):
|
|
|
1066
1102
|
seq=(_zero, rise, _one))
|
|
1067
1103
|
|
|
1068
1104
|
|
|
1069
|
-
def square(width, edge=0, type='erf'):
|
|
1105
|
+
def square(width: float, edge: float = 0, type: str = 'erf') -> Waveform:
|
|
1070
1106
|
if width <= 0:
|
|
1071
1107
|
return zero()
|
|
1072
1108
|
if edge == 0:
|
|
@@ -1079,7 +1115,9 @@ def square(width, edge=0, type='erf'):
|
|
|
1079
1115
|
(step(edge, type=type) >> width / 2))
|
|
1080
1116
|
|
|
1081
1117
|
|
|
1082
|
-
def gaussian(width,
|
|
1118
|
+
def gaussian(width: float,
|
|
1119
|
+
plateau: float = 0.0,
|
|
1120
|
+
d: int | None = None) -> Waveform:
|
|
1083
1121
|
if width <= 0 and plateau <= 0.0:
|
|
1084
1122
|
return zero()
|
|
1085
1123
|
# width is two times FWHM
|
|
@@ -1087,26 +1125,27 @@ def gaussian(width, plateau=0.0):
|
|
|
1087
1125
|
std_sq2 = width / 3.3302184446307908
|
|
1088
1126
|
# std is set to give total pulse area same as a square
|
|
1089
1127
|
# std_sq2 = width/np.sqrt(np.pi)
|
|
1128
|
+
if d is None:
|
|
1129
|
+
base = lambda shift: basic_wave(GAUSSIAN, std_sq2, shift=shift)
|
|
1130
|
+
else:
|
|
1131
|
+
base = lambda shift: basic_wave(D_GAUSSIAN, std_sq2, d, shift=shift)
|
|
1132
|
+
|
|
1090
1133
|
if round(0.5 * plateau, NDIGITS) <= 0.0:
|
|
1091
1134
|
return Waveform(bounds=(round(-0.75 * width,
|
|
1092
1135
|
NDIGITS), round(0.75 * width,
|
|
1093
1136
|
NDIGITS), +inf),
|
|
1094
|
-
seq=(_zero,
|
|
1137
|
+
seq=(_zero, base(0), _zero))
|
|
1095
1138
|
else:
|
|
1096
1139
|
return Waveform(bounds=(round(-0.75 * width - 0.5 * plateau,
|
|
1097
1140
|
NDIGITS), round(-0.5 * plateau, NDIGITS),
|
|
1098
1141
|
round(0.5 * plateau, NDIGITS),
|
|
1099
1142
|
round(0.75 * width + 0.5 * plateau,
|
|
1100
1143
|
NDIGITS), +inf),
|
|
1101
|
-
seq=(_zero,
|
|
1102
|
-
|
|
1103
|
-
std_sq2,
|
|
1104
|
-
shift=-0.5 * plateau), _one,
|
|
1105
|
-
basic_wave(GAUSSIAN, std_sq2,
|
|
1106
|
-
shift=0.5 * plateau), _zero))
|
|
1144
|
+
seq=(_zero, base(-0.5 * plateau), _one,
|
|
1145
|
+
base(0.5 * plateau), _zero))
|
|
1107
1146
|
|
|
1108
1147
|
|
|
1109
|
-
def cos(w, phi=0):
|
|
1148
|
+
def cos(w: float, phi: float = 0) -> Waveform:
|
|
1110
1149
|
if w == 0:
|
|
1111
1150
|
return const(np.cos(phi))
|
|
1112
1151
|
if w < 0:
|
|
@@ -1115,7 +1154,7 @@ def cos(w, phi=0):
|
|
|
1115
1154
|
return Waveform(seq=(basic_wave(COS, w, shift=-phi / w), ))
|
|
1116
1155
|
|
|
1117
1156
|
|
|
1118
|
-
def sin(w, phi=0):
|
|
1157
|
+
def sin(w: float, phi: float = 0) -> Waveform:
|
|
1119
1158
|
if w == 0:
|
|
1120
1159
|
return const(np.sin(phi))
|
|
1121
1160
|
if w < 0:
|
|
@@ -1124,7 +1163,7 @@ def sin(w, phi=0):
|
|
|
1124
1163
|
return Waveform(seq=(basic_wave(COS, w, shift=(pi / 2 - phi) / w), ))
|
|
1125
1164
|
|
|
1126
1165
|
|
|
1127
|
-
def exp(alpha):
|
|
1166
|
+
def exp(alpha: float | complex) -> Waveform:
|
|
1128
1167
|
if isinstance(alpha, complex):
|
|
1129
1168
|
if alpha.real == 0:
|
|
1130
1169
|
return cos(alpha.imag) + 1j * sin(alpha.imag)
|
|
@@ -1134,7 +1173,7 @@ def exp(alpha):
|
|
|
1134
1173
|
return Waveform(seq=(basic_wave(EXP, alpha), ))
|
|
1135
1174
|
|
|
1136
1175
|
|
|
1137
|
-
def sinc(bw):
|
|
1176
|
+
def sinc(bw: float) -> Waveform:
|
|
1138
1177
|
if bw <= 0:
|
|
1139
1178
|
return zero()
|
|
1140
1179
|
width = 100 / bw
|
|
@@ -1143,7 +1182,7 @@ def sinc(bw):
|
|
|
1143
1182
|
seq=(_zero, basic_wave(SINC, bw), _zero))
|
|
1144
1183
|
|
|
1145
1184
|
|
|
1146
|
-
def cosPulse(width, plateau=0.0):
|
|
1185
|
+
def cosPulse(width: float, plateau: float = 0.0) -> Waveform:
|
|
1147
1186
|
# cos = basic_wave(COS, 2*np.pi/width)
|
|
1148
1187
|
# pulse = mul(add(cos, _one), _half)
|
|
1149
1188
|
if round(0.5 * plateau, NDIGITS) > 0:
|
|
@@ -1157,19 +1196,21 @@ def cosPulse(width, plateau=0.0):
|
|
|
1157
1196
|
seq=(_zero, pulse, _zero))
|
|
1158
1197
|
|
|
1159
1198
|
|
|
1160
|
-
def hanning(width, plateau=0.0):
|
|
1199
|
+
def hanning(width: float, plateau: float = 0.0) -> Waveform:
|
|
1161
1200
|
return cosPulse(width, plateau=plateau)
|
|
1162
1201
|
|
|
1163
1202
|
|
|
1164
|
-
def cosh(w):
|
|
1203
|
+
def cosh(w: float) -> Waveform:
|
|
1165
1204
|
return Waveform(seq=(basic_wave(COSH, w), ))
|
|
1166
1205
|
|
|
1167
1206
|
|
|
1168
|
-
def sinh(w):
|
|
1207
|
+
def sinh(w: float) -> Waveform:
|
|
1169
1208
|
return Waveform(seq=(basic_wave(SINH, w), ))
|
|
1170
1209
|
|
|
1171
1210
|
|
|
1172
|
-
def coshPulse(width
|
|
1211
|
+
def coshPulse(width: float,
|
|
1212
|
+
eps: float = 1.0,
|
|
1213
|
+
plateau: float = 0.0) -> Waveform:
|
|
1173
1214
|
"""Cosine hyperbolic pulse with the following im
|
|
1174
1215
|
|
|
1175
1216
|
pulse edge shape:
|
|
@@ -1219,25 +1260,25 @@ def coshPulse(width, eps=1.0, plateau=0.0):
|
|
|
1219
1260
|
seq=(_zero, raising, _one, falling, _zero))
|
|
1220
1261
|
|
|
1221
1262
|
|
|
1222
|
-
def general_cosine(duration, *arg):
|
|
1263
|
+
def general_cosine(duration: float, *arg: float) -> Waveform:
|
|
1223
1264
|
wav = zero()
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
for i, a in enumerate(
|
|
1265
|
+
arg_ = np.asarray(arg)
|
|
1266
|
+
arg_ /= arg_[::2].sum()
|
|
1267
|
+
for i, a in enumerate(arg_, start=1):
|
|
1227
1268
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1228
1269
|
return wav * square(duration)
|
|
1229
1270
|
|
|
1230
1271
|
|
|
1231
|
-
def slepian(duration, *arg):
|
|
1272
|
+
def slepian(duration: float, *arg: float) -> Waveform:
|
|
1232
1273
|
wav = zero()
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
for i, a in enumerate(
|
|
1274
|
+
arg_ = np.asarray(arg)
|
|
1275
|
+
arg_ /= arg_[::2].sum()
|
|
1276
|
+
for i, a in enumerate(arg_, start=1):
|
|
1236
1277
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1237
1278
|
return wav * square(duration)
|
|
1238
1279
|
|
|
1239
1280
|
|
|
1240
|
-
def mollifier(width, plateau: float = 0.0, d: int = 0):
|
|
1281
|
+
def mollifier(width: float, plateau: float = 0.0, d: int = 0) -> Waveform:
|
|
1241
1282
|
"""
|
|
1242
1283
|
Mollifier function is a smooth function that is 1 at the origin and 0 outside a certain radius.
|
|
1243
1284
|
It is defined as:
|
|
@@ -1298,7 +1339,13 @@ def t():
|
|
|
1298
1339
|
return Waveform(seq=((((LINEAR, 0), ), (1, )), (1, )))
|
|
1299
1340
|
|
|
1300
1341
|
|
|
1301
|
-
def drag(freq
|
|
1342
|
+
def drag(freq: float,
|
|
1343
|
+
width: float,
|
|
1344
|
+
plateau: float = 0,
|
|
1345
|
+
delta: float = 0,
|
|
1346
|
+
block_freq: float | None = None,
|
|
1347
|
+
phase: float = 0,
|
|
1348
|
+
t0: float = 0) -> Waveform:
|
|
1302
1349
|
phase += pi * delta * (width + plateau)
|
|
1303
1350
|
if plateau <= 0:
|
|
1304
1351
|
return Waveform(seq=(_zero,
|
|
@@ -1327,7 +1374,11 @@ def drag(freq, width, plateau=0, delta=0, block_freq=None, phase=0, t0=0):
|
|
|
1327
1374
|
NDIGITS), +inf))
|
|
1328
1375
|
|
|
1329
1376
|
|
|
1330
|
-
def chirp(f0
|
|
1377
|
+
def chirp(f0: float,
|
|
1378
|
+
f1: float,
|
|
1379
|
+
T: float,
|
|
1380
|
+
phi0: float = 0,
|
|
1381
|
+
type: str = 'linear') -> Waveform:
|
|
1331
1382
|
"""
|
|
1332
1383
|
A chirp is a signal in which the frequency increases (up-chirp)
|
|
1333
1384
|
or decreases (down-chirp) with time. In some sources, the term
|
|
@@ -1366,7 +1417,7 @@ def chirp(f0, f1, T, phi0=0, type='linear'):
|
|
|
1366
1417
|
raise ValueError(f'unknown type {type}')
|
|
1367
1418
|
|
|
1368
1419
|
|
|
1369
|
-
def interp(x, y):
|
|
1420
|
+
def interp(x: NDArray[np.float64], y: NDArray[np.float64]) -> Waveform:
|
|
1370
1421
|
seq, bounds = [_zero], [x[0]]
|
|
1371
1422
|
for x1, x2, y1, y2 in zip(x[:-1], x[1:], y[:-1], y[1:]):
|
|
1372
1423
|
if x2 == x1:
|
|
@@ -1384,12 +1435,20 @@ def interp(x, y):
|
|
|
1384
1435
|
for b in bounds)).simplify()
|
|
1385
1436
|
|
|
1386
1437
|
|
|
1387
|
-
def cut(wav
|
|
1438
|
+
def cut(wav: Waveform,
|
|
1439
|
+
start: float | None = None,
|
|
1440
|
+
stop: float | None = None,
|
|
1441
|
+
head: float | None = None,
|
|
1442
|
+
tail: float | None = None,
|
|
1443
|
+
min: float | None = None,
|
|
1444
|
+
max: float | None = None) -> Waveform:
|
|
1388
1445
|
offset = 0
|
|
1389
1446
|
if start is not None and head is not None:
|
|
1390
|
-
offset = head - wav(np.array([1.0 * start
|
|
1447
|
+
offset = head - cast(NDArray[np.float64], wav(np.array([1.0 * start
|
|
1448
|
+
])))[0]
|
|
1391
1449
|
elif stop is not None and tail is not None:
|
|
1392
|
-
offset = tail - wav(np.array([1.0 * stop
|
|
1450
|
+
offset = tail - cast(NDArray[np.float64], wav(np.array([1.0 * stop
|
|
1451
|
+
])))[0]
|
|
1393
1452
|
wav = wav + offset
|
|
1394
1453
|
|
|
1395
1454
|
if start is not None:
|
|
@@ -1420,15 +1479,15 @@ def samplingPoints(start, stop, points):
|
|
|
1420
1479
|
tuple(points)), _zero))
|
|
1421
1480
|
|
|
1422
1481
|
|
|
1423
|
-
def mixing(I,
|
|
1424
|
-
Q=None,
|
|
1482
|
+
def mixing(I: Waveform,
|
|
1483
|
+
Q: Waveform | None = None,
|
|
1425
1484
|
*,
|
|
1426
|
-
phase=0.0,
|
|
1427
|
-
freq=0.0,
|
|
1428
|
-
ratioIQ=1.0,
|
|
1429
|
-
phaseDiff=0.0,
|
|
1430
|
-
block_freq=None,
|
|
1431
|
-
DRAGScaling=None):
|
|
1485
|
+
phase: float = 0.0,
|
|
1486
|
+
freq: float = 0.0,
|
|
1487
|
+
ratioIQ: float = 1.0,
|
|
1488
|
+
phaseDiff: float = 0.0,
|
|
1489
|
+
block_freq: float | None = None,
|
|
1490
|
+
DRAGScaling: float | None = None) -> tuple[Waveform, Waveform]:
|
|
1432
1491
|
"""SSB or envelope mixing
|
|
1433
1492
|
"""
|
|
1434
1493
|
if Q is None:
|
|
@@ -1442,8 +1501,8 @@ def mixing(I,
|
|
|
1442
1501
|
Qout = -I * sin(w, -phase + phaseDiff) + Q * cos(w, -phase + phaseDiff)
|
|
1443
1502
|
else:
|
|
1444
1503
|
# envelope mixing
|
|
1445
|
-
Iout = I * np.cos(-phase) + Q * np.sin(-phase)
|
|
1446
|
-
Qout = -I * np.sin(-phase) + Q * np.cos(-phase)
|
|
1504
|
+
Iout = cast(Waveform, I * np.cos(-phase) + Q * np.sin(-phase))
|
|
1505
|
+
Qout = cast(Waveform, -I * np.sin(-phase) + Q * np.cos(-phase))
|
|
1447
1506
|
|
|
1448
1507
|
# apply DRAG
|
|
1449
1508
|
if block_freq is not None and block_freq != freq:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|