waveforms 2.2.0__tar.gz → 2.2.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.
- {waveforms-2.2.0 → waveforms-2.2.1}/PKG-INFO +1 -1
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/_waveform.pyi +11 -6
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/_waveform.pyx +14 -2
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/distortion.py +39 -25
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/multy_drag.py +0 -16
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/version.py +1 -1
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/waveform.py +126 -77
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/waveform_parser.py +1 -1
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms.egg-info/PKG-INFO +1 -1
- {waveforms-2.2.0 → waveforms-2.2.1}/LICENSE +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/MANIFEST.in +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/README.md +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/pyproject.toml +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/setup.cfg +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/setup.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/src/waveform.h +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/tests/test_multi_drag.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/tests/test_waveform.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/tests/test_wavevstack.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/WaveformLexer.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/WaveformListener.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/WaveformParser.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/__init__.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/__main__.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms/utils.py +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms.egg-info/SOURCES.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms.egg-info/entry_points.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/waveforms.egg-info/requires.txt +0 -0
- {waveforms-2.2.0 → waveforms-2.2.1}/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
|
|
|
@@ -360,7 +365,8 @@ def _mollifier(t: np.ndarray, r: float, d: int):
|
|
|
360
365
|
for n in range(1, d):
|
|
361
366
|
p = np.poly1d([1, 0, -2, 0, 1]) * p.deriv() + np.poly1d(
|
|
362
367
|
[-4 * n, 0, 4 * n - 2, 0]) * p
|
|
363
|
-
return np.exp(1 / (np.abs(x)**2 - 1) +
|
|
368
|
+
return np.exp(1 / (np.abs(x)**2 - 1) +
|
|
369
|
+
1) * p(x) / (1 - x**2)**(2 * d) / r**d
|
|
364
370
|
|
|
365
371
|
|
|
366
372
|
LINEAR = registerBaseFunc(_LINEAR)
|
|
@@ -377,6 +383,7 @@ COSH = registerBaseFunc(_COSH)
|
|
|
377
383
|
SINH = registerBaseFunc(_SINH)
|
|
378
384
|
DRAG = registerBaseFunc(_drag)
|
|
379
385
|
MOLLIFIER = registerBaseFunc(_mollifier)
|
|
386
|
+
D_GAUSSIAN = registerBaseFunc(_D_GAUSSIAN)
|
|
380
387
|
|
|
381
388
|
|
|
382
389
|
def _d_LINEAR(shift, *args):
|
|
@@ -447,7 +454,11 @@ def _d_HYPERBOLICCHIRP(shift, f0, k, phi0):
|
|
|
447
454
|
|
|
448
455
|
|
|
449
456
|
def _d_MOLLIFIER(shift, r, d):
|
|
450
|
-
return (((((MOLLIFIER, r, d+1, shift), ), (1, )), ), (1, ))
|
|
457
|
+
return (((((MOLLIFIER, r, d + 1, shift), ), (1, )), ), (1, ))
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _d_D_GAUSSIAN(shift, std_sq2, n):
|
|
461
|
+
return (((((D_GAUSSIAN, std_sq2, n + 1, shift), ), (1, )), ), (1, ))
|
|
451
462
|
|
|
452
463
|
|
|
453
464
|
# register derivative
|
|
@@ -464,6 +475,7 @@ registerDerivative(LINEARCHIRP, _d_LINEARCHIRP)
|
|
|
464
475
|
registerDerivative(EXPONENTIALCHIRP, _d_EXPONENTIALCHIRP)
|
|
465
476
|
registerDerivative(HYPERBOLICCHIRP, _d_HYPERBOLICCHIRP)
|
|
466
477
|
registerDerivative(MOLLIFIER, _d_MOLLIFIER)
|
|
478
|
+
registerDerivative(D_GAUSSIAN, _d_D_GAUSSIAN)
|
|
467
479
|
|
|
468
480
|
|
|
469
481
|
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.1"
|
|
@@ -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:
|
|
@@ -751,7 +759,7 @@ class WaveVStack(Waveform):
|
|
|
751
759
|
ret.offset = self.offset
|
|
752
760
|
return ret
|
|
753
761
|
|
|
754
|
-
def __add__(self, other):
|
|
762
|
+
def __add__(self, other) -> WaveVStack:
|
|
755
763
|
ret = WaveVStack()
|
|
756
764
|
ret.wlist.extend(self.wlist)
|
|
757
765
|
if isinstance(other, WaveVStack):
|
|
@@ -769,10 +777,10 @@ class WaveVStack(Waveform):
|
|
|
769
777
|
ret.offset += other
|
|
770
778
|
return ret
|
|
771
779
|
|
|
772
|
-
def __radd__(self, v):
|
|
780
|
+
def __radd__(self, v) -> WaveVStack:
|
|
773
781
|
return self + v
|
|
774
782
|
|
|
775
|
-
def __mul__(self, other):
|
|
783
|
+
def __mul__(self, other) -> WaveVStack:
|
|
776
784
|
if isinstance(other, Waveform):
|
|
777
785
|
other = other.simplify() << self.shift
|
|
778
786
|
ret = WaveVStack([Waveform(*w) * other for w in self.wlist])
|
|
@@ -785,10 +793,10 @@ class WaveVStack(Waveform):
|
|
|
785
793
|
ret.offset = self.offset * other
|
|
786
794
|
return ret
|
|
787
795
|
|
|
788
|
-
def __rmul__(self, v):
|
|
796
|
+
def __rmul__(self, v) -> WaveVStack:
|
|
789
797
|
return self * v
|
|
790
798
|
|
|
791
|
-
def __eq__(self, other):
|
|
799
|
+
def __eq__(self, other) -> bool:
|
|
792
800
|
if self.wlist:
|
|
793
801
|
return False
|
|
794
802
|
else:
|
|
@@ -999,6 +1007,23 @@ def _format_MOLLIFIER(shift, *args):
|
|
|
999
1007
|
return f"\\mathrm{{Mollifier}}^{{({d})}}\\left(t{shift_str}, r={r}\\right)"
|
|
1000
1008
|
|
|
1001
1009
|
|
|
1010
|
+
def _format_D_GAUSSIAN(shift, *args):
|
|
1011
|
+
sigma = _num_latex(args[0] / np.sqrt(2))
|
|
1012
|
+
d = args[1]
|
|
1013
|
+
shift_str = _num_latex(-shift)
|
|
1014
|
+
if shift_str == '0':
|
|
1015
|
+
shift_str = ''
|
|
1016
|
+
elif shift_str[0] != '-':
|
|
1017
|
+
shift_str = '+' + shift_str
|
|
1018
|
+
|
|
1019
|
+
if d == 0:
|
|
1020
|
+
return f"\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1021
|
+
elif d == 1:
|
|
1022
|
+
return f"\\frac{{\\mathrm{{d}}}}{{\\mathrm{{d}}t}}\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1023
|
+
else:
|
|
1024
|
+
return f"\\frac{{\\mathrm{{d}}^{{{d}}}}}{{\\mathrm{{d}}t^{{{d}}}}}\\mathrm{{Gaussian}}\\left(t{shift_str}, \\sigma={sigma}\\right)"
|
|
1025
|
+
|
|
1026
|
+
|
|
1002
1027
|
registerBaseFuncLatex(LINEAR, _format_LINEAR)
|
|
1003
1028
|
registerBaseFuncLatex(GAUSSIAN, _format_GAUSSIAN)
|
|
1004
1029
|
registerBaseFuncLatex(ERF, _format_ERF)
|
|
@@ -1009,6 +1034,7 @@ registerBaseFuncLatex(COSH, _format_COSH)
|
|
|
1009
1034
|
registerBaseFuncLatex(SINH, _format_SINH)
|
|
1010
1035
|
registerBaseFuncLatex(DRAG, _format_DRAG)
|
|
1011
1036
|
registerBaseFuncLatex(MOLLIFIER, _format_MOLLIFIER)
|
|
1037
|
+
registerBaseFuncLatex(D_GAUSSIAN, _format_D_GAUSSIAN)
|
|
1012
1038
|
|
|
1013
1039
|
|
|
1014
1040
|
def D(wav: Waveform, d: int = 1) -> Waveform:
|
|
@@ -1066,7 +1092,7 @@ def step(edge, type='erf'):
|
|
|
1066
1092
|
seq=(_zero, rise, _one))
|
|
1067
1093
|
|
|
1068
1094
|
|
|
1069
|
-
def square(width, edge=0, type='erf'):
|
|
1095
|
+
def square(width: float, edge: float = 0, type: str = 'erf') -> Waveform:
|
|
1070
1096
|
if width <= 0:
|
|
1071
1097
|
return zero()
|
|
1072
1098
|
if edge == 0:
|
|
@@ -1079,7 +1105,9 @@ def square(width, edge=0, type='erf'):
|
|
|
1079
1105
|
(step(edge, type=type) >> width / 2))
|
|
1080
1106
|
|
|
1081
1107
|
|
|
1082
|
-
def gaussian(width,
|
|
1108
|
+
def gaussian(width: float,
|
|
1109
|
+
plateau: float = 0.0,
|
|
1110
|
+
d: int | None = None) -> Waveform:
|
|
1083
1111
|
if width <= 0 and plateau <= 0.0:
|
|
1084
1112
|
return zero()
|
|
1085
1113
|
# width is two times FWHM
|
|
@@ -1087,26 +1115,27 @@ def gaussian(width, plateau=0.0):
|
|
|
1087
1115
|
std_sq2 = width / 3.3302184446307908
|
|
1088
1116
|
# std is set to give total pulse area same as a square
|
|
1089
1117
|
# std_sq2 = width/np.sqrt(np.pi)
|
|
1118
|
+
if d is None:
|
|
1119
|
+
base = lambda shift: basic_wave(GAUSSIAN, std_sq2, shift=shift)
|
|
1120
|
+
else:
|
|
1121
|
+
base = lambda shift: basic_wave(D_GAUSSIAN, std_sq2, d, shift=shift)
|
|
1122
|
+
|
|
1090
1123
|
if round(0.5 * plateau, NDIGITS) <= 0.0:
|
|
1091
1124
|
return Waveform(bounds=(round(-0.75 * width,
|
|
1092
1125
|
NDIGITS), round(0.75 * width,
|
|
1093
1126
|
NDIGITS), +inf),
|
|
1094
|
-
seq=(_zero,
|
|
1127
|
+
seq=(_zero, base(0), _zero))
|
|
1095
1128
|
else:
|
|
1096
1129
|
return Waveform(bounds=(round(-0.75 * width - 0.5 * plateau,
|
|
1097
1130
|
NDIGITS), round(-0.5 * plateau, NDIGITS),
|
|
1098
1131
|
round(0.5 * plateau, NDIGITS),
|
|
1099
1132
|
round(0.75 * width + 0.5 * plateau,
|
|
1100
1133
|
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))
|
|
1134
|
+
seq=(_zero, base(-0.5 * plateau), _one,
|
|
1135
|
+
base(0.5 * plateau), _zero))
|
|
1107
1136
|
|
|
1108
1137
|
|
|
1109
|
-
def cos(w, phi=0):
|
|
1138
|
+
def cos(w: float, phi: float = 0) -> Waveform:
|
|
1110
1139
|
if w == 0:
|
|
1111
1140
|
return const(np.cos(phi))
|
|
1112
1141
|
if w < 0:
|
|
@@ -1115,7 +1144,7 @@ def cos(w, phi=0):
|
|
|
1115
1144
|
return Waveform(seq=(basic_wave(COS, w, shift=-phi / w), ))
|
|
1116
1145
|
|
|
1117
1146
|
|
|
1118
|
-
def sin(w, phi=0):
|
|
1147
|
+
def sin(w: float, phi: float = 0) -> Waveform:
|
|
1119
1148
|
if w == 0:
|
|
1120
1149
|
return const(np.sin(phi))
|
|
1121
1150
|
if w < 0:
|
|
@@ -1124,7 +1153,7 @@ def sin(w, phi=0):
|
|
|
1124
1153
|
return Waveform(seq=(basic_wave(COS, w, shift=(pi / 2 - phi) / w), ))
|
|
1125
1154
|
|
|
1126
1155
|
|
|
1127
|
-
def exp(alpha):
|
|
1156
|
+
def exp(alpha: float | complex) -> Waveform:
|
|
1128
1157
|
if isinstance(alpha, complex):
|
|
1129
1158
|
if alpha.real == 0:
|
|
1130
1159
|
return cos(alpha.imag) + 1j * sin(alpha.imag)
|
|
@@ -1134,7 +1163,7 @@ def exp(alpha):
|
|
|
1134
1163
|
return Waveform(seq=(basic_wave(EXP, alpha), ))
|
|
1135
1164
|
|
|
1136
1165
|
|
|
1137
|
-
def sinc(bw):
|
|
1166
|
+
def sinc(bw: float) -> Waveform:
|
|
1138
1167
|
if bw <= 0:
|
|
1139
1168
|
return zero()
|
|
1140
1169
|
width = 100 / bw
|
|
@@ -1143,7 +1172,7 @@ def sinc(bw):
|
|
|
1143
1172
|
seq=(_zero, basic_wave(SINC, bw), _zero))
|
|
1144
1173
|
|
|
1145
1174
|
|
|
1146
|
-
def cosPulse(width, plateau=0.0):
|
|
1175
|
+
def cosPulse(width: float, plateau: float = 0.0) -> Waveform:
|
|
1147
1176
|
# cos = basic_wave(COS, 2*np.pi/width)
|
|
1148
1177
|
# pulse = mul(add(cos, _one), _half)
|
|
1149
1178
|
if round(0.5 * plateau, NDIGITS) > 0:
|
|
@@ -1157,19 +1186,21 @@ def cosPulse(width, plateau=0.0):
|
|
|
1157
1186
|
seq=(_zero, pulse, _zero))
|
|
1158
1187
|
|
|
1159
1188
|
|
|
1160
|
-
def hanning(width, plateau=0.0):
|
|
1189
|
+
def hanning(width: float, plateau: float = 0.0) -> Waveform:
|
|
1161
1190
|
return cosPulse(width, plateau=plateau)
|
|
1162
1191
|
|
|
1163
1192
|
|
|
1164
|
-
def cosh(w):
|
|
1193
|
+
def cosh(w: float) -> Waveform:
|
|
1165
1194
|
return Waveform(seq=(basic_wave(COSH, w), ))
|
|
1166
1195
|
|
|
1167
1196
|
|
|
1168
|
-
def sinh(w):
|
|
1197
|
+
def sinh(w: float) -> Waveform:
|
|
1169
1198
|
return Waveform(seq=(basic_wave(SINH, w), ))
|
|
1170
1199
|
|
|
1171
1200
|
|
|
1172
|
-
def coshPulse(width
|
|
1201
|
+
def coshPulse(width: float,
|
|
1202
|
+
eps: float = 1.0,
|
|
1203
|
+
plateau: float = 0.0) -> Waveform:
|
|
1173
1204
|
"""Cosine hyperbolic pulse with the following im
|
|
1174
1205
|
|
|
1175
1206
|
pulse edge shape:
|
|
@@ -1219,25 +1250,25 @@ def coshPulse(width, eps=1.0, plateau=0.0):
|
|
|
1219
1250
|
seq=(_zero, raising, _one, falling, _zero))
|
|
1220
1251
|
|
|
1221
1252
|
|
|
1222
|
-
def general_cosine(duration, *arg):
|
|
1253
|
+
def general_cosine(duration: float, *arg: float) -> Waveform:
|
|
1223
1254
|
wav = zero()
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
for i, a in enumerate(
|
|
1255
|
+
arg_ = np.asarray(arg)
|
|
1256
|
+
arg_ /= arg_[::2].sum()
|
|
1257
|
+
for i, a in enumerate(arg_, start=1):
|
|
1227
1258
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1228
1259
|
return wav * square(duration)
|
|
1229
1260
|
|
|
1230
1261
|
|
|
1231
|
-
def slepian(duration, *arg):
|
|
1262
|
+
def slepian(duration: float, *arg: float) -> Waveform:
|
|
1232
1263
|
wav = zero()
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
for i, a in enumerate(
|
|
1264
|
+
arg_ = np.asarray(arg)
|
|
1265
|
+
arg_ /= arg_[::2].sum()
|
|
1266
|
+
for i, a in enumerate(arg_, start=1):
|
|
1236
1267
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1237
1268
|
return wav * square(duration)
|
|
1238
1269
|
|
|
1239
1270
|
|
|
1240
|
-
def mollifier(width, plateau: float = 0.0, d: int = 0):
|
|
1271
|
+
def mollifier(width: float, plateau: float = 0.0, d: int = 0) -> Waveform:
|
|
1241
1272
|
"""
|
|
1242
1273
|
Mollifier function is a smooth function that is 1 at the origin and 0 outside a certain radius.
|
|
1243
1274
|
It is defined as:
|
|
@@ -1298,7 +1329,13 @@ def t():
|
|
|
1298
1329
|
return Waveform(seq=((((LINEAR, 0), ), (1, )), (1, )))
|
|
1299
1330
|
|
|
1300
1331
|
|
|
1301
|
-
def drag(freq
|
|
1332
|
+
def drag(freq: float,
|
|
1333
|
+
width: float,
|
|
1334
|
+
plateau: float = 0,
|
|
1335
|
+
delta: float = 0,
|
|
1336
|
+
block_freq: float | None = None,
|
|
1337
|
+
phase: float = 0,
|
|
1338
|
+
t0: float = 0) -> Waveform:
|
|
1302
1339
|
phase += pi * delta * (width + plateau)
|
|
1303
1340
|
if plateau <= 0:
|
|
1304
1341
|
return Waveform(seq=(_zero,
|
|
@@ -1327,7 +1364,11 @@ def drag(freq, width, plateau=0, delta=0, block_freq=None, phase=0, t0=0):
|
|
|
1327
1364
|
NDIGITS), +inf))
|
|
1328
1365
|
|
|
1329
1366
|
|
|
1330
|
-
def chirp(f0
|
|
1367
|
+
def chirp(f0: float,
|
|
1368
|
+
f1: float,
|
|
1369
|
+
T: float,
|
|
1370
|
+
phi0: float = 0,
|
|
1371
|
+
type: str = 'linear') -> Waveform:
|
|
1331
1372
|
"""
|
|
1332
1373
|
A chirp is a signal in which the frequency increases (up-chirp)
|
|
1333
1374
|
or decreases (down-chirp) with time. In some sources, the term
|
|
@@ -1366,7 +1407,7 @@ def chirp(f0, f1, T, phi0=0, type='linear'):
|
|
|
1366
1407
|
raise ValueError(f'unknown type {type}')
|
|
1367
1408
|
|
|
1368
1409
|
|
|
1369
|
-
def interp(x, y):
|
|
1410
|
+
def interp(x: NDArray[np.float64], y: NDArray[np.float64]) -> Waveform:
|
|
1370
1411
|
seq, bounds = [_zero], [x[0]]
|
|
1371
1412
|
for x1, x2, y1, y2 in zip(x[:-1], x[1:], y[:-1], y[1:]):
|
|
1372
1413
|
if x2 == x1:
|
|
@@ -1384,12 +1425,20 @@ def interp(x, y):
|
|
|
1384
1425
|
for b in bounds)).simplify()
|
|
1385
1426
|
|
|
1386
1427
|
|
|
1387
|
-
def cut(wav
|
|
1428
|
+
def cut(wav: Waveform,
|
|
1429
|
+
start: float | None = None,
|
|
1430
|
+
stop: float | None = None,
|
|
1431
|
+
head: float | None = None,
|
|
1432
|
+
tail: float | None = None,
|
|
1433
|
+
min: float | None = None,
|
|
1434
|
+
max: float | None = None) -> Waveform:
|
|
1388
1435
|
offset = 0
|
|
1389
1436
|
if start is not None and head is not None:
|
|
1390
|
-
offset = head - wav(np.array([1.0 * start
|
|
1437
|
+
offset = head - cast(NDArray[np.float64], wav(np.array([1.0 * start
|
|
1438
|
+
])))[0]
|
|
1391
1439
|
elif stop is not None and tail is not None:
|
|
1392
|
-
offset = tail - wav(np.array([1.0 * stop
|
|
1440
|
+
offset = tail - cast(NDArray[np.float64], wav(np.array([1.0 * stop
|
|
1441
|
+
])))[0]
|
|
1393
1442
|
wav = wav + offset
|
|
1394
1443
|
|
|
1395
1444
|
if start is not None:
|
|
@@ -1420,15 +1469,15 @@ def samplingPoints(start, stop, points):
|
|
|
1420
1469
|
tuple(points)), _zero))
|
|
1421
1470
|
|
|
1422
1471
|
|
|
1423
|
-
def mixing(I,
|
|
1424
|
-
Q=None,
|
|
1472
|
+
def mixing(I: Waveform,
|
|
1473
|
+
Q: Waveform | None = None,
|
|
1425
1474
|
*,
|
|
1426
|
-
phase=0.0,
|
|
1427
|
-
freq=0.0,
|
|
1428
|
-
ratioIQ=1.0,
|
|
1429
|
-
phaseDiff=0.0,
|
|
1430
|
-
block_freq=None,
|
|
1431
|
-
DRAGScaling=None):
|
|
1475
|
+
phase: float = 0.0,
|
|
1476
|
+
freq: float = 0.0,
|
|
1477
|
+
ratioIQ: float = 1.0,
|
|
1478
|
+
phaseDiff: float = 0.0,
|
|
1479
|
+
block_freq: float | None = None,
|
|
1480
|
+
DRAGScaling: float | None = None) -> tuple[Waveform, Waveform]:
|
|
1432
1481
|
"""SSB or envelope mixing
|
|
1433
1482
|
"""
|
|
1434
1483
|
if Q is None:
|
|
@@ -1442,8 +1491,8 @@ def mixing(I,
|
|
|
1442
1491
|
Qout = -I * sin(w, -phase + phaseDiff) + Q * cos(w, -phase + phaseDiff)
|
|
1443
1492
|
else:
|
|
1444
1493
|
# envelope mixing
|
|
1445
|
-
Iout = I * np.cos(-phase) + Q * np.sin(-phase)
|
|
1446
|
-
Qout = -I * np.sin(-phase) + Q * np.cos(-phase)
|
|
1494
|
+
Iout = cast(Waveform, I * np.cos(-phase) + Q * np.sin(-phase))
|
|
1495
|
+
Qout = cast(Waveform, -I * np.sin(-phase) + Q * np.cos(-phase))
|
|
1447
1496
|
|
|
1448
1497
|
# apply DRAG
|
|
1449
1498
|
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
|