waveforms 2.1.0__tar.gz → 2.2.0__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.1.0 → waveforms-2.2.0}/PKG-INFO +1 -1
  2. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/__init__.py +4 -3
  3. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/_waveform.pyi +3 -2
  4. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/_waveform.pyx +18 -0
  5. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/utils.py +14 -12
  6. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/version.py +1 -1
  7. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/waveform.py +130 -40
  8. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/waveform_parser.py +11 -12
  9. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms.egg-info/PKG-INFO +1 -1
  10. {waveforms-2.1.0 → waveforms-2.2.0}/LICENSE +0 -0
  11. {waveforms-2.1.0 → waveforms-2.2.0}/MANIFEST.in +0 -0
  12. {waveforms-2.1.0 → waveforms-2.2.0}/README.md +0 -0
  13. {waveforms-2.1.0 → waveforms-2.2.0}/pyproject.toml +0 -0
  14. {waveforms-2.1.0 → waveforms-2.2.0}/setup.cfg +0 -0
  15. {waveforms-2.1.0 → waveforms-2.2.0}/setup.py +0 -0
  16. {waveforms-2.1.0 → waveforms-2.2.0}/src/waveform.h +0 -0
  17. {waveforms-2.1.0 → waveforms-2.2.0}/tests/test_multi_drag.py +0 -0
  18. {waveforms-2.1.0 → waveforms-2.2.0}/tests/test_waveform.py +0 -0
  19. {waveforms-2.1.0 → waveforms-2.2.0}/tests/test_wavevstack.py +0 -0
  20. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/WaveformLexer.py +0 -0
  21. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/WaveformListener.py +0 -0
  22. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/WaveformParser.py +0 -0
  23. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/__main__.py +0 -0
  24. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/distortion.py +0 -0
  25. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms/multy_drag.py +0 -0
  26. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms.egg-info/SOURCES.txt +0 -0
  27. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms.egg-info/dependency_links.txt +0 -0
  28. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms.egg-info/entry_points.txt +0 -0
  29. {waveforms-2.1.0 → waveforms-2.2.0}/waveforms.egg-info/requires.txt +0 -0
  30. {waveforms-2.1.0 → waveforms-2.2.0}/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.1.0
3
+ Version: 2.2.0
4
4
  Summary: Edit waveforms used in experiment
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -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, one, poly,
8
- registerBaseFunc, registerDerivative, samplingPoints,
9
- sign, sin, sinc, sinh, square, step, t, zero)
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
@@ -28,7 +28,7 @@ def is_const(x: tuple[tuple, tuple]) -> bool:
28
28
  pass
29
29
 
30
30
 
31
- def basic_wave(Type, *args, shift=0) -> tuple[tuple, tuple]:
31
+ def basic_wave(Type: int, *args, shift: float = 0) -> tuple[tuple, tuple]:
32
32
  pass
33
33
 
34
34
 
@@ -53,7 +53,7 @@ def calc_parts(bounds: tuple,
53
53
  x: np.ndarray,
54
54
  function_lib: dict,
55
55
  min=-inf,
56
- max=inf) -> tuple[list[np.ndarray], type]:
56
+ max=inf) -> tuple[list[tuple[int, int, np.ndarray]], type]:
57
57
  pass
58
58
 
59
59
 
@@ -103,6 +103,7 @@ HYPERBOLICCHIRP: int = ...
103
103
  COSH: int = ...
104
104
  SINH: int = ...
105
105
  DRAG: int = ...
106
+ MOLLIFIER: int = ...
106
107
 
107
108
 
108
109
  def simplify(expr: tuple[tuple, tuple], eps: float) -> tuple[tuple, tuple]:
@@ -351,6 +351,18 @@ def _drag(t: np.ndarray, t0: float, freq: float, width: float, delta: float,
351
351
  return Omega_x * np.cos(wt) + Omega_y * np.sin(wt)
352
352
 
353
353
 
354
+ def _mollifier(t: np.ndarray, r: float, d: int):
355
+ x = t / r
356
+ if d == 0:
357
+ return np.exp(1 / (np.abs(x)**2 - 1) + 1)
358
+ else:
359
+ p = np.poly1d([-2, 0])
360
+ for n in range(1, d):
361
+ p = np.poly1d([1, 0, -2, 0, 1]) * p.deriv() + np.poly1d(
362
+ [-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
364
+
365
+
354
366
  LINEAR = registerBaseFunc(_LINEAR)
355
367
  GAUSSIAN = registerBaseFunc(_GAUSSIAN)
356
368
  ERF = registerBaseFunc(_ERF)
@@ -364,6 +376,7 @@ HYPERBOLICCHIRP = registerBaseFunc(_HYPERBOLICCHIRP)
364
376
  COSH = registerBaseFunc(_COSH)
365
377
  SINH = registerBaseFunc(_SINH)
366
378
  DRAG = registerBaseFunc(_drag)
379
+ MOLLIFIER = registerBaseFunc(_mollifier)
367
380
 
368
381
 
369
382
  def _d_LINEAR(shift, *args):
@@ -433,6 +446,10 @@ def _d_HYPERBOLICCHIRP(shift, f0, k, phi0):
433
446
  shift)), (-1, 1)), ), (2 * pi * f0, ))
434
447
 
435
448
 
449
+ def _d_MOLLIFIER(shift, r, d):
450
+ return (((((MOLLIFIER, r, d+1, shift), ), (1, )), ), (1, ))
451
+
452
+
436
453
  # register derivative
437
454
  registerDerivative(LINEAR, _d_LINEAR)
438
455
  registerDerivative(GAUSSIAN, _d_GAUSSIAN)
@@ -446,6 +463,7 @@ registerDerivative(SINH, _d_SINH)
446
463
  registerDerivative(LINEARCHIRP, _d_LINEARCHIRP)
447
464
  registerDerivative(EXPONENTIALCHIRP, _d_EXPONENTIALCHIRP)
448
465
  registerDerivative(HYPERBOLICCHIRP, _d_HYPERBOLICCHIRP)
466
+ registerDerivative(MOLLIFIER, _d_MOLLIFIER)
449
467
 
450
468
 
451
469
  def _cos_power_n(x, n):
@@ -1,6 +1,6 @@
1
1
  from itertools import repeat
2
2
  from types import MappingProxyType
3
- from typing import Optional, Sequence
3
+ from typing import Optional, Sequence, cast
4
4
 
5
5
  import numpy as np
6
6
  import scipy.sparse as sp
@@ -20,13 +20,13 @@ def freeze(x):
20
20
  elif isinstance(x, (np.ndarray, np.matrix)):
21
21
  x.flags.writeable = False
22
22
  elif isinstance(x, sp.spmatrix):
23
- x.data.flags.writeable = False
24
- if x.format in {'csr', 'csc', 'bsr'}:
25
- x.indices.flags.writeable = False
26
- x.indptr.flags.writeable = False
27
- elif x.format == 'coo':
28
- x.row.flags.writeable = False
29
- x.col.flags.writeable = False
23
+ cast(np.ndarray, getattr(x, 'data')).flags.writeable = False
24
+ if getattr(x, 'format') in {'csr', 'csc', 'bsr'}:
25
+ cast(np.ndarray, getattr(x, 'indices')).flags.writeable = False
26
+ cast(np.ndarray, getattr(x, 'indptr')).flags.writeable = False
27
+ elif getattr(x, 'format') == 'coo':
28
+ cast(np.ndarray, getattr(x, 'row')).flags.writeable = False
29
+ cast(np.ndarray, getattr(x, 'col')).flags.writeable = False
30
30
  elif isinstance(x, bytearray):
31
31
  x = bytes(x)
32
32
  return x
@@ -72,12 +72,14 @@ def getFTMatrix(fList: Sequence[float],
72
72
  if weight is None or len(weight) == 0:
73
73
  weight = np.full(numOfPoints, 2 / numOfPoints)
74
74
  if phaseList is None or len(phaseList) == 0:
75
- phaseList = np.zeros_like(fList)
75
+ phase_list = np.zeros_like(fList)
76
+ else:
77
+ phase_list = phaseList
76
78
  if weight.ndim == 1:
77
- weightList = repeat(weight)
79
+ weight_list = repeat(weight)
78
80
  else:
79
- weightList = weight
80
- for f, phase, weight in zip(fList, phaseList, weightList):
81
+ weight_list = weight
82
+ for f, phase, weight in zip(fList, phase_list, weight_list):
81
83
  e.append(weight * np.exp(-1j * (2 * np.pi * f * t + phase)))
82
84
  return np.asarray(e).T
83
85
 
@@ -1,2 +1,2 @@
1
1
  """Define version number here and read it from setup.py automatically"""
2
- __version__ = "2.1.0"
2
+ __version__ = "2.2.0"
@@ -1,16 +1,17 @@
1
1
  from fractions import Fraction
2
+ from typing import Generator, Iterable, cast
2
3
 
3
4
  import numpy as np
4
5
  from numpy import e, inf, pi
6
+ from numpy.typing import NDArray
5
7
  from scipy.signal import sosfilt
6
8
 
7
- from ._waveform import (_D, COS, COSH, DRAG, ERF, EXP, EXPONENTIALCHIRP,
8
- GAUSSIAN, HYPERBOLICCHIRP, INTERP, LINEAR, LINEARCHIRP,
9
- NDIGITS, SINC, SINH, _baseFunc, _baseFunc_latex,
10
- _const, _half, _one, _zero, add, basic_wave,
11
- calc_parts, filter, is_const, merge_waveform, mul, pow,
12
- registerBaseFunc, registerBaseFuncLatex,
13
- registerDerivative, shift, simplify, wave_sum)
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)
14
15
 
15
16
 
16
17
  def _test_spec_num(num, spec):
@@ -124,7 +125,7 @@ class Waveform:
124
125
  self.start = None
125
126
  self.stop = None
126
127
  self.sample_rate = None
127
- self.filters = None
128
+ self.filters: tuple[np.ndarray, float] | None = None
128
129
  self.label = None
129
130
 
130
131
  @staticmethod
@@ -160,12 +161,14 @@ class Waveform:
160
161
  else:
161
162
  return min(self.stop, self._end(self.bounds, self.seq))
162
163
 
163
- def sample(self,
164
- sample_rate=None,
165
- out=None,
166
- chunk_size=None,
167
- function_lib=None,
168
- filters=None):
164
+ def sample(
165
+ self,
166
+ sample_rate=None,
167
+ out: np.ndarray | None = None,
168
+ chunk_size=None,
169
+ function_lib=None,
170
+ filters: tuple[np.ndarray, float] | None = None
171
+ ) -> np.ndarray | Iterable[np.ndarray]:
169
172
  if sample_rate is None:
170
173
  sample_rate = self.sample_rate
171
174
  if self.start is None or self.stop is None or sample_rate is None:
@@ -179,28 +182,39 @@ class Waveform:
179
182
  sig = self.__call__(x, out=out, function_lib=function_lib)
180
183
  if filters is not None:
181
184
  sos, initial = filters
185
+ if not isinstance(sos, np.ndarray):
186
+ sos = np.array(sos)
187
+ elif not sos.flags.writeable:
188
+ sos = sos.copy()
182
189
  if initial:
183
- sig = sosfilt(sos, sig - initial) + initial
190
+ sig = cast(np.ndarray, sosfilt(sos,
191
+ sig - initial)) + initial
184
192
  else:
185
- sig = sosfilt(sos, sig)
186
- return sig
193
+ sig = cast(np.ndarray, sosfilt(sos, sig))
194
+ return cast(np.ndarray, sig)
187
195
  else:
188
196
  return self._sample_iter(sample_rate, chunk_size, out,
189
197
  function_lib, filters)
190
198
 
191
- def _sample_iter(self, sample_rate, chunk_size, out, function_lib,
192
- filters):
193
- start = self.start
199
+ def _sample_iter(
200
+ self, sample_rate, chunk_size, out: np.ndarray | None, function_lib,
201
+ filters: tuple[np.ndarray, float] | None
202
+ ) -> Generator[np.ndarray, None, None]:
203
+ start = cast(float, self.start)
194
204
  start_n = 0
195
205
  if filters is not None:
196
206
  sos, initial = filters
207
+ if not isinstance(sos, np.ndarray):
208
+ sos = np.array(sos)
209
+ elif not sos.flags.writeable:
210
+ sos = sos.copy()
197
211
  # zi = sosfilt_zi(sos)
198
212
  zi = np.zeros((sos.shape[0], 2))
199
213
  length = chunk_size / sample_rate
200
- while start < self.stop:
201
- if start + length > self.stop:
202
- length = self.stop - start
203
- stop = self.stop
214
+ while start < cast(float, self.stop):
215
+ if start + length > cast(float, self.stop):
216
+ length = cast(float, self.stop) - start
217
+ stop = cast(float, self.stop)
204
218
  size = round((stop - start) * sample_rate)
205
219
  else:
206
220
  stop = start + length
@@ -209,13 +223,17 @@ class Waveform:
209
223
 
210
224
  if filters is None:
211
225
  if out is not None:
212
- yield self.__call__(x,
213
- out=out[start_n:],
214
- function_lib=function_lib)
226
+ yield cast(
227
+ np.ndarray,
228
+ self.__call__(x,
229
+ out=out[start_n:],
230
+ function_lib=function_lib))
215
231
  else:
216
- yield self.__call__(x, function_lib=function_lib)
232
+ yield cast(np.ndarray,
233
+ self.__call__(x, function_lib=function_lib))
217
234
  else:
218
- sig = self.__call__(x, function_lib=function_lib)
235
+ sig = cast(np.ndarray,
236
+ self.__call__(x, function_lib=function_lib))
219
237
  if initial:
220
238
  sig -= initial
221
239
  sig, zi = sosfilt(sos, sig, zi=zi)
@@ -223,7 +241,7 @@ class Waveform:
223
241
  sig += initial
224
242
  if out is not None:
225
243
  out[start_n:start_n + size] = sig
226
- yield sig
244
+ yield cast(np.ndarray, sig)
227
245
 
228
246
  start = stop
229
247
  start_n += chunk_size
@@ -498,16 +516,21 @@ class Waveform:
498
516
  for start, stop, part in parts:
499
517
  out[start:stop] += part
500
518
 
501
- def __call__(self,
502
- x,
503
- frag=False,
504
- out=None,
505
- accumulate=False,
506
- function_lib=None):
519
+ def __call__(
520
+ self,
521
+ x,
522
+ frag=False,
523
+ out: np.ndarray | None = None,
524
+ accumulate=False,
525
+ function_lib=None
526
+ ) -> NDArray[np.float64] | list[tuple[int, int,
527
+ NDArray[np.float64]]] | np.float64:
507
528
  if function_lib is None:
508
529
  function_lib = _baseFunc
509
530
  if isinstance(x, (int, float, complex)):
510
- return self.__call__(np.array([x]), function_lib=function_lib)[0]
531
+ return cast(
532
+ NDArray[np.float64],
533
+ self.__call__(np.array([x]), function_lib=function_lib))[0]
511
534
  parts, dtype = calc_parts(self.bounds, self.seq, x, function_lib,
512
535
  self.min, self.max)
513
536
  if not frag:
@@ -957,6 +980,25 @@ def _format_DRAG(shift, *args):
957
980
  return f"DRAG(...)"
958
981
 
959
982
 
983
+ def _format_MOLLIFIER(shift, *args):
984
+ r = _num_latex(args[0])
985
+ d = _num_latex(args[1])
986
+ shift_str = _num_latex(-shift)
987
+ if shift_str == '0':
988
+ shift_str = ''
989
+ elif shift_str[0] != '-':
990
+ shift_str = '+' + shift_str
991
+
992
+ if d == '0':
993
+ return f"\\mathrm{{Mollifier}}\\left(t{shift_str}, r={r}\\right)"
994
+ elif d == '1':
995
+ return f"\\mathrm{{Mollifier}}'\\left(t{shift_str}, r={r}\\right)"
996
+ elif d == '2':
997
+ return f"\\mathrm{{Mollifier}}''\\left(t{shift_str}, r={r}\\right)"
998
+ else:
999
+ return f"\\mathrm{{Mollifier}}^{{({d})}}\\left(t{shift_str}, r={r}\\right)"
1000
+
1001
+
960
1002
  registerBaseFuncLatex(LINEAR, _format_LINEAR)
961
1003
  registerBaseFuncLatex(GAUSSIAN, _format_GAUSSIAN)
962
1004
  registerBaseFuncLatex(ERF, _format_ERF)
@@ -966,12 +1008,26 @@ registerBaseFuncLatex(EXP, _format_EXP)
966
1008
  registerBaseFuncLatex(COSH, _format_COSH)
