waveforms 3.3.0__tar.gz → 3.4.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 (29) hide show
  1. {waveforms-3.3.0/waveforms.egg-info → waveforms-3.4.0}/PKG-INFO +1 -1
  2. {waveforms-3.3.0 → waveforms-3.4.0}/setup.py +4 -0
  3. {waveforms-3.3.0 → waveforms-3.4.0}/tests/test_core.py +72 -0
  4. {waveforms-3.3.0 → waveforms-3.4.0}/tests/test_waveform.py +38 -5
  5. {waveforms-3.3.0 → waveforms-3.4.0}/tests/test_wavevstack.py +47 -1
  6. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/__init__.py +9 -2
  7. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/_cwaveform.c +1168 -253
  8. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/_cwaveform.h +5 -0
  9. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/_waveform.pyx +35 -50
  10. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/version.py +1 -1
  11. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/waveform.py +86 -35
  12. {waveforms-3.3.0 → waveforms-3.4.0/waveforms.egg-info}/PKG-INFO +1 -1
  13. {waveforms-3.3.0 → waveforms-3.4.0}/LICENSE +0 -0
  14. {waveforms-3.3.0 → waveforms-3.4.0}/MANIFEST.in +0 -0
  15. {waveforms-3.3.0 → waveforms-3.4.0}/README.md +0 -0
  16. {waveforms-3.3.0 → waveforms-3.4.0}/pyproject.toml +0 -0
  17. {waveforms-3.3.0 → waveforms-3.4.0}/setup.cfg +0 -0
  18. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/Waveform.g4 +0 -0
  19. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/__main__.py +0 -0
  20. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/_cwaveform.md +0 -0
  21. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/_waveform.pyi +0 -0
  22. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/distortion.py +0 -0
  23. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/utils.py +0 -0
  24. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms/waveform_parser.py +0 -0
  25. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms.egg-info/SOURCES.txt +0 -0
  26. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms.egg-info/dependency_links.txt +0 -0
  27. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms.egg-info/entry_points.txt +0 -0
  28. {waveforms-3.3.0 → waveforms-3.4.0}/waveforms.egg-info/requires.txt +0 -0
  29. {waveforms-3.3.0 → waveforms-3.4.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: 3.3.0
3
+ Version: 3.4.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>
@@ -39,15 +39,19 @@ def get_extensions():
39
39
  sources = [os.path.join(dirpath, filename)]
40
40
  include_dirs = []
41
41
  extra_link_args = []
42
+ libraries = []
42
43
  if filename == '_waveform.pyx':
43
44
  sources.append(os.path.join(
44
45
  'waveforms', '_cwaveform.c'))
45
46
  include_dirs.append('waveforms')
46
47
  if sys.platform == 'darwin':
47
48
  extra_link_args.extend(['-framework', 'Accelerate'])
49
+ elif sys.platform.startswith('linux'):
50
+ libraries.append('m')
48
51
  extensions.append(
49
52
  Extension(module_name(dirpath, filename), sources,
50
53
  include_dirs=include_dirs,
54
+ libraries=libraries,
51
55
  extra_link_args=extra_link_args))
52
56
 
53
57
  return extensions
@@ -80,6 +80,78 @@ def test_c_symbolic_frequency_filter():
80
80
  )
81
81
 
82
82
 
