waveforms 2.1.1__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.1.1 → waveforms-2.2.1}/PKG-INFO +1 -1
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/__init__.py +4 -3
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/_waveform.pyi +13 -7
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/_waveform.pyx +30 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/distortion.py +39 -25
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/multy_drag.py +0 -16
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/version.py +1 -1
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/waveform.py +242 -107
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/waveform_parser.py +12 -13
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/PKG-INFO +1 -1
- {waveforms-2.1.1 → waveforms-2.2.1}/LICENSE +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/MANIFEST.in +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/README.md +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/pyproject.toml +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/setup.cfg +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/setup.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/src/waveform.h +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/tests/test_multi_drag.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/tests/test_waveform.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/tests/test_wavevstack.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/WaveformLexer.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/WaveformListener.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/WaveformParser.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/__main__.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms/utils.py +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/SOURCES.txt +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/entry_points.txt +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/requires.txt +0 -0
- {waveforms-2.1.1 → waveforms-2.2.1}/waveforms.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,8 @@ from .multy_drag import drag_sin, drag_sinx
|
|
|
4
4
|
from .version import __version__
|
|
5
5
|
from .waveform import (D, Waveform, WaveVStack, chirp, const, cos, cosh,
|
|
6
6
|
coshPulse, cosPulse, cut, drag, exp, function, gaussian,
|
|
7
|
-
general_cosine, hanning, interp, mixing,
|
|
8
|
-
registerBaseFunc, registerDerivative,
|
|
9
|
-
sign, sin, sinc, sinh, square, step, t,
|
|
7
|
+
general_cosine, hanning, interp, mixing, mollifier, one,
|
|
8
|
+
poly, registerBaseFunc, registerDerivative,
|
|
9
|
+
samplingPoints, sign, sin, sinc, sinh, square, step, t,
|
|
10
|
+
zero)
|
|
10
11
|
from .waveform_parser import wave_eval
|
|
@@ -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 = ...
|
|
@@ -28,7 +29,7 @@ def is_const(x: tuple[tuple, tuple]) -> bool:
|
|
|
28
29
|
pass
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def basic_wave(Type, *args, shift=0) -> tuple[tuple, tuple]:
|
|
32
|
+
def basic_wave(Type: int, *args, shift: float = 0) -> tuple[tuple, tuple]:
|
|
32
33
|
pass
|
|
33
34
|
|
|
34
35
|
|
|
@@ -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
|
|
|
@@ -103,6 +107,8 @@ HYPERBOLICCHIRP: int = ...
|
|
|
103
107
|
COSH: int = ...
|
|
104
108
|
SINH: int = ...
|
|
105
109
|
DRAG: int = ...
|
|
110
|
+
MOLLIFIER: int = ...
|
|
111
|
+
D_GAUSSIAN: int = ...
|
|
106
112
|
|
|
107
113
|
|
|
108
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
|
|
|
@@ -351,6 +356,19 @@ def _drag(t: np.ndarray, t0: float, freq: float, width: float, delta: float,
|
|
|
351
356
|
return Omega_x * np.cos(wt) + Omega_y * np.sin(wt)
|
|
352
357
|
|
|
353
358
|
|
|
359
|
+
def _mollifier(t: np.ndarray, r: float, d: int):
|
|
360
|
+
x = t / r
|
|
361
|
+
if d == 0:
|
|
362
|
+
return np.exp(1 / (np.abs(x)**2 - 1) + 1)
|
|
363
|
+
else:
|
|
364
|
+
p = np.poly1d([-2, 0])
|
|
365
|
+
for n in range(1, d):
|
|
366
|
+
p = np.poly1d([1, 0, -2, 0, 1]) * p.deriv() + np.poly1d(
|
|
367
|
+
[-4 * n, 0, 4 * n - 2, 0]) * p
|
|
368
|
+
return np.exp(1 / (np.abs(x)**2 - 1) +
|
|
369
|
+
1) * p(x) / (1 - x**2)**(2 * d) / r**d
|
|
370
|
+
|
|
371
|
+
|
|
354
372
|
LINEAR = registerBaseFunc(_LINEAR)
|
|
355
373
|
GAUSSIAN = registerBaseFunc(_GAUSSIAN)
|
|
356
374
|
ERF = registerBaseFunc(_ERF)
|
|
@@ -364,6 +382,8 @@ HYPERBOLICCHIRP = registerBaseFunc(_HYPERBOLICCHIRP)
|
|
|
364
382
|
COSH = registerBaseFunc(_COSH)
|
|
365
383
|
SINH = registerBaseFunc(_SINH)
|
|
366
384
|
DRAG = registerBaseFunc(_drag)
|
|
385
|
+
MOLLIFIER = registerBaseFunc(_mollifier)
|
|
386
|
+
D_GAUSSIAN = registerBaseFunc(_D_GAUSSIAN)
|
|
367
387
|
|
|
368
388
|
|
|
369
389
|
def _d_LINEAR(shift, *args):
|
|
@@ -433,6 +453,14 @@ def _d_HYPERBOLICCHIRP(shift, f0, k, phi0):
|
|
|
433
453
|
shift)), (-1, 1)), ), (2 * pi * f0, ))
|
|
434
454
|
|
|
435
455
|
|
|
456
|
+
def _d_MOLLIFIER(shift, r, d):
|
|
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, ))
|
|
462
|
+
|
|
463
|
+
|
|
436
464
|
# register derivative
|
|
437
465
|
registerDerivative(LINEAR, _d_LINEAR)
|
|
438
466
|
registerDerivative(GAUSSIAN, _d_GAUSSIAN)
|
|
@@ -446,6 +474,8 @@ registerDerivative(SINH, _d_SINH)
|
|
|
446
474
|
registerDerivative(LINEARCHIRP, _d_LINEARCHIRP)
|
|
447
475
|
registerDerivative(EXPONENTIALCHIRP, _d_EXPONENTIALCHIRP)
|
|
448
476
|
registerDerivative(HYPERBOLICCHIRP, _d_HYPERBOLICCHIRP)
|
|
477
|
+
registerDerivative(MOLLIFIER, _d_MOLLIFIER)
|
|
478
|
+
registerDerivative(D_GAUSSIAN, _d_D_GAUSSIAN)
|
|
449
479
|
|
|
450
480
|
|
|
451
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
|
+
__version__ = "2.2.1"
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from fractions import Fraction
|
|
4
|
+
from typing import Generator, Iterable, cast
|
|
2
5
|
|
|
3
6
|
import numpy as np
|
|
4
7
|
from numpy import e, inf, pi
|
|
8
|
+
from numpy.typing import NDArray
|
|
5
9
|
from scipy.signal import sosfilt
|
|
6
10
|
|
|
7
|
-
from ._waveform import (_D, COS, COSH, DRAG, ERF, EXP,
|
|
8
|
-
GAUSSIAN, HYPERBOLICCHIRP, INTERP,
|
|
9
|
-
|
|
10
|
-
_const, _half, _one, _zero,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
registerDerivative, shift,
|
|
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)
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
def _test_spec_num(num, spec):
|
|
@@ -124,13 +129,13 @@ class Waveform:
|
|
|
124
129
|
self.start = None
|
|
125
130
|
self.stop = None
|
|
126
131
|
self.sample_rate = None
|
|
127
|
-
self.filters = None
|
|
132
|
+
self.filters: tuple[np.ndarray, float] | None = None
|
|
128
133
|
self.label = None
|
|
129
134
|
|
|
130
135
|
@staticmethod
|
|
131
136
|
def _begin(bounds, seq):
|
|
132
137
|
for i, s in enumerate(seq):
|
|
133
|
-
if s
|
|
138
|
+
if s != _zero:
|
|
134
139
|
if i == 0:
|
|
135
140
|
return -inf
|
|
136
141
|
return bounds[i - 1]
|
|
@@ -140,7 +145,7 @@ class Waveform:
|
|
|
140
145
|
def _end(bounds, seq):
|
|
141
146
|
N = len(bounds)
|
|
142
147
|
for i, s in enumerate(seq[::-1]):
|
|
143
|
-
if s
|
|
148
|
+
if s != _zero:
|
|
144
149
|
if i == 0:
|
|
145
150
|
return inf
|
|
146
151
|
return bounds[N - i - 1]
|
|
@@ -160,12 +165,14 @@ class Waveform:
|
|
|
160
165
|
else:
|
|
161
166
|
return min(self.stop, self._end(self.bounds, self.seq))
|
|
162
167
|
|
|
163
|
-
def sample(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
def sample(
|
|
169
|
+
self,
|
|
170
|
+
sample_rate=None,
|
|
171
|
+
out: np.ndarray | None = None,
|
|
172
|
+
chunk_size=None,
|
|
173
|
+
function_lib=None,
|
|
174
|
+
filters: tuple[np.ndarray, float] | None = None
|
|
175
|
+
) -> np.ndarray | Iterable[np.ndarray]:
|
|
169
176
|
if sample_rate is None:
|
|
170
177
|
sample_rate = self.sample_rate
|
|
171
178
|
if self.start is None or self.stop is None or sample_rate is None:
|
|
@@ -176,7 +183,8 @@ class Waveform:
|
|
|
176
183
|
filters = self.filters
|
|
177
184
|
if chunk_size is None:
|
|
178
185
|
x = np.arange(self.start, self.stop, 1 / sample_rate)
|
|
179
|
-
sig =
|
|
186
|
+
sig = cast(np.ndarray,
|
|
187
|
+
self.__call__(x, out=out, function_lib=function_lib))
|
|
180
188
|
if filters is not None:
|
|
181
189
|
sos, initial = filters
|
|
182
190
|
if not isinstance(sos, np.ndarray):
|
|
@@ -184,27 +192,34 @@ class Waveform:
|
|
|
184
192
|
elif not sos.flags.writeable:
|
|
185
193
|
sos = sos.copy()
|
|
186
194
|
if initial:
|
|
187
|
-
sig = sosfilt(sos,
|
|
195
|
+
sig = cast(np.ndarray, sosfilt(sos,
|
|
196
|
+
sig - initial)) + initial
|
|
188
197
|
else:
|
|
189
|
-
sig = sosfilt(sos, sig)
|
|
190
|
-
return sig
|
|
198
|
+
sig = cast(np.ndarray, sosfilt(sos, sig))
|
|
199
|
+
return cast(np.ndarray, sig)
|
|
191
200
|
else:
|
|
192
201
|
return self._sample_iter(sample_rate, chunk_size, out,
|
|
193
202
|
function_lib, filters)
|
|
194
203
|
|
|
195
|
-
def _sample_iter(
|
|
196
|
-
|
|
197
|
-
|
|
204
|
+
def _sample_iter(
|
|
205
|
+
self, sample_rate, chunk_size, out: np.ndarray | None, function_lib,
|
|
206
|
+
filters: tuple[np.ndarray, float] | None
|
|
207
|
+
) -> Generator[np.ndarray, None, None]:
|
|
208
|
+
start = cast(float, self.start)
|
|
198
209
|
start_n = 0
|
|
199
210
|
if filters is not None:
|
|
200
211
|
sos, initial = filters
|
|
212
|
+
if not isinstance(sos, np.ndarray):
|
|
213
|
+
sos = np.array(sos)
|
|
214
|
+
elif not sos.flags.writeable:
|
|
215
|
+
sos = sos.copy()
|
|
201
216
|
# zi = sosfilt_zi(sos)
|
|
202
217
|
zi = np.zeros((sos.shape[0], 2))
|
|
203
218
|
length = chunk_size / sample_rate
|
|
204
|
-
while start < self.stop:
|
|
205
|
-
if start + length > self.stop:
|
|
206
|
-
length = self.stop - start
|
|
207
|
-
stop = self.stop
|
|
219
|
+
while start < cast(float, self.stop):
|
|
220
|
+
if start + length > cast(float, self.stop):
|
|
221
|
+
length = cast(float, self.stop) - start
|
|
222
|
+
stop = cast(float, self.stop)
|
|
208
223
|
size = round((stop - start) * sample_rate)
|
|
209
224
|
else:
|
|
210
225
|
stop = start + length
|
|
@@ -213,13 +228,17 @@ class Waveform:
|
|
|
213
228
|
|
|
214
229
|
if filters is None:
|
|
215
230
|
if out is not None:
|
|
216
|
-
yield
|
|
217
|
-
|
|
218
|
-
|
|
231
|
+
yield cast(
|
|
232
|
+
np.ndarray,
|
|
233
|
+
self.__call__(x,
|
|
234
|
+
out=out[start_n:],
|
|
235
|
+
function_lib=function_lib))
|
|
219
236
|
else:
|
|
220
|
-
yield
|
|
237
|
+
yield cast(np.ndarray,
|
|
238
|
+
self.__call__(x, function_lib=function_lib))
|
|
221
239
|
else:
|
|
222
|
-
sig =
|
|
240
|
+
sig = cast(np.ndarray,
|
|
241
|
+
self.__call__(x, function_lib=function_lib))
|
|
223
242
|
if initial:
|
|
224
243
|
sig -= initial
|
|
225
244
|
sig, zi = sosfilt(sos, sig, zi=zi)
|
|
@@ -227,7 +246,7 @@ class Waveform:
|
|
|
227
246
|
sig += initial
|
|
228
247
|
if out is not None:
|
|
229
248
|
out[start_n:start_n + size] = sig
|
|
230
|
-
yield sig
|
|
249
|
+
yield cast(np.ndarray, sig)
|
|
231
250
|
|
|
232
251
|
start = stop
|
|
233
252
|
start_n += chunk_size
|
|
@@ -379,22 +398,22 @@ class Waveform:
|
|
|
379
398
|
return Waveform(*merge_waveform(self.bounds, self.seq, other.bounds,
|
|
380
399
|
other.seq, oper))
|
|
381
400
|
|
|
382
|
-
def __pow__(self, n):
|
|
401
|
+
def __pow__(self, n) -> Waveform:
|
|
383
402
|
return Waveform(self.bounds, tuple(pow(w, n) for w in self.seq))
|
|
384
403
|
|
|
385
|
-
def __add__(self, other):
|
|
404
|
+
def __add__(self, other) -> Waveform:
|
|
386
405
|
if isinstance(other, Waveform):
|
|
387
406
|
return self._comb(other, add)
|
|
388
407
|
else:
|
|
389
408
|
return self + const(other)
|
|
390
409
|
|
|
391
|
-
def __radd__(self, v):
|
|
410
|
+
def __radd__(self, v) -> Waveform:
|
|
392
411
|
return const(v) + self
|
|
393
412
|
|
|
394
|
-
def __ior__(self, other):
|
|
413
|
+
def __ior__(self, other) -> Waveform:
|
|
395
414
|
return self | other
|
|
396
415
|
|
|
397
|
-
def __or__(self, other):
|
|
416
|
+
def __or__(self, other) -> Waveform:
|
|
398
417
|
if isinstance(other, (int, float, complex)):
|
|
399
418
|
other = const(other)
|
|
400
419
|
w = self.marker + other.marker
|
|
@@ -407,10 +426,10 @@ class Waveform:
|
|
|
407
426
|
|
|
408
427
|
return self._comb(other, _or)
|
|
409
428
|
|
|
410
|
-
def __iand__(self, other):
|
|
429
|
+
def __iand__(self, other) -> Waveform:
|
|
411
430
|
return self & other
|
|
412
431
|
|
|
413
|
-
def __and__(self, other):
|
|
432
|
+
def __and__(self, other) -> Waveform:
|
|
414
433
|
if isinstance(other, (int, float, complex)):
|
|
415
434
|
other = const(other)
|
|
416
435
|
w = self.marker + other.marker
|
|
@@ -429,7 +448,7 @@ class Waveform:
|
|
|
429
448
|
return Waveform(w.bounds,
|
|
430
449
|
tuple(_zero if s == _zero else _one for s in w.seq))
|
|
431
450
|
|
|
432
|
-
def mask(self, edge=0):
|
|
451
|
+
def mask(self, edge: float = 0) -> Waveform:
|
|
433
452
|
w = self.marker
|
|
434
453
|
in_wave = w.seq[0] == _zero
|
|
435
454
|
bounds = []
|
|
@@ -457,31 +476,31 @@ class Waveform:
|
|
|
457
476
|
bounds.append(b)
|
|
458
477
|
return Waveform(tuple(bounds), tuple(seq))
|
|
459
478
|
|
|
460
|
-
def __mul__(self, other):
|
|
479
|
+
def __mul__(self, other) -> Waveform:
|
|
461
480
|
if isinstance(other, Waveform):
|
|
462
481
|
return self._comb(other, mul)
|
|
463
482
|
else:
|
|
464
483
|
return self * const(other)
|
|
465
484
|
|
|
466
|
-
def __rmul__(self, v):
|
|
485
|
+
def __rmul__(self, v) -> Waveform:
|
|
467
486
|
return const(v) * self
|
|
468
487
|
|
|
469
|
-
def __truediv__(self, other):
|
|
488
|
+
def __truediv__(self, other) -> Waveform:
|
|
470
489
|
if isinstance(other, Waveform):
|
|
471
490
|
raise TypeError('division by waveform')
|
|
472
491
|
else:
|
|
473
492
|
return self * const(1 / other)
|
|
474
493
|
|
|
475
|
-
def __neg__(self):
|
|
494
|
+
def __neg__(self) -> Waveform:
|
|
476
495
|
return -1 * self
|
|
477
496
|
|
|
478
|
-
def __sub__(self, other):
|
|
497
|
+
def __sub__(self, other) -> Waveform:
|
|
479
498
|
return self + (-other)
|
|
480
499
|
|
|
481
|
-
def __rsub__(self, v):
|
|
500
|
+
def __rsub__(self, v) -> Waveform:
|
|
482
501
|
return v + (-self)
|
|
483
502
|
|
|
484
|
-
def __rshift__(self, time):
|
|
503
|
+
def __rshift__(self, time) -> Waveform:
|
|
485
504
|
return Waveform(
|
|
486
505
|
tuple(round(bound + time, NDIGITS) for bound in self.bounds),
|
|
487
506
|
tuple(shift(expr, time) for expr in self.seq))
|
|
@@ -502,16 +521,22 @@ class Waveform:
|
|
|
502
521
|
for start, stop, part in parts:
|
|
503
522
|
out[start:stop] += part
|
|
504
523
|
|
|
505
|
-
def __call__(
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
524
|
+
def __call__(
|
|
525
|
+
self,
|
|
526
|
+
x,
|
|
527
|
+
frag=False,
|
|
528
|
+
out: np.ndarray | list | None = None,
|
|
529
|
+
accumulate=False,
|
|
530
|
+
function_lib=None
|
|
531
|
+
) -> NDArray[np.float64 | np.complex128] | list[
|
|
532
|
+
tuple[int, int, NDArray[np.float64 | np.complex128]] | int
|
|
533
|
+
| float | complex] | np.float64:
|
|
511
534
|
if function_lib is None:
|
|
512
535
|
function_lib = _baseFunc
|
|
513
536
|
if isinstance(x, (int, float, complex)):
|
|
514
|
-
return
|
|
537
|
+
return cast(
|
|
538
|
+
NDArray[np.float64],
|
|
539
|
+
self.__call__(np.array([x]), function_lib=function_lib))[0]
|
|
515
540
|
parts, dtype = calc_parts(self.bounds, self.seq, x, function_lib,
|
|
516
541
|
self.min, self.max)
|
|
517
542
|
if not frag:
|
|
@@ -522,8 +547,9 @@ class Waveform:
|
|
|
522
547
|
self._fill_parts(parts, out)
|
|
523
548
|
else:
|
|
524
549
|
if out is None:
|
|
525
|
-
return parts
|
|
550
|
+
return cast(list, parts)
|
|
526
551
|
else:
|
|
552
|
+
out = cast(list, out)
|
|
527
553
|
if not accumulate:
|
|
528
554
|
out.clear()
|
|
529
555
|
out.extend(parts)
|
|
@@ -647,7 +673,8 @@ class WaveVStack(Waveform):
|
|
|
647
673
|
|
|
648
674
|
def __call__(self, x, frag=False, out=None, function_lib=None):
|
|
649
675
|
assert frag is False, 'WaveVStack does not support frag mode'
|
|
650
|
-
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)
|
|
651
678
|
if self.shift != 0:
|
|
652
679
|
x = x - self.shift
|
|
653
680
|
if function_lib is None:
|
|
@@ -732,7 +759,7 @@ class WaveVStack(Waveform):
|
|
|
732
759
|
ret.offset = self.offset
|
|
733
760
|
return ret
|
|
734
761
|
|
|
735
|
-
def __add__(self, other):
|
|
762
|
+
def __add__(self, other) -> WaveVStack:
|
|
736
763
|
ret = WaveVStack()
|
|
737
764
|
ret.wlist.extend(self.wlist)
|
|
738
765
|
if isinstance(other, WaveVStack):
|
|
@@ -750,10 +777,10 @@ class WaveVStack(Waveform):
|
|
|
750
777
|
ret.offset += other
|
|
751
778
|
return ret
|
|
752
779
|
|
|
753
|
-
def __radd__(self, v):
|
|
780
|
+
def __radd__(self, v) -> WaveVStack:
|
|
754
781
|
return self + v
|
|
755
782
|
|
|
756
|
-
def __mul__(self, other):
|
|
783
|
+
def __mul__(self, other) -> WaveVStack:
|
|
757
784
|
if isinstance(other, Waveform):
|
|
758
785
|
other = other.simplify() << self.shift
|
|
759
786
|
ret = WaveVStack([Waveform(*w) * other for w in self.wlist])
|
|
@@ -766,10 +793,10 @@ class WaveVStack(Waveform):
|
|
|
766
793
|
ret.offset = self.offset * other
|
|
767
794
|
return ret
|
|
768
795
|
|
|
769
|
-
def __rmul__(self, v):
|
|
796
|
+
def __rmul__(self, v) -> WaveVStack:
|
|
770
797
|
return self * v
|
|
771
798
|
|
|
772
|
-
def __eq__(self, other):
|
|
799
|
+
def __eq__(self, other) -> bool:
|
|
773
800
|
if self.wlist:
|
|
774
801
|
return False
|
|
775
802
|
else:
|
|
@@ -961,6 +988,42 @@ def _format_DRAG(shift, *args):
|
|
|
961
988
|
return f"DRAG(...)"
|
|
962
989
|
|
|
963
990
|
|
|
991
|
+
def _format_MOLLIFIER(shift, *args):
|
|
992
|
+
r = _num_latex(args[0])
|
|
993
|
+
d = _num_latex(args[1])
|
|
994
|
+
shift_str = _num_latex(-shift)
|
|
995
|
+
if shift_str == '0':
|
|
996
|
+
shift_str = ''
|
|
997
|
+
elif shift_str[0] != '-':
|
|
998
|
+
shift_str = '+' + shift_str
|
|
999
|
+
|
|
1000
|
+
if d == '0':
|
|
1001
|
+
return f"\\mathrm{{Mollifier}}\\left(t{shift_str}, r={r}\\right)"
|
|
1002
|
+
elif d == '1':
|
|
1003
|
+
return f"\\mathrm{{Mollifier}}'\\left(t{shift_str}, r={r}\\right)"
|
|
1004
|
+
elif d == '2':
|
|
1005
|
+
return f"\\mathrm{{Mollifier}}''\\left(t{shift_str}, r={r}\\right)"
|
|
1006
|
+
else:
|
|
1007
|
+
return f"\\mathrm{{Mollifier}}^{{({d})}}\\left(t{shift_str}, r={r}\\right)"
|
|
1008
|
+
|
|
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
|
+
|
|
964
1027
|
registerBaseFuncLatex(LINEAR, _format_LINEAR)
|
|
965
1028
|
registerBaseFuncLatex(GAUSSIAN, _format_GAUSSIAN)
|
|
966
1029
|
registerBaseFuncLatex(ERF, _format_ERF)
|
|
@@ -970,12 +1033,27 @@ registerBaseFuncLatex(EXP, _format_EXP)
|
|
|
970
1033
|
registerBaseFuncLatex(COSH, _format_COSH)
|
|
971
1034
|
registerBaseFuncLatex(SINH, _format_SINH)
|
|
972
1035
|
registerBaseFuncLatex(DRAG, _format_DRAG)
|
|
1036
|
+
registerBaseFuncLatex(MOLLIFIER, _format_MOLLIFIER)
|
|
1037
|
+
registerBaseFuncLatex(D_GAUSSIAN, _format_D_GAUSSIAN)
|
|
973
1038
|
|
|
974
1039
|
|
|
975
|
-
def D(wav):
|
|
1040
|
+
def D(wav: Waveform, d: int = 1) -> Waveform:
|
|
976
1041
|
"""derivative
|
|
1042
|
+
|
|
1043
|
+
Parameters
|
|
1044
|
+
----------
|
|
1045
|
+
wav : Waveform
|
|
1046
|
+
The waveform to take the derivative of.
|
|
1047
|
+
d : int, optional
|
|
1048
|
+
The order of the derivative, by default 1.
|
|
977
1049
|
"""
|
|
978
|
-
|
|
1050
|
+
assert d >= 0 and isinstance(d, int), "d must be a non-negative integer"
|
|
1051
|
+
if d == 0:
|
|
1052
|
+
return wav
|
|
1053
|
+
elif d == 1:
|
|
1054
|
+
return Waveform(bounds=wav.bounds, seq=tuple(_D(x) for x in wav.seq))
|
|
1055
|
+
else:
|
|
1056
|
+
return D(D(wav, d - 1), 1)
|
|
979
1057
|
|
|
980
1058
|
|
|
981
1059
|
def convolve(a, b):
|
|
@@ -1014,7 +1092,7 @@ def step(edge, type='erf'):
|
|
|
1014
1092
|
seq=(_zero, rise, _one))
|
|
1015
1093
|
|
|
1016
1094
|
|
|
1017
|
-
def square(width, edge=0, type='erf'):
|
|
1095
|
+
def square(width: float, edge: float = 0, type: str = 'erf') -> Waveform:
|
|
1018
1096
|
if width <= 0:
|
|
1019
1097
|
return zero()
|
|
1020
1098
|
if edge == 0:
|
|
@@ -1027,7 +1105,9 @@ def square(width, edge=0, type='erf'):
|
|
|
1027
1105
|
(step(edge, type=type) >> width / 2))
|
|
1028
1106
|
|
|
1029
1107
|
|
|
1030
|
-
def gaussian(width,
|
|
1108
|
+
def gaussian(width: float,
|
|
1109
|
+
plateau: float = 0.0,
|
|
1110
|
+
d: int | None = None) -> Waveform:
|
|
1031
1111
|
if width <= 0 and plateau <= 0.0:
|
|
1032
1112
|
return zero()
|
|
1033
1113
|
# width is two times FWHM
|
|
@@ -1035,26 +1115,27 @@ def gaussian(width, plateau=0.0):
|
|
|
1035
1115
|
std_sq2 = width / 3.3302184446307908
|
|
1036
1116
|
# std is set to give total pulse area same as a square
|
|
1037
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
|
+
|
|
1038
1123
|
if round(0.5 * plateau, NDIGITS) <= 0.0:
|
|
1039
1124
|
return Waveform(bounds=(round(-0.75 * width,
|
|
1040
1125
|
NDIGITS), round(0.75 * width,
|
|
1041
1126
|
NDIGITS), +inf),
|
|
1042
|
-
seq=(_zero,
|
|
1127
|
+
seq=(_zero, base(0), _zero))
|
|
1043
1128
|
else:
|
|
1044
1129
|
return Waveform(bounds=(round(-0.75 * width - 0.5 * plateau,
|
|
1045
1130
|
NDIGITS), round(-0.5 * plateau, NDIGITS),
|
|
1046
1131
|
round(0.5 * plateau, NDIGITS),
|
|
1047
1132
|
round(0.75 * width + 0.5 * plateau,
|
|
1048
1133
|
NDIGITS), +inf),
|
|
1049
|
-
seq=(_zero,
|
|
1050
|
-
|
|
1051
|
-
std_sq2,
|
|
1052
|
-
shift=-0.5 * plateau), _one,
|
|
1053
|
-
basic_wave(GAUSSIAN, std_sq2,
|
|
1054
|
-
shift=0.5 * plateau), _zero))
|
|
1134
|
+
seq=(_zero, base(-0.5 * plateau), _one,
|
|
1135
|
+
base(0.5 * plateau), _zero))
|
|
1055
1136
|
|
|
1056
1137
|
|
|
1057
|
-
def cos(w, phi=0):
|
|
1138
|
+
def cos(w: float, phi: float = 0) -> Waveform:
|
|
1058
1139
|
if w == 0:
|
|
1059
1140
|
return const(np.cos(phi))
|
|
1060
1141
|
if w < 0:
|
|
@@ -1063,7 +1144,7 @@ def cos(w, phi=0):
|
|
|
1063
1144
|
return Waveform(seq=(basic_wave(COS, w, shift=-phi / w), ))
|
|
1064
1145
|
|
|
1065
1146
|
|
|
1066
|
-
def sin(w, phi=0):
|
|
1147
|
+
def sin(w: float, phi: float = 0) -> Waveform:
|
|
1067
1148
|
if w == 0:
|
|
1068
1149
|
return const(np.sin(phi))
|
|
1069
1150
|
if w < 0:
|
|
@@ -1072,7 +1153,7 @@ def sin(w, phi=0):
|
|
|
1072
1153
|
return Waveform(seq=(basic_wave(COS, w, shift=(pi / 2 - phi) / w), ))
|
|
1073
1154
|
|
|
1074
1155
|
|
|
1075
|
-
def exp(alpha):
|
|
1156
|
+
def exp(alpha: float | complex) -> Waveform:
|
|
1076
1157
|
if isinstance(alpha, complex):
|
|
1077
1158
|
if alpha.real == 0:
|
|
1078
1159
|
return cos(alpha.imag) + 1j * sin(alpha.imag)
|
|
@@ -1082,7 +1163,7 @@ def exp(alpha):
|
|
|
1082
1163
|
return Waveform(seq=(basic_wave(EXP, alpha), ))
|
|
1083
1164
|
|
|
1084
1165
|
|
|
1085
|
-
def sinc(bw):
|
|
1166
|
+
def sinc(bw: float) -> Waveform:
|
|
1086
1167
|
if bw <= 0:
|
|
1087
1168
|
return zero()
|
|
1088
1169
|
width = 100 / bw
|
|
@@ -1091,7 +1172,7 @@ def sinc(bw):
|
|
|
1091
1172
|
seq=(_zero, basic_wave(SINC, bw), _zero))
|
|
1092
1173
|
|
|
1093
1174
|
|
|
1094
|
-
def cosPulse(width, plateau=0.0):
|
|
1175
|
+
def cosPulse(width: float, plateau: float = 0.0) -> Waveform:
|
|
1095
1176
|
# cos = basic_wave(COS, 2*np.pi/width)
|
|
1096
1177
|
# pulse = mul(add(cos, _one), _half)
|
|
1097
1178
|
if round(0.5 * plateau, NDIGITS) > 0:
|
|
@@ -1105,19 +1186,21 @@ def cosPulse(width, plateau=0.0):
|
|
|
1105
1186
|
seq=(_zero, pulse, _zero))
|
|
1106
1187
|
|
|
1107
1188
|
|
|
1108
|
-
def hanning(width, plateau=0.0):
|
|
1189
|
+
def hanning(width: float, plateau: float = 0.0) -> Waveform:
|
|
1109
1190
|
return cosPulse(width, plateau=plateau)
|
|
1110
1191
|
|
|
1111
1192
|
|
|
1112
|
-
def cosh(w):
|
|
1193
|
+
def cosh(w: float) -> Waveform:
|
|
1113
1194
|
return Waveform(seq=(basic_wave(COSH, w), ))
|
|
1114
1195
|
|
|
1115
1196
|
|
|
1116
|
-
def sinh(w):
|
|
1197
|
+
def sinh(w: float) -> Waveform:
|
|
1117
1198
|
return Waveform(seq=(basic_wave(SINH, w), ))
|
|
1118
1199
|
|
|
1119
1200
|
|
|
1120
|
-
def coshPulse(width
|
|
1201
|
+
def coshPulse(width: float,
|
|
1202
|
+
eps: float = 1.0,
|
|
1203
|
+
plateau: float = 0.0) -> Waveform:
|
|
1121
1204
|
"""Cosine hyperbolic pulse with the following im
|
|
1122
1205
|
|
|
1123
1206
|
pulse edge shape:
|
|
@@ -1167,24 +1250,58 @@ def coshPulse(width, eps=1.0, plateau=0.0):
|
|
|
1167
1250
|
seq=(_zero, raising, _one, falling, _zero))
|
|
1168
1251
|
|
|
1169
1252
|
|
|
1170
|
-
def general_cosine(duration, *arg):
|
|
1253
|
+
def general_cosine(duration: float, *arg: float) -> Waveform:
|
|
1171
1254
|
wav = zero()
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
for i, a in enumerate(
|
|
1255
|
+
arg_ = np.asarray(arg)
|
|
1256
|
+
arg_ /= arg_[::2].sum()
|
|
1257
|
+
for i, a in enumerate(arg_, start=1):
|
|
1175
1258
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1176
1259
|
return wav * square(duration)
|
|
1177
1260
|
|
|
1178
1261
|
|
|
1179
|
-
def slepian(duration, *arg):
|
|
1262
|
+
def slepian(duration: float, *arg: float) -> Waveform:
|
|
1180
1263
|
wav = zero()
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
for i, a in enumerate(
|
|
1264
|
+
arg_ = np.asarray(arg)
|
|
1265
|
+
arg_ /= arg_[::2].sum()
|
|
1266
|
+
for i, a in enumerate(arg_, start=1):
|
|
1184
1267
|
wav += a / 2 * (1 - (-1)**i * cos(i * 2 * pi / duration))
|
|
1185
1268
|
return wav * square(duration)
|
|
1186
1269
|
|
|
1187
1270
|
|
|
1271
|
+
def mollifier(width: float, plateau: float = 0.0, d: int = 0) -> Waveform:
|
|
1272
|
+
"""
|
|
1273
|
+
Mollifier function is a smooth function that is 1 at the origin and 0 outside a certain radius.
|
|
1274
|
+
It is defined as:
|
|
1275
|
+
|
|
1276
|
+
f(x) = exp(1 / ((x / r) ^ 2 - 1) + 1) in case |x| < r
|
|
1277
|
+
= 0 in case |x| >= r
|
|
1278
|
+
where r = width / 2 is the radius of the mollifier.
|
|
1279
|
+
|
|
1280
|
+
The parameter plateau is the width of the plateau.
|
|
1281
|
+
The parameter d is the order of the derivative.
|
|
1282
|
+
"""
|
|
1283
|
+
assert d >= 0 and isinstance(d, int), "d must be a non-negative integer"
|
|
1284
|
+
assert width > 0, "width must be positive"
|
|
1285
|
+
|
|
1286
|
+
if plateau <= 0:
|
|
1287
|
+
return Waveform(bounds=(-0.5 * width, 0.5 * width, inf),
|
|
1288
|
+
seq=(_zero, basic_wave(MOLLIFIER, width / 2,
|
|
1289
|
+
d), _zero))
|
|
1290
|
+
else:
|
|
1291
|
+
return Waveform(bounds=(-0.5 * width - 0.5 * plateau, -0.5 * plateau,
|
|
1292
|
+
0.5 * plateau, 0.5 * width + 0.5 * plateau,
|
|
1293
|
+
inf),
|
|
1294
|
+
seq=(_zero,
|
|
1295
|
+
basic_wave(MOLLIFIER,
|
|
1296
|
+
width / 2,
|
|
1297
|
+
d,
|
|
1298
|
+
shift=-0.5 * plateau), _one,
|
|
1299
|
+
basic_wave(MOLLIFIER,
|
|
1300
|
+
width / 2,
|
|
1301
|
+
d,
|
|
1302
|
+
shift=0.5 * plateau), _zero))
|
|
1303
|
+
|
|
1304
|
+
|
|
1188
1305
|
def _poly(*a):
|
|
1189
1306
|
"""
|
|
1190
1307
|
a[0] + a[1] * t + a[2] * t**2 + ...
|
|
@@ -1212,7 +1329,13 @@ def t():
|
|
|
1212
1329
|
return Waveform(seq=((((LINEAR, 0), ), (1, )), (1, )))
|
|
1213
1330
|
|
|
1214
1331
|
|
|
1215
|
-
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:
|
|
1216
1339
|
phase += pi * delta * (width + plateau)
|
|
1217
1340
|
if plateau <= 0:
|
|
1218
1341
|
return Waveform(seq=(_zero,
|
|
@@ -1241,7 +1364,11 @@ def drag(freq, width, plateau=0, delta=0, block_freq=None, phase=0, t0=0):
|
|
|
1241
1364
|
NDIGITS), +inf))
|
|
1242
1365
|
|
|
1243
1366
|
|
|
1244
|
-
def chirp(f0
|
|
1367
|
+
def chirp(f0: float,
|
|
1368
|
+
f1: float,
|
|
1369
|
+
T: float,
|
|
1370
|
+
phi0: float = 0,
|
|
1371
|
+
type: str = 'linear') -> Waveform:
|
|
1245
1372
|
"""
|
|
1246
1373
|
A chirp is a signal in which the frequency increases (up-chirp)
|
|
1247
1374
|
or decreases (down-chirp) with time. In some sources, the term
|
|
@@ -1280,7 +1407,7 @@ def chirp(f0, f1, T, phi0=0, type='linear'):
|
|
|
1280
1407
|
raise ValueError(f'unknown type {type}')
|
|
1281
1408
|
|
|
1282
1409
|
|
|
1283
|
-
def interp(x, y):
|
|
1410
|
+
def interp(x: NDArray[np.float64], y: NDArray[np.float64]) -> Waveform:
|
|
1284
1411
|
seq, bounds = [_zero], [x[0]]
|
|
1285
1412
|
for x1, x2, y1, y2 in zip(x[:-1], x[1:], y[:-1], y[1:]):
|
|
1286
1413
|
if x2 == x1:
|
|
@@ -1298,12 +1425,20 @@ def interp(x, y):
|
|
|
1298
1425
|
for b in bounds)).simplify()
|
|
1299
1426
|
|
|
1300
1427
|
|
|
1301
|
-
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:
|
|
1302
1435
|
offset = 0
|
|
1303
1436
|
if start is not None and head is not None:
|
|
1304
|
-
offset = head - wav(np.array([1.0 * start
|
|
1437
|
+
offset = head - cast(NDArray[np.float64], wav(np.array([1.0 * start
|
|
1438
|
+
])))[0]
|
|
1305
1439
|
elif stop is not None and tail is not None:
|
|
1306
|
-
offset = tail - wav(np.array([1.0 * stop
|
|
1440
|
+
offset = tail - cast(NDArray[np.float64], wav(np.array([1.0 * stop
|
|
1441
|
+
])))[0]
|
|
1307
1442
|
wav = wav + offset
|
|
1308
1443
|
|
|
1309
1444
|
if start is not None:
|
|
@@ -1334,15 +1469,15 @@ def samplingPoints(start, stop, points):
|
|
|
1334
1469
|
tuple(points)), _zero))
|
|
1335
1470
|
|
|
1336
1471
|
|
|
1337
|
-
def mixing(I,
|
|
1338
|
-
Q=None,
|
|
1472
|
+
def mixing(I: Waveform,
|
|
1473
|
+
Q: Waveform | None = None,
|
|
1339
1474
|
*,
|
|
1340
|
-
phase=0.0,
|
|
1341
|
-
freq=0.0,
|
|
1342
|
-
ratioIQ=1.0,
|
|
1343
|
-
phaseDiff=0.0,
|
|
1344
|
-
block_freq=None,
|
|
1345
|
-
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]:
|
|
1346
1481
|
"""SSB or envelope mixing
|
|
1347
1482
|
"""
|
|
1348
1483
|
if Q is None:
|
|
@@ -1356,8 +1491,8 @@ def mixing(I,
|
|
|
1356
1491
|
Qout = -I * sin(w, -phase + phaseDiff) + Q * cos(w, -phase + phaseDiff)
|
|
1357
1492
|
else:
|
|
1358
1493
|
# envelope mixing
|
|
1359
|
-
Iout = I * np.cos(-phase) + Q * np.sin(-phase)
|
|
1360
|
-
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))
|
|
1361
1496
|
|
|
1362
1497
|
# apply DRAG
|
|
1363
1498
|
if block_freq is not None and block_freq != freq:
|
|
@@ -1380,7 +1515,7 @@ def mixing(I,
|
|
|
1380
1515
|
__all__ = [
|
|
1381
1516
|
'D', 'Waveform', 'chirp', 'const', 'cos', 'cosh', 'coshPulse', 'cosPulse',
|
|
1382
1517
|
'cut', 'drag', 'exp', 'function', 'gaussian', 'general_cosine', 'hanning',
|
|
1383
|
-
'interp', 'mixing', 'one', 'poly', 'registerBaseFunc',
|
|
1518
|
+
'interp', 'mixing', 'mollifier', 'one', 'poly', 'registerBaseFunc',
|
|
1384
1519
|
'registerDerivative', 'samplingPoints', 'sign', 'sin', 'sinc', 'sinh',
|
|
1385
1520
|
'square', 'step', 't', 'zero'
|
|
1386
1521
|
]
|
|
@@ -4,7 +4,7 @@ from ast import literal_eval
|
|
|
4
4
|
from functools import lru_cache
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from antlr4 import
|
|
7
|
+
from antlr4 import CommonTokenStream, InputStream
|
|
8
8
|
from antlr4.error.ErrorListener import ErrorListener
|
|
9
9
|
|
|
10
10
|
from . import multy_drag, waveform
|
|
@@ -30,9 +30,9 @@ class WaveformVisitor:
|
|
|
30
30
|
self.functions = [
|
|
31
31
|
'D', 'chirp', 'const', 'cos', 'cosh', 'coshPulse', 'cosPulse',
|
|
32
32
|
'cut', 'drag', 'drag_sin', 'drag_sinx', 'exp', 'gaussian',
|
|
33
|
-
'general_cosine', 'hanning', 'interp', 'mixing', '
|
|
34
|
-
'
|
|
35
|
-
't', 'zero'
|
|
33
|
+
'general_cosine', 'hanning', 'interp', 'mixing', 'mollifier',
|
|
34
|
+
'one', 'poly', 'samplingPoints', 'sign', 'sin', 'sinc', 'sinh',
|
|
35
|
+
'square', 'step', 't', 'zero'
|
|
36
36
|
]
|
|
37
37
|
self.constants = {
|
|
38
38
|
'pi': waveform.pi,
|
|
@@ -226,14 +226,13 @@ def _generate_antlr_parser():
|
|
|
226
226
|
|
|
227
227
|
# Generate ANTLR files
|
|
228
228
|
try:
|
|
229
|
-
result = subprocess.run(
|
|
230
|
-
"antlr4", "-Dlanguage=Python3",
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
check=True)
|
|
229
|
+
result = subprocess.run(
|
|
230
|
+
["antlr4", "-Dlanguage=Python3",
|
|
231
|
+
str(grammar_file)],
|
|
232
|
+
cwd=str(current_dir),
|
|
233
|
+
capture_output=True,
|
|
234
|
+
text=True,
|
|
235
|
+
check=True)
|
|
237
236
|
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
238
237
|
# Fall back to java command if antlr4 command is not available
|
|
239
238
|
try:
|
|
@@ -258,7 +257,7 @@ def parse_waveform_expression(expr: str) -> waveform.Waveform:
|
|
|
258
257
|
try:
|
|
259
258
|
# Generate parser files if they don't exist
|
|
260
259
|
# _generate_antlr_parser()
|
|
261
|
-
|
|
260
|
+
|
|
262
261
|
# Import generated ANTLR classes
|
|
263
262
|
from .WaveformLexer import WaveformLexer
|
|
264
263
|
from .WaveformParser import WaveformParser
|
|
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
|