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.
Files changed (30) hide show
  1. {waveforms-2.2.0 → waveforms-2.2.2}/PKG-INFO +1 -1
  2. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/_waveform.pyi +11 -6
  3. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/_waveform.pyx +17 -3
  4. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/distortion.py +39 -25
  5. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/multy_drag.py +0 -16
  6. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/version.py +1 -1
  7. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/waveform.py +136 -77
  8. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/waveform_parser.py +1 -1
  9. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/PKG-INFO +1 -1
  10. {waveforms-2.2.0 → waveforms-2.2.2}/LICENSE +0 -0
  11. {waveforms-2.2.0 → waveforms-2.2.2}/MANIFEST.in +0 -0
  12. {waveforms-2.2.0 → waveforms-2.2.2}/README.md +0 -0
  13. {waveforms-2.2.0 → waveforms-2.2.2}/pyproject.toml +0 -0
  14. {waveforms-2.2.0 → waveforms-2.2.2}/setup.cfg +0 -0
  15. {waveforms-2.2.0 → waveforms-2.2.2}/setup.py +0 -0
  16. {waveforms-2.2.0 → waveforms-2.2.2}/src/waveform.h +0 -0
  17. {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_multi_drag.py +0 -0
  18. {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_waveform.py +0 -0
  19. {waveforms-2.2.0 → waveforms-2.2.2}/tests/test_wavevstack.py +0 -0
  20. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformLexer.py +0 -0
  21. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformListener.py +0 -0
  22. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/WaveformParser.py +0 -0
  23. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/__init__.py +0 -0
  24. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/__main__.py +0 -0
  25. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms/utils.py +0 -0
  26. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/SOURCES.txt +0 -0
  27. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/dependency_links.txt +0 -0
  28. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/entry_points.txt +0 -0
  29. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/requires.txt +0 -0
  30. {waveforms-2.2.0 → waveforms-2.2.2}/waveforms.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Edit waveforms used in experiment
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -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(bounds: tuple,
52
- seq: tuple,
53
- x: np.ndarray,
54
- function_lib: dict,
55
- min=-inf,
56
- max=inf) -> tuple[list[tuple[int, int, np.ndarray]], type]:
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.exp(1 / (np.abs(x)**2 - 1) + 1)
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.exp(1 / (np.abs(x)**2 - 1) + 1) * p(x) / (1 - x**2)**(2 * d) / r**d
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 repeat, zip_longest
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(amp: float | Sequence[float],
102
- tau: float | Sequence[float],
103
- sample_rate: float,
104
- inv: bool = False,
105
- output='ba') -> tuple[np.ndarray, np.ndarray]:
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 = numerator(0) / denominator(0) * np.prod(1 - p) / np.prod(1 - z)
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 zpk2tf(z, p, k)
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 = exp_decay_filter(amp, tau, sample_rate)
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(sig: np.ndarray,
266
- filters: list = None,
267
- ker: np.ndarray = None,
268
- initial: float = 0.0,
269
- initial_x: np.ndarray | None = None,
270
- initial_y: np.ndarray | None = None,
271
- zi: np.ndarray | None = None,
272
- return_zf: bool = False) -> np.ndarray:
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 = exp_decay_filter(amp, abs(tau), sample_rate)
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.0"
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
- _D, COS, COSH, DRAG, ERF, EXP, EXPONENTIALCHIRP, GAUSSIAN, HYPERBOLICCHIRP,
11
- INTERP, LINEAR, LINEARCHIRP, MOLLIFIER, NDIGITS, SINC, SINH, _baseFunc,
12
- _baseFunc_latex, _const, _half, _one, _zero, add, basic_wave, calc_parts,
13
- filter, is_const, merge_waveform, mul, pow, registerBaseFunc,
14
- registerBaseFuncLatex, registerDerivative, shift, simplify, wave_sum)
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 is not _zero:
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 is not _zero:
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 = self.__call__(x, out=out, function_lib=function_lib)
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[tuple[int, int,
527
- NDArray[np.float64]]] | np.float64:
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=complex)
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, plateau=0.0):
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, basic_wave(GAUSSIAN, std_sq2), _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
- basic_wave(GAUSSIAN,
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, eps=1.0, plateau=0.0):
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
- arg = np.asarray(arg)
1225
- arg /= arg[::2].sum()
1226
- for i, a in enumerate(arg, start=1):
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
- arg = np.asarray(arg)
1234
- arg /= arg[::2].sum()
1235
- for i, a in enumerate(arg, start=1):
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, width, plateau=0, delta=0, block_freq=None, phase=0, t0=0):
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, f1, T, phi0=0, type='linear'):
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, start=None, stop=None, head=None, tail=None, min=None, max=None):
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]))[0]
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]))[0]
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:
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Edit waveforms used in experiment
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes