waveforms 2.2.3__tar.gz → 2.3.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.
- {waveforms-2.2.3 → waveforms-2.3.0}/MANIFEST.in +1 -1
- {waveforms-2.2.3 → waveforms-2.3.0}/PKG-INFO +1 -1
- {waveforms-2.2.3 → waveforms-2.3.0}/tests/test_waveform.py +38 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/tests/test_wavevstack.py +10 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/_waveform.pyx +176 -80
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/version.py +1 -1
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/PKG-INFO +1 -1
- {waveforms-2.2.3 → waveforms-2.3.0}/LICENSE +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/README.md +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/pyproject.toml +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/setup.cfg +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/setup.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/src/waveform.h +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/tests/test_multi_drag.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/Waveform.g4 +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/WaveformLexer.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/WaveformListener.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/WaveformParser.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/__init__.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/__main__.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/_waveform.pyi +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/distortion.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/multy_drag.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/utils.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/waveform.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms/waveform_parser.py +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/SOURCES.txt +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/entry_points.txt +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/requires.txt +0 -0
- {waveforms-2.2.3 → waveforms-2.3.0}/waveforms.egg-info/top_level.txt +0 -0
|
@@ -138,6 +138,44 @@ def test_chirp():
|
|
|
138
138
|
assert np.allclose(wav3(t), _chirp(t, 1, 2, 10, 4, 'hyperbolic'))
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
def test_gaussian_derivative_and_mollifier():
|
|
142
|
+
x = np.linspace(-1.999, 1.999, 4097)
|
|
143
|
+
|
|
144
|
+
width = 4.0
|
|
145
|
+
std_sq2 = width / 3.3302184446307908
|
|
146
|
+
n = 8
|
|
147
|
+
expected = ((-1)**n / std_sq2**n
|
|
148
|
+
* special.eval_hermite(n, x / std_sq2)
|
|
149
|
+
* np.exp(-(x / std_sq2)**2))
|
|
150
|
+
assert np.allclose(gaussian(width, d=n)(x), expected)
|
|
151
|
+
|
|
152
|
+
d = 6
|
|
153
|
+
r = width / 2
|
|
154
|
+
scaled = x / r
|
|
155
|
+
xx_1 = scaled * scaled - 1
|
|
156
|
+
p = np.poly1d([-2, 0])
|
|
157
|
+
for order in range(1, d):
|
|
158
|
+
p = (np.poly1d([1, 0, -2, 0, 1]) * p.deriv()
|
|
159
|
+
+ np.poly1d([-4 * order, 0, 4 * order - 2, 0]) * p)
|
|
160
|
+
expected = (np.exp(1 / xx_1 + 1) / (-xx_1)**(2 * d)
|
|
161
|
+
* p(scaled) / r**d)
|
|
162
|
+
assert np.allclose(mollifier(width, d=d)(x), expected)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_sampling_points_and_clipping():
|
|
166
|
+
x = np.linspace(-2.0, 2.0, 4096, endpoint=False)
|
|
167
|
+
points = tuple(np.sin(np.linspace(0.0, 3.0, 65)))
|
|
168
|
+
wav = samplingPoints(-2.0, 2.0, points)
|
|
169
|
+
assert np.allclose(wav(x), np.interp(x, np.linspace(-2.0, 2.0, 65),
|
|
170
|
+
points))
|
|
171
|
+
|
|
172
|
+
wav = 2.0 * cos(2.1) + 0.3 * sin(0.7)
|
|
173
|
+
wav.min = -0.4
|
|
174
|
+
wav.max = 0.6
|
|
175
|
+
assert np.allclose(wav(x), np.clip(2.0 * np.cos(2.1 * x)
|
|
176
|
+
+ 0.3 * np.sin(0.7 * x), -0.4, 0.6))
|
|
177
|
+
|
|
178
|
+
|
|
141
179
|
def test_parser():
|
|
142
180
|
assert wave_eval("one()") == one()
|
|
143
181
|
assert wave_eval("zero()") == zero()
|
|
@@ -141,3 +141,13 @@ def test_wave_sum():
|
|
|
141
141
|
assert wave_sum([((-1.0, np.inf), (((), ()), ((((), ()), ), (0.02, )))),
|
|
142
142
|
((-1.0, np.inf), (((), ()), ((((), ()), ), (-0.02, ))))
|
|
143
143
|
]) == ((np.inf, ), (((), ()), ))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_wave_sum_preserves_value_before_a_new_earliest_bound():
|
|
147
|
+
source = (square(1.0) >> 5.0, const(2.0), square(1.0))
|
|
148
|
+
waves = [(wav.bounds, wav.seq) for wav in source]
|
|
149
|
+
combined = Waveform(*wave_sum(waves))
|
|
150
|
+
x = np.array([-10.0, 0.0, 2.0, 5.0, 10.0])
|
|
151
|
+
expected = sum(wav(x) for wav in source)
|
|
152
|
+
|
|
153
|
+
assert np.allclose(combined(x), expected)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pickle
|
|
2
2
|
from bisect import bisect_left
|
|
3
|
+
from functools import lru_cache
|
|
3
4
|
from itertools import chain, product
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
@@ -127,90 +128,174 @@ def pow(x, n):
|
|
|
127
128
|
return ret
|
|
128
129
|
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
cdef object _calc_impl(object wav, object x, object function_lib):
|
|
132
|
+
cdef dict value_cache = {}
|
|
133
|
+
cdef object term_list = wav[0]
|
|
134
|
+
cdef object coefficient_list = wav[1]
|
|
135
|
+
cdef object monomial
|
|
136
|
+
cdef object base_list
|
|
137
|
+
cdef object power_list
|
|
138
|
+
cdef object base
|
|
139
|
+
cdef object value
|
|
140
|
+
cdef object product_value
|
|
141
|
+
cdef object term_value
|
|
142
|
+
cdef object total = None
|
|
143
|
+
cdef object coefficient
|
|
144
|
+
cdef object func
|
|
145
|
+
cdef object shift
|
|
146
|
+
cdef object args
|
|
147
|
+
cdef Py_ssize_t i, j
|
|
148
|
+
cdef Py_ssize_t term_count = len(term_list)
|
|
149
|
+
cdef Py_ssize_t base_count
|
|
150
|
+
|
|
151
|
+
for i in range(term_count):
|
|
152
|
+
monomial = term_list[i]
|
|
153
|
+
base_list = monomial[0]
|
|
154
|
+
power_list = monomial[1]
|
|
155
|
+
coefficient = coefficient_list[i]
|
|
156
|
+
product_value = None
|
|
157
|
+
base_count = len(base_list)
|
|
158
|
+
|
|
159
|
+
for j in range(base_count):
|
|
160
|
+
base = base_list[j]
|
|
161
|
+
if base in value_cache:
|
|
162
|
+
value = value_cache[base]
|
|
163
|
+
else:
|
|
164
|
+
func = function_lib[base[0]]
|
|
165
|
+
shift = base[-1]
|
|
166
|
+
args = base[1:-1]
|
|
167
|
+
if shift == 0:
|
|
168
|
+
value = func(x, *args)
|
|
169
|
+
else:
|
|
170
|
+
value = func(x - shift, *args)
|
|
171
|
+
value_cache[base] = value
|
|
132
172
|
|
|
173
|
+
if power_list[j] != 1:
|
|
174
|
+
value = value**power_list[j]
|
|
133
175
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def _calc_m(t, x):
|
|
138
|
-
ret = 1
|
|
139
|
-
for mt, n in zip(*t):
|
|
140
|
-
if mt not in lru_cache:
|
|
141
|
-
func_id, *args, shift = mt
|
|
142
|
-
lru_cache[mt] = _apply(function_lib, func_id, x, shift, *args)
|
|
143
|
-
if n == 1:
|
|
144
|
-
ret = ret * lru_cache[mt]
|
|
176
|
+
if product_value is None:
|
|
177
|
+
product_value = value
|
|
145
178
|
else:
|
|
146
|
-
|
|
147
|
-
|
|
179
|
+
product_value = product_value * value
|
|
180
|
+
|
|
181
|
+
if product_value is None:
|
|
182
|
+
term_value = coefficient
|
|
183
|
+
elif coefficient == 1:
|
|
184
|
+
term_value = product_value
|
|
185
|
+
else:
|
|
186
|
+
term_value = coefficient * product_value
|
|
187
|
+
|
|
188
|
+
if total is None:
|
|
189
|
+
total = term_value
|
|
190
|
+
else:
|
|
191
|
+
total = total + term_value
|
|
192
|
+
|
|
193
|
+
if total is None:
|
|
194
|
+
return 0
|
|
195
|
+
return total
|
|
148
196
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return ret
|
|
197
|
+
|
|
198
|
+
def _calc(wav, x, function_lib):
|
|
199
|
+
return _calc_impl(wav, x, function_lib)
|
|
153
200
|
|
|
154
201
|
|
|
155
202
|
def calc_parts(bounds, seq, x, function_lib, min=-inf, max=inf):
|
|
156
|
-
range_list = np.searchsorted(x, bounds)
|
|
157
|
-
parts = []
|
|
158
|
-
|
|
159
|
-
dtype = float
|
|
160
|
-
|
|
203
|
+
cdef object range_list = np.searchsorted(x, bounds)
|
|
204
|
+
cdef list parts = []
|
|
205
|
+
cdef object part
|
|
206
|
+
cdef object dtype = float
|
|
207
|
+
cdef Py_ssize_t i
|
|
208
|
+
cdef Py_ssize_t start = 0
|
|
209
|
+
cdef Py_ssize_t stop
|
|
210
|
+
cdef Py_ssize_t count = len(range_list)
|
|
211
|
+
cdef bint should_clip = min != -inf or max != inf
|
|
212
|
+
|
|
213
|
+
for i in range(count):
|
|
214
|
+
stop = range_list[i]
|
|
161
215
|
if start < stop and seq[i] != _zero:
|
|
162
|
-
part =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
216
|
+
part = _calc_impl(seq[i], x[start:stop], function_lib)
|
|
217
|
+
if should_clip:
|
|
218
|
+
part = np.clip(part, min, max)
|
|
219
|
+
if np.iscomplexobj(part):
|
|
166
220
|
dtype = complex
|
|
167
221
|
parts.append((start, stop, part))
|
|
168
222
|
start = stop
|
|
169
223
|
return parts, dtype
|
|
170
224
|
|
|
171
225
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
elif len(bounds) == 1:
|
|
186
|
-
bounds = list(bounds_)
|
|
187
|
-
seq = [add(seq[0], s) for s in seq_]
|
|
188
|
-
else:
|
|
189
|
-
lo = 0
|
|
190
|
-
for b, s in zip(bounds_, seq_):
|
|
191
|
-
i = bisect_left(bounds, b, lo=lo)
|
|
192
|
-
if bounds[i] > b:
|
|
193
|
-
bounds.insert(i, b)
|
|
194
|
-
if i == 0:
|
|
195
|
-
seq.insert(i, s)
|
|
196
|
-
else:
|
|
197
|
-
seq.insert(i, add(s, seq[i]))
|
|
198
|
-
up = i - 1
|
|
199
|
-
else:
|
|
200
|
-
up = i
|
|
201
|
-
for j in range(lo + 1, up + 1):
|
|
202
|
-
seq[j] = add(seq[j], s)
|
|
203
|
-
lo = i
|
|
204
|
-
|
|
205
|
-
i = 0
|
|
206
|
-
while i < len(bounds) - 1:
|
|
207
|
-
if seq[i] == seq[i + 1]:
|
|
208
|
-
del seq[i]
|
|
209
|
-
del bounds[i]
|
|
226
|
+
cdef void _accumulate_expr(dict accumulator, object expr, int sign):
|
|
227
|
+
cdef object term_list = expr[0]
|
|
228
|
+
cdef object value_list = expr[1]
|
|
229
|
+
cdef object term
|
|
230
|
+
cdef object value
|
|
231
|
+
cdef Py_ssize_t i
|
|
232
|
+
cdef Py_ssize_t count = len(term_list)
|
|
233
|
+
|
|
234
|
+
for i in range(count):
|
|
235
|
+
term = term_list[i]
|
|
236
|
+
value = accumulator.get(term, 0) + sign * value_list[i]
|
|
237
|
+
if value == 0:
|
|
238
|
+
accumulator.pop(term, None)
|
|
210
239
|
else:
|
|
211
|
-
|
|
240
|
+
accumulator[term] = value
|
|
212
241
|
|
|
213
|
-
|
|
242
|
+
|
|
243
|
+
cdef object _snapshot_expr(dict accumulator):
|
|
244
|
+
cdef list items
|
|
245
|
+
if not accumulator:
|
|
246
|
+
return _zero
|
|
247
|
+
items = sorted(accumulator.items())
|
|
248
|
+
return (tuple(item[0] for item in items),
|
|
249
|
+
tuple(item[1] for item in items))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def wave_sum(waves):
|
|
253
|
+
cdef dict accumulator = {}
|
|
254
|
+
cdef dict events = {}
|
|
255
|
+
cdef list output_bounds = []
|
|
256
|
+
cdef list output_seq
|
|
257
|
+
cdef object bounds
|
|
258
|
+
cdef object seq
|
|
259
|
+
cdef object changes
|
|
260
|
+
cdef object old_expr
|
|
261
|
+
cdef object new_expr
|
|
262
|
+
cdef object expr
|
|
263
|
+
cdef object boundary
|
|
264
|
+
cdef Py_ssize_t i, j
|
|
265
|
+
cdef Py_ssize_t wave_count
|
|
266
|
+
cdef Py_ssize_t bound_count
|
|
267
|
+
|
|
268
|
+
if not waves:
|
|
269
|
+
return ((inf, ), (_zero, ))
|
|
270
|
+
if len(waves) == 1:
|
|
271
|
+
return waves[0]
|
|
272
|
+
|
|
273
|
+
wave_count = len(waves)
|
|
274
|
+
for i in range(wave_count):
|
|
275
|
+
bounds, seq = waves[i]
|
|
276
|
+
_accumulate_expr(accumulator, seq[0], 1)
|
|
277
|
+
bound_count = len(bounds)
|
|
278
|
+
for j in range(bound_count - 1):
|
|
279
|
+
boundary = bounds[j]
|
|
280
|
+
changes = events.get(boundary)
|
|
281
|
+
if changes is None:
|
|
282
|
+
changes = []
|
|
283
|
+
events[boundary] = changes
|
|
284
|
+
changes.append((seq[j], seq[j + 1]))
|
|
285
|
+
|
|
286
|
+
output_seq = [_snapshot_expr(accumulator)]
|
|
287
|
+
for boundary in sorted(events):
|
|
288
|
+
changes = events[boundary]
|
|
289
|
+
for old_expr, new_expr in changes:
|
|
290
|
+
_accumulate_expr(accumulator, old_expr, -1)
|
|
291
|
+
_accumulate_expr(accumulator, new_expr, 1)
|
|
292
|
+
expr = _snapshot_expr(accumulator)
|
|
293
|
+
if expr != output_seq[-1]:
|
|
294
|
+
output_bounds.append(boundary)
|
|
295
|
+
output_seq.append(expr)
|
|
296
|
+
|
|
297
|
+
output_bounds.append(inf)
|
|
298
|
+
return tuple(output_bounds), tuple(output_seq)
|
|
214
299
|
|
|
215
300
|
|
|
216
301
|
def merge_waveform(b1, s1, b2, s2, oper):
|
|
@@ -296,8 +381,9 @@ def _GAUSSIAN(t, std_sq2):
|
|
|
296
381
|
|
|
297
382
|
|
|
298
383
|
def _D_GAUSSIAN(t, std_sq2, n):
|
|
299
|
-
|
|
300
|
-
|
|
384
|
+
x = t / std_sq2
|
|
385
|
+
return ((-1)**n / std_sq2**n * special.eval_hermite(n, x)
|
|
386
|
+
* np.exp(-(x**2)))
|
|
301
387
|
|
|
302
388
|
|
|
303
389
|
def _ERF(t, std_sq2):
|
|
@@ -316,8 +402,13 @@ def _EXP(t, alpha):
|
|
|
316
402
|
return np.exp(alpha * t)
|
|
317
403
|
|
|
318
404
|
|
|
405
|
+
@lru_cache(maxsize=256)
|
|
406
|
+
def _interp_grid(start, stop, size):
|
|
407
|
+
return np.linspace(start, stop, size)
|
|
408
|
+
|
|
409
|
+
|
|
319
410
|
def _INTERP(t, start, stop, points):
|
|
320
|
-
return np.interp(t,
|
|
411
|
+
return np.interp(t, _interp_grid(start, stop, len(points)), points)
|
|
321
412
|
|
|
322
413
|
|
|
323
414
|
def _LINEARCHIRP(t, f0, f1, T, phi0):
|
|
@@ -356,19 +447,24 @@ def _drag(t: np.ndarray, t0: float, freq: float, width: float, delta: float,
|
|
|
356
447
|
return Omega_x * np.cos(wt) + Omega_y * np.sin(wt)
|
|
357
448
|
|
|
358
449
|
|
|
450
|
+
@lru_cache(maxsize=64)
|
|
451
|
+
def _mollifier_poly(d):
|
|
452
|
+
p = np.poly1d([-2, 0])
|
|
453
|
+
for n in range(1, d):
|
|
454
|
+
p = (np.poly1d([1, 0, -2, 0, 1]) * p.deriv()
|
|
455
|
+
+ np.poly1d([-4 * n, 0, 4 * n - 2, 0]) * p)
|
|
456
|
+
return p
|
|
457
|
+
|
|
458
|
+
|
|
359
459
|
def _mollifier(t: np.ndarray, r: float, d: int):
|
|
360
460
|
x = t / r
|
|
361
|
-
xx_1 =
|
|
461
|
+
xx_1 = x * x - 1
|
|
362
462
|
if d == 0:
|
|
363
463
|
return np.where(xx_1 >= 0, 0, np.exp(1 / xx_1 + 1))
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
[-4 * n, 0, 4 * n - 2, 0]) * p
|
|
369
|
-
return np.where(xx_1 >= 0, 0,
|
|
370
|
-
np.exp(1 / xx_1 + 1) /
|
|
371
|
-
(-xx_1)**(2 * d)) * p(x) / r**d
|
|
464
|
+
p = _mollifier_poly(d)
|
|
465
|
+
return (np.where(xx_1 >= 0, 0,
|
|
466
|
+
np.exp(1 / xx_1 + 1) / (-xx_1)**(2 * d))
|
|
467
|
+
* p(x) / r**d)
|
|
372
468
|
|
|
373
469
|
|
|
374
470
|
LINEAR = registerBaseFunc(_LINEAR)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Define version number here and read it from setup.py automatically"""
|
|
2
|
-
__version__ = "2.
|
|
2
|
+
__version__ = "2.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|