967
1009
  registerBaseFuncLatex(SINH, _format_SINH)
968
1010
  registerBaseFuncLatex(DRAG, _format_DRAG)
1011
+ registerBaseFuncLatex(MOLLIFIER, _format_MOLLIFIER)
969
1012
 
970
1013
 
971
- def D(wav):
1014
+ def D(wav: Waveform, d: int = 1) -> Waveform:
972
1015
  """derivative
1016
+
1017
+ Parameters
1018
+ ----------
1019
+ wav : Waveform
1020
+ The waveform to take the derivative of.
1021
+ d : int, optional
1022
+ The order of the derivative, by default 1.
973
1023
  """
974
- return Waveform(bounds=wav.bounds, seq=tuple(_D(x) for x in wav.seq))
1024
+ assert d >= 0 and isinstance(d, int), "d must be a non-negative integer"
1025
+ if d == 0:
1026
+ return wav
1027
+ elif d == 1:
1028
+ return Waveform(bounds=wav.bounds, seq=tuple(_D(x) for x in wav.seq))
1029
+ else:
1030
+ return D(D(wav, d - 1), 1)
975
1031
 
976
1032
 
977
1033
  def convolve(a, b):
@@ -1181,6 +1237,40 @@ def slepian(duration, *arg):
1181
1237
  return wav * square(duration)
1182
1238
 
1183
1239
 
1240
+ def mollifier(width, plateau: float = 0.0, d: int = 0):
1241
+ """
1242
+ Mollifier function is a smooth function that is 1 at the origin and 0 outside a certain radius.
1243
+ It is defined as:
1244
+
1245
+ f(x) = exp(1 / ((x / r) ^ 2 - 1) + 1) in case |x| < r
1246
+ = 0 in case |x| >= r
1247
+ where r = width / 2 is the radius of the mollifier.
1248
+
1249
+ The parameter plateau is the width of the plateau.
1250
+ The parameter d is the order of the derivative.
1251
+ """
1252
+ assert d >= 0 and isinstance(d, int), "d must be a non-negative integer"
1253
+ assert width > 0, "width must be positive"
1254
+
1255
+ if plateau <= 0:
1256
+ return Waveform(bounds=(-0.5 * width, 0.5 * width, inf),
1257
+ seq=(_zero, basic_wave(MOLLIFIER, width / 2,
1258
+ d), _zero))
1259
+ else:
1260
+ return Waveform(bounds=(-0.5 * width - 0.5 * plateau, -0.5 * plateau,
1261
+ 0.5 * plateau, 0.5 * width + 0.5 * plateau,
1262
+ inf),
1263
+ seq=(_zero,
1264
+ basic_wave(MOLLIFIER,
1265
+ width / 2,
1266
+ d,
1267
+ shift=-0.5 * plateau), _one,
1268
+ basic_wave(MOLLIFIER,
1269
+ width / 2,
1270
+ d,
1271
+ shift=0.5 * plateau), _zero))
1272
+
1273
+
1184
1274
  def _poly(*a):
1185
1275
  """
1186
1276
  a[0] + a[1] * t + a[2] * t**2 + ...
@@ -1376,7 +1466,7 @@ def mixing(I,
1376
1466
  __all__ = [
1377
1467
  'D', 'Waveform', 'chirp', 'const', 'cos', 'cosh', 'coshPulse', 'cosPulse',
1378
1468
  'cut', 'drag', 'exp', 'function', 'gaussian', 'general_cosine', 'hanning',
1379
- 'interp', 'mixing', 'one', 'poly', 'registerBaseFunc',
1469
+ 'interp', 'mixing', 'mollifier', 'one', 'poly', 'registerBaseFunc',
1380
1470
  'registerDerivative', 'samplingPoints', 'sign', 'sin', 'sinc', 'sinh',
1381
1471
  'square', 'step', 't', 'zero'
1382
1472
  ]
@@ -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', 'one', 'poly',
34
- 'samplingPoints', 'sign', 'sin', 'sinc', 'sinh', 'square', 'step',
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
- str(grammar_file)
232
- ],
233
- cwd=str(current_dir),
234
- capture_output=True,
235
- text=True,
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 2.1.0
3
+ Version: 2.2.0
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