83
+ def test_c_symbolic_filter_reduces_trigonometric_products_and_powers():
84
+ x = np.linspace(-1.0, 1.0, 4097)
85
+ envelope = wf.gaussian(2.0)
86
+ first_frequency = 9.0
87
+ second_frequency = 7.0
88
+ first_phase = 0.31
89
+ second_phase = -0.27
90
+ cutoff = 5.0
91
+
92
+ first_cos = wf.cos(first_frequency, first_phase)
93
+ first_sin = wf.sin(first_frequency, first_phase)
94
+ second_cos = wf.cos(second_frequency, second_phase)
95
+ second_sin = wf.sin(second_frequency, second_phase)
96
+ difference = first_frequency - second_frequency
97
+ phase_difference = first_phase - second_phase
98
+
99
+ assert np.allclose(
100
+ (2 * envelope * first_cos * second_cos).filter(high=cutoff)(x),
101
+ (envelope * wf.cos(difference, phase_difference))(x),
102
+ atol=2e-11,
103
+ )
104
+ assert np.allclose(
105
+ (2 * envelope * first_cos * second_sin).filter(high=cutoff)(x),
106
+ (-envelope * wf.sin(difference, phase_difference))(x),
107
+ atol=2e-11,
108
+ )
109
+ assert np.allclose(
110
+ (2 * envelope * first_sin * second_sin).filter(high=cutoff)(x),
111
+ (envelope * wf.cos(difference, phase_difference))(x),
112
+ atol=2e-11,
113
+ )
114
+
115
+ powered = wf.cos(first_frequency, first_phase) ** 2
116
+ assert np.allclose(powered.filter(high=cutoff)(x), 0.5, atol=2e-11)
117
+ assert np.allclose(
118
+ powered.filter(2 * first_frequency, np.inf)(x),
119
+ (0.5 * wf.cos(2 * first_frequency, 2 * first_phase))(x),
120
+ atol=2e-11,
121
+ )
122
+
123
+ retained = (1.5e-15 * first_cos).filter(eps=1e-15)
124
+ discarded = (0.5e-15 * first_cos).filter(eps=1e-15)
125
+ assert np.max(np.abs(retained(x))) > 1e-15
126
+ assert np.array_equal(discarded(x), np.zeros_like(x))
127
+
128
+
129
+ def test_down_conversion_filter_matches_historic_symbolic_behavior():
130
+ x = np.linspace(-100.0, 100.0, 10001)
131
+ envelope = wf.gaussian(100.0)
132
+ radio_frequency = 92.0451
133
+ local_frequency = 92.0
134
+ phase = 0.32
135
+ rf, _ = wf.mixing(
136
+ envelope, freq=radio_frequency, phase=phase, DRAGScaling=0.0,
137
+ )
138
+
139
+ i = (2 * rf * wf.cos(-2 * np.pi * local_frequency)).filter(
140
+ high=2 * np.pi * local_frequency,
141
+ )
142
+ q = (2 * rf * wf.sin(-2 * np.pi * local_frequency)).filter(
143
+ high=2 * np.pi * local_frequency,
144
+ )
145
+ difference = 2 * np.pi * (radio_frequency - local_frequency)
146
+
147
+ assert np.allclose(
148
+ i(x), (envelope * wf.cos(difference, -phase))(x), atol=2e-9,
149
+ )
150
+ assert np.allclose(
151
+ q(x), (envelope * wf.sin(difference, -phase))(x), atol=2e-9,
152
+ )
153
+
154
+
83
155
  def test_wave_block_roundtrip_pickle_and_numerics():
84
156
  actual = _pulse()
85
157
  actual.start = -20e-9
@@ -22,8 +22,9 @@ PUBLIC_NAMES = {
22
22
  "exp", "function",
23
23
  "gaussian", "general_cosine", "get_time_resolution", "hanning", "interp", "mixing",
24
24
  "mollifier", "one", "poly", "registerBaseFunc", "registerDerivative",
25
- "samplingPoints", "set_time_resolution", "sign", "sin", "sinc", "sinh", "square", "step",
26
- "t", "wave_eval", "zero",
25
+ "quantize_time", "sample_clock", "sample_grid", "samplingPoints",
26
+ "set_time_resolution", "sign", "sin", "sinc", "sinh", "square",
27
+ "step", "t", "tick_to_time", "time_to_tick", "wave_eval", "zero",
27
28
  }
28
29
 
29
30
 
@@ -34,6 +35,9 @@ def test_public_api_and_basic_sampling():
34
35
  assert np.allclose(wf.sin(0.7)(x), np.sin(0.7 * x))
35
36
  assert np.allclose(wf.poly([1, -0.5, 0.25])(x),
36
37
  1 - 0.5 * x + 0.25 * x**2)
38
+ assert wf.zero().is_zero()
39
+ assert (0 * wf.gaussian(1)).is_zero()
40
+ assert not wf.one().is_zero()
37
41
 
38
42
 
39
43
  def test_binary_roundtrip_is_zero_copy_for_bytes_input():
@@ -461,7 +465,7 @@ def test_device_sample_clocks_and_rational_fallback():
461
465
  assert np.all(np.diff(grid) > 0)
462
466
 
463
467
 
464
- def test_fixed_width_quantization_supported_sampling_and_fallback():
468
+ def test_fixed_width_quantization_and_rational_sampling():
465
469
  values = np.array([-2, -1, -0.5, 0, 0.5, 1, 2.0])
466
470
  assert np.array_equal(
467
471
  quantize_samples(values, 16),
@@ -473,6 +477,15 @@ def test_fixed_width_quantization_supported_sampling_and_fallback():
473
477
  1073741824, 2147483647, 2147483647],
474
478
  )
475
479
 
480
+ # Exercise the fused SIMD path, including its historic half-away-from-zero
481
+ # rounding rule and multidimensional ``out`` handling.
482
+ half_steps = np.array([0.5, -0.5, 1.5, -1.5] * 8) / 32768.0
483
+ target = np.empty((8, 4), dtype=np.int16)
484
+ assert quantize_samples(half_steps.reshape(8, 4), 16, out=target) is target
485
+ assert np.array_equal(
486
+ target.reshape(-1), np.array([1, -1, 2, -2] * 8, dtype=np.int16)
487
+ )
488
+
476
489
  wav = 0.8 * wf.gaussian(20e-9)
477
490
  wav.start = -20e-9
478
491
  wav.stop = 20e-9
@@ -489,8 +502,28 @@ def test_fixed_width_quantization_supported_sampling_and_fallback():
489
502
  assert np.array_equal(chunks, whole)
490
503
 
491
504
  odd_rate = 7_000_000_000
492
- legacy_grid = np.arange(wav.start, wav.stop, 1 / odd_rate)
493
- assert np.array_equal(wav.sample(odd_rate), wav(legacy_grid))
505
+ rational_samples = wav.sample(odd_rate)
506
+ numerator, denominator = sample_clock(odd_rate)
507
+ rational_grid = sample_grid(
508
+ time_to_tick(wav.start), len(rational_samples),
509
+ numerator, denominator,
510
+ )
511
+ assert np.allclose(rational_samples, wav(rational_grid), rtol=0, atol=1e-15)
512
+
513
+
514
+ def test_simd_node_evaluation_matches_scalar_boundaries():
515
+ positions = np.linspace(-2.0, 2.0, 513)
516
+ waves = (
517
+ wf.t(),
518
+ wf.exp(0.2),
519
+ wf.sinc(1.3),
520
+ wf.cosh(0.3),
521
+ wf.sinh(0.3),
522
+ (wf.gaussian(4.0, 1.5) * wf.cos(2.2)) ** 2,
523
+ )
524
+ for wave in waves:
525
+ expected = np.array([wave(float(position)) for position in positions])
526
+ assert np.allclose(wave(positions), expected, rtol=1e-12, atol=1e-13)
494
527
 
495
528
 
496
529
  def test_integer_sampling_fast_paths_are_bit_exact_and_pickle_safe():
@@ -3,7 +3,10 @@ import pickle
3
3
  import numpy as np
4
4
  from scipy.signal import butter, lfilter, lfiltic, tf2sos
5
5
 
6
- from waveforms import WaveVStack, cos, gaussian, pi, poly, sin, step, zero
6
+ from waveforms import (
7
+ Waveform, WaveVStack, cos, gaussian, pi, poly, sin, step,
8
+ time_to_tick, zero,
9
+ )
7
10
 
8
11
 
9
12
  def _waves():
@@ -59,3 +62,46 @@ def test_wavevstack_binary_template_sharing_and_pickle():
59
62
  restored = WaveVStack.from_bytes(stack.to_bytes())
60
63
  assert restored == stack
61
64
  assert pickle.loads(pickle.dumps(stack)) == stack
65
+
66
+
67
+ def test_wavevstack_content_deduplication_and_constant_time_support():
68
+ base = gaussian(20e-9) * cos(2 * pi * 5e9)
69
+ waves = [
70
+ Waveform.from_bytes(base.to_bytes()) >> (index * 80e-9)
71
+ for index in range(100)
72
+ ]
73
+ stack = WaveVStack([zero(), *waves])
74
+
75
+ assert len(stack) == stack.event_count == 100
76
+ assert stack.template_count == 1
77
+ assert np.isclose(stack.begin, base.begin)
78
+ assert np.isclose(stack.end, base.end + 99 * 80e-9)
79
+
80
+ restored = WaveVStack.from_bytes(stack.to_bytes())
81
+ shifted = restored >> 2e-9
82
+ assert restored.template_count == 1
83
+ assert restored.event_count == 100
84
+ assert np.isclose(shifted.begin, stack.begin + 2e-9)
85
+ assert np.isclose(shifted.end, stack.end + 2e-9)
86
+
87
+ empty = WaveVStack()
88
+ assert empty.begin == -np.inf
89
+ assert empty.end == np.inf
90
+
91
+
92
+ def test_wavevstack_compiler_event_columns():
93
+ template = gaussian(20e-9) * cos(2 * pi * 5e9)
94
+ delays = np.arange(100, dtype=np.int64) * time_to_tick(80e-9)
95
+ stack = WaveVStack.from_events(
96
+ (template,), np.zeros(100, dtype=np.uint32), delays,
97
+ np.full(100, 0.25),
98
+ )
99
+ expected = WaveVStack([
100
+ 0.25 * template >> (index * 80e-9) for index in range(100)
101
+ ])
102
+
103
+ assert stack.template_count == 1
104
+ assert stack.event_count == 100
105
+ assert stack.to_bytes() == expected.to_bytes()
106
+ positions = np.linspace(-20e-9, 8e-6, 1000)
107
+ assert np.allclose(stack(positions), expected(positions))
@@ -30,9 +30,12 @@ from .waveform import (
30
30
  mollifier,
31
31
  one,
32
32
  poly,
33
+ quantize_time,
33
34
  registerBaseFunc,
34
35
  registerDerivative,
35
36
  samplingPoints,
37
+ sample_clock,
38
+ sample_grid,
36
39
  set_time_resolution,
37
40
  sign,
38
41
  sin,
@@ -41,6 +44,8 @@ from .waveform import (
41
44
  square,
42
45
  step,
43
46
  t,
47
+ tick_to_time,
48
+ time_to_tick,
44
49
  wave_eval,
45
50
  zero,
46
51
  )
@@ -53,7 +58,9 @@ __all__ = [
53
58
  "e", "exp", "function",
54
59
  "gaussian", "general_cosine", "get_time_resolution", "hanning",
55
60
  "interp", "mixing", "mollifier", "one", "pi", "poly",
56
- "registerBaseFunc", "registerDerivative", "samplingPoints",
61
+ "quantize_time", "registerBaseFunc", "registerDerivative",
62
+ "sample_clock", "sample_grid", "samplingPoints",
57
63
  "set_time_resolution", "sign", "sin", "sinc", "sinh", "square",
58
- "step", "t", "wave_eval", "zero", "__version__",
64
+ "step", "t", "tick_to_time", "time_to_tick", "wave_eval", "zero",
65
+ "__version__",
59
66
  ]