waveforms 3.4.0__tar.gz → 3.5.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-3.4.0/waveforms.egg-info → waveforms-3.5.0}/PKG-INFO +34 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/README.md +33 -0
- waveforms-3.5.0/tests/test_nonlinear.py +255 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/tests/test_waveform.py +1 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/__init__.py +2 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/_cwaveform.c +789 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/_cwaveform.h +58 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/_cwaveform.md +35 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/_waveform.pyi +27 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/_waveform.pyx +207 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/distortion.py +166 -10
- waveforms-3.5.0/waveforms/nonlinear.py +353 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/version.py +1 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/waveform.py +117 -38
- {waveforms-3.4.0 → waveforms-3.5.0/waveforms.egg-info}/PKG-INFO +34 -1
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms.egg-info/SOURCES.txt +2 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/LICENSE +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/MANIFEST.in +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/pyproject.toml +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/setup.cfg +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/setup.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/tests/test_core.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/tests/test_wavevstack.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/Waveform.g4 +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/__main__.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/utils.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms/waveform_parser.py +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms.egg-info/dependency_links.txt +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms.egg-info/entry_points.txt +0 -0
- {waveforms-3.4.0 → waveforms-3.5.0}/waveforms.egg-info/requires.txt +0 -0
- {waveforms-3.4.0 → waveforms-3.5.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
|
+
Version: 3.5.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>
|
|
@@ -153,6 +153,39 @@ Real waveforms and stacks therefore avoid complex storage and arithmetic for
|
|
|
153
153
|
the common real-valued case. `ComplexWaveform.real` and `.imag` expose the two
|
|
154
154
|
real channel waveforms.
|
|
155
155
|
|
|
156
|
+
### Post-sampling nonlinear calibration
|
|
157
|
+
|
|
158
|
+
`NonlinearMap` compiles one monotone branch of calibration samples to a compact
|
|
159
|
+
native lookup table. A centered map is useful when a waveform describes a
|
|
160
|
+
frequency excursion around an idle point:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
frequency = np.array([4.0e9, 4.5e9, 5.0e9, 5.5e9, 6.0e9])
|
|
164
|
+
flux = np.array([0.31, 0.22, 0.08, -0.06, -0.16])
|
|
165
|
+
|
|
166
|
+
frequency_to_flux = wf.NonlinearMap.from_samples(
|
|
167
|
+
frequency,
|
|
168
|
+
flux,
|
|
169
|
+
method="monotone_cubic", # PCHIP compiled to uniform cubic segments
|
|
170
|
+
reference=5.0e9, # map(0) == 0 around the idle frequency
|
|
171
|
+
dtype=np.float32,
|
|
172
|
+
extrapolate="error",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
trajectory.start = 0
|
|
176
|
+
trajectory.stop = 200e-9
|
|
177
|
+
trajectory.sample_rate = 2_400_000_000
|
|
178
|
+
trajectory.nonlinear = frequency_to_flux
|
|
179
|
+
flux_samples = trajectory.sample(dtype=np.int16)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The sampling order is waveform accumulation, nonlinear mapping, optional SOS
|
|
183
|
+
filtering/predistortion, and finally integer quantization. This is important for
|
|
184
|
+
`WaveVStack`: the map is applied to the accumulated trajectory rather than to
|
|
185
|
+
each pulse event independently. `method="linear"` selects the smaller and
|
|
186
|
+
fastest two-point interpolation path. Maps serialize independently through
|
|
187
|
+
`to_bytes()`/`from_bytes()` using the language-neutral `NLM1` format.
|
|
188
|
+
|
|
156
189
|
## Reporting Issues
|
|
157
190
|
Please report all issues [on github](https://github.com/feihoo87/waveforms/issues).
|
|
158
191
|
|
|
@@ -109,6 +109,39 @@ Real waveforms and stacks therefore avoid complex storage and arithmetic for
|
|
|
109
109
|
the common real-valued case. `ComplexWaveform.real` and `.imag` expose the two
|
|
110
110
|
real channel waveforms.
|
|
111
111
|
|
|
112
|
+
### Post-sampling nonlinear calibration
|
|
113
|
+
|
|
114
|
+
`NonlinearMap` compiles one monotone branch of calibration samples to a compact
|
|
115
|
+
native lookup table. A centered map is useful when a waveform describes a
|
|
116
|
+
frequency excursion around an idle point:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
frequency = np.array([4.0e9, 4.5e9, 5.0e9, 5.5e9, 6.0e9])
|
|
120
|
+
flux = np.array([0.31, 0.22, 0.08, -0.06, -0.16])
|
|
121
|
+
|
|
122
|
+
frequency_to_flux = wf.NonlinearMap.from_samples(
|
|
123
|
+
frequency,
|
|
124
|
+
flux,
|
|
125
|
+
method="monotone_cubic", # PCHIP compiled to uniform cubic segments
|
|
126
|
+
reference=5.0e9, # map(0) == 0 around the idle frequency
|
|
127
|
+
dtype=np.float32,
|
|
128
|
+
extrapolate="error",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
trajectory.start = 0
|
|
132
|
+
trajectory.stop = 200e-9
|
|
133
|
+
trajectory.sample_rate = 2_400_000_000
|
|
134
|
+
trajectory.nonlinear = frequency_to_flux
|
|
135
|
+
flux_samples = trajectory.sample(dtype=np.int16)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The sampling order is waveform accumulation, nonlinear mapping, optional SOS
|
|
139
|
+
filtering/predistortion, and finally integer quantization. This is important for
|
|
140
|
+
`WaveVStack`: the map is applied to the accumulated trajectory rather than to
|
|
141
|
+
each pulse event independently. `method="linear"` selects the smaller and
|
|
142
|
+
fastest two-point interpolation path. Maps serialize independently through
|
|
143
|
+
`to_bytes()`/`from_bytes()` using the language-neutral `NLM1` format.
|
|
144
|
+
|
|
112
145
|
## Reporting Issues
|
|
113
146
|
Please report all issues [on github](https://github.com/feihoo87/waveforms/issues).
|
|
114
147
|
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from scipy.interpolate import PchipInterpolator
|
|
6
|
+
from scipy.signal import butter, sosfilt, tf2sos
|
|
7
|
+
|
|
8
|
+
import waveforms as wf
|
|
9
|
+
from waveforms._waveform import quantize_samples
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_linear_map_binary_roundtrip_clip_and_output():
|
|
13
|
+
x = np.linspace(-2.0, 2.0, 9)
|
|
14
|
+
y = 0.5 + 1.75 * x
|
|
15
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
16
|
+
x, y, method="linear", table_size=9,
|
|
17
|
+
)
|
|
18
|
+
points = np.linspace(-2.0, 2.0, 1001)
|
|
19
|
+
expected = 0.5 + 1.75 * points
|
|
20
|
+
assert np.allclose(mapping(points), expected, rtol=0, atol=2e-15)
|
|
21
|
+
|
|
22
|
+
target = np.empty_like(points)
|
|
23
|
+
assert mapping(points, out=target) is target
|
|
24
|
+
assert np.array_equal(target, mapping(points))
|
|
25
|
+
assert mapping(0.25) == pytest.approx(0.9375)
|
|
26
|
+
assert mapping.method == "linear"
|
|
27
|
+
assert mapping.dtype == np.dtype(np.float64)
|
|
28
|
+
assert mapping.extrapolate == "error"
|
|
29
|
+
assert mapping.point_count == 9
|
|
30
|
+
assert mapping.domain == (-2.0, 2.0)
|
|
31
|
+
|
|
32
|
+
data = mapping.to_bytes()
|
|
33
|
+
assert data[:4] == b"NLM1"
|
|
34
|
+
restored = wf.NonlinearMap.from_bytes(data)
|
|
35
|
+
assert restored.to_bytes() is data
|
|
36
|
+
assert restored == mapping
|
|
37
|
+
assert hash(restored) == hash(mapping)
|
|
38
|
+
assert pickle.loads(pickle.dumps(mapping)) == mapping
|
|
39
|
+
assert np.array_equal(
|
|
40
|
+
mapping._apply_quantized(points, 16),
|
|
41
|
+
quantize_samples(mapping(points), 16),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
malformed = bytearray(data)
|
|
45
|
+
malformed[9] = 1
|
|
46
|
+
with pytest.raises(ValueError, match="NLM1"):
|
|
47
|
+
wf.NonlinearMap.from_bytes(malformed)
|
|
48
|
+
with pytest.raises(ValueError, match="NLM1"):
|
|
49
|
+
wf.NonlinearMap.from_bytes(data[:-1])
|
|
50
|
+
|
|
51
|
+
with pytest.raises(ValueError, match="outside its domain"):
|
|
52
|
+
mapping(np.array([-2.1, 0.0]))
|
|
53
|
+
clipped = wf.NonlinearMap.from_samples(
|
|
54
|
+
x, y, method="linear", table_size=9, extrapolate="clip",
|
|
55
|
+
)
|
|
56
|
+
assert np.array_equal(clipped([-3.0, 3.0]), [y[0], y[-1]])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_monotone_cubic_accuracy_storage_and_centering():
|
|
60
|
+
x = np.array([4.0, 4.15, 4.4, 4.9, 5.6, 6.0])
|
|
61
|
+
y = np.array([0.31, 0.27, 0.18, 0.02, -0.11, -0.16])
|
|
62
|
+
reference = 4.9
|
|
63
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
64
|
+
x, y, table_size=1025, reference=reference,
|
|
65
|
+
)
|
|
66
|
+
relative = np.linspace(mapping.domain[0], mapping.domain[1], 10001)
|
|
67
|
+
reference_curve = PchipInterpolator(x, y)
|
|
68
|
+
expected = reference_curve(reference + relative) - reference_curve(reference)
|
|
69
|
+
assert np.max(np.abs(mapping(relative) - expected)) < 2e-7
|
|
70
|
+
assert mapping(0.0) == pytest.approx(0.0, abs=2e-15)
|
|
71
|
+
assert mapping.method == "monotone_cubic"
|
|
72
|
+
assert mapping.reference_input == reference
|
|
73
|
+
assert mapping.reference_output == pytest.approx(
|
|
74
|
+
reference_curve(reference), abs=2e-7)
|
|
75
|
+
assert np.all(np.diff(mapping(relative)) <= 0)
|
|
76
|
+
|
|
77
|
+
compact = wf.NonlinearMap.from_samples(
|
|
78
|
+
x, y, table_size=1025, reference=reference, dtype=np.float32,
|
|
79
|
+
)
|
|
80
|
+
assert compact.dtype == np.dtype(np.float32)
|
|
81
|
+
assert compact(0.0) == pytest.approx(0.0, abs=2e-7)
|
|
82
|
+
assert len(compact.to_bytes()) == 48 + 16 * (compact.point_count - 1)
|
|
83
|
+
assert len(mapping.to_bytes()) == 48 + 32 * (mapping.point_count - 1)
|
|
84
|
+
assert np.max(np.abs(compact(relative) - expected)) < 3e-7
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.mark.parametrize("x,y,message", [
|
|
88
|
+
([0, 0, 1], [0, 1, 2], "increase strictly"),
|
|
89
|
+
([0, 1, 2], [0, 2, 1], "one inverse branch"),
|
|
90
|
+
([0, 1], [0, np.nan], "finite"),
|
|
91
|
+
])
|
|
92
|
+
def test_map_validation(x, y, message):
|
|
93
|
+
with pytest.raises(ValueError, match=message):
|
|
94
|
+
wf.NonlinearMap.from_samples(x, y)
|
|
95
|
+
with pytest.raises(ValueError, match="reference"):
|
|
96
|
+
wf.NonlinearMap.from_samples([0, 1], [0, 1], reference=2)
|
|
97
|
+
with pytest.raises(TypeError, match="float32 or float64"):
|
|
98
|
+
wf.NonlinearMap.from_samples([0, 1], [0, 1], dtype=np.int16)
|
|
99
|
+
with pytest.raises(ValueError, match="method"):
|
|
100
|
+
wf.NonlinearMap.from_samples([0, 1], [0, 1], method="bezier")
|
|
101
|
+
with pytest.raises(ValueError, match="NLM1"):
|
|
102
|
+
wf.NonlinearMap.from_bytes(b"not a map")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_error_controlled_table_compilation():
|
|
106
|
+
x = np.array([0.0, 0.13, 0.41, 1.0])
|
|
107
|
+
y = np.array([0.0, 0.2, 0.75, 1.0])
|
|
108
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
109
|
+
x, y, method="linear", table_size=5,
|
|
110
|
+
max_error=1e-3, max_table_size=4097,
|
|
111
|
+
)
|
|
112
|
+
assert mapping.point_count > 5
|
|
113
|
+
points = np.linspace(0.0, 1.0, 100_001)
|
|
114
|
+
assert np.max(np.abs(mapping(points) - np.interp(points, x, y))) < 1e-3
|
|
115
|
+
|
|
116
|
+
with pytest.raises(ValueError, match="requires more than"):
|
|
117
|
+
wf.NonlinearMap.from_samples(
|
|
118
|
+
x, y, method="linear", table_size=5,
|
|
119
|
+
max_error=1e-6, max_table_size=9,
|
|
120
|
+
)
|
|
121
|
+
with pytest.raises(ValueError, match="max_error"):
|
|
122
|
+
wf.NonlinearMap.from_samples(x, y, max_error=0)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_waveform_sampling_order_chunking_quantization_and_pickle():
|
|
126
|
+
sample_rate = 1024
|
|
127
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
128
|
+
[0.0, 0.25, 0.5, 0.75, 1.0],
|
|
129
|
+
[0.0, 0.0625, 0.25, 0.5625, 1.0],
|
|
130
|
+
table_size=257,
|
|
131
|
+
)
|
|
132
|
+
b, a = butter(3, 40.0, "lowpass", fs=sample_rate)
|
|
133
|
+
sos = tf2sos(b, a)
|
|
134
|
+
waveform = wf.t()
|
|
135
|
+
waveform.start = 0.0
|
|
136
|
+
waveform.stop = 1.0
|
|
137
|
+
waveform.sample_rate = sample_rate
|
|
138
|
+
waveform.nonlinear = mapping
|
|
139
|
+
waveform.filters = (sos, 0.0)
|
|
140
|
+
|
|
141
|
+
raw = np.arange(sample_rate, dtype=np.float64) / sample_rate
|
|
142
|
+
expected = sosfilt(sos, mapping(raw))
|
|
143
|
+
actual = waveform.sample()
|
|
144
|
+
assert np.allclose(actual, expected, rtol=3e-14, atol=3e-14)
|
|
145
|
+
chunks = np.concatenate(list(waveform.sample(chunk_size=73)))
|
|
146
|
+
assert np.allclose(chunks, expected, rtol=3e-14, atol=3e-14)
|
|
147
|
+
|
|
148
|
+
expected_int16 = quantize_samples(expected, 16)
|
|
149
|
+
output = np.empty(sample_rate, dtype=np.int16)
|
|
150
|
+
assert waveform.sample(dtype=np.int16, out=output) is output
|
|
151
|
+
assert np.array_equal(output, expected_int16)
|
|
152
|
+
|
|
153
|
+
restored = pickle.loads(pickle.dumps(waveform))
|
|
154
|
+
assert restored.nonlinear == mapping
|
|
155
|
+
assert np.array_equal(restored.sample(), actual)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_stack_mapping_happens_after_event_accumulation():
|
|
159
|
+
pulse = 0.4 * wf.square(1.0)
|
|
160
|
+
stack = wf.WaveVStack([pulse, pulse])
|
|
161
|
+
stack.start = -0.25
|
|
162
|
+
stack.stop = 0.25
|
|
163
|
+
stack.sample_rate = 1000
|
|
164
|
+
stack.nonlinear = wf.NonlinearMap.from_samples(
|
|
165
|
+
[0.0, 0.4, 0.8], [0.0, 0.16, 0.64], table_size=257,
|
|
166
|
+
)
|
|
167
|
+
samples = stack.sample()
|
|
168
|
+
assert np.allclose(samples, 0.64, atol=2e-14)
|
|
169
|
+
assert not np.allclose(samples, 0.32)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_complex_waveform_uses_explicit_component_maps():
|
|
173
|
+
real_map = wf.NonlinearMap.from_samples(
|
|
174
|
+
[0.0, 0.5, 1.0], [0.0, 0.25, 1.0], table_size=257,
|
|
175
|
+
)
|
|
176
|
+
imag_map = wf.NonlinearMap.from_samples(
|
|
177
|
+
[0.0, 0.5, 1.0], [0.0, 1.0, 2.0],
|
|
178
|
+
method="linear", table_size=257,
|
|
179
|
+
)
|
|
180
|
+
waveform = wf.ComplexWaveform(0.5, 0.25)
|
|
181
|
+
waveform.start = 0.0
|
|
182
|
+
waveform.stop = 1.0
|
|
183
|
+
waveform.sample_rate = 16
|
|
184
|
+
waveform.nonlinear = (real_map, imag_map)
|
|
185
|
+
expected = real_map(0.5) + 1j * imag_map(0.25)
|
|
186
|
+
assert np.allclose(waveform.sample(), expected)
|
|
187
|
+
i, q = waveform.sample_iq(dtype=np.int16)
|
|
188
|
+
assert np.array_equal(i, quantize_samples(
|
|
189
|
+
np.full(16, expected.real), 16))
|
|
190
|
+
assert np.array_equal(q, quantize_samples(
|
|
191
|
+
np.full(16, expected.imag), 16))
|
|
192
|
+
|
|
193
|
+
waveform.nonlinear = real_map
|
|
194
|
+
with pytest.raises(TypeError, match="complex waveforms require"):
|
|
195
|
+
waveform.sample()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@pytest.mark.parametrize("method", ["linear", "monotone_cubic"])
|
|
199
|
+
@pytest.mark.parametrize("storage", [np.float32, np.float64])
|
|
200
|
+
def test_simd_batches_match_scalar_edges_aliasing_and_quantization(
|
|
201
|
+
method, storage):
|
|
202
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
203
|
+
[-1.0, -0.6, -0.1, 0.35, 1.0],
|
|
204
|
+
[-0.8, -0.5, 0.05, 0.4, 0.9],
|
|
205
|
+
method=method, table_size=257, dtype=storage, extrapolate="clip",
|
|
206
|
+
)
|
|
207
|
+
source = np.linspace(-1.2, 1.2, 65)
|
|
208
|
+
source[0] = -1.0
|
|
209
|
+
source[-1] = 1.0
|
|
210
|
+
for count in (1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 65):
|
|
211
|
+
values = source[:count].copy()
|
|
212
|
+
expected = np.array([mapping(float(value)) for value in values])
|
|
213
|
+
actual = mapping(values)
|
|
214
|
+
assert np.allclose(actual, expected, rtol=2e-15, atol=2e-15)
|
|
215
|
+
|
|
216
|
+
in_place = values.copy()
|
|
217
|
+
assert mapping(in_place, out=in_place) is in_place
|
|
218
|
+
assert np.allclose(in_place, expected, rtol=2e-15, atol=2e-15)
|
|
219
|
+
|
|
220
|
+
assert np.array_equal(
|
|
221
|
+
mapping._apply_quantized(values, 16),
|
|
222
|
+
quantize_samples(expected, 16),
|
|
223
|
+
)
|
|
224
|
+
assert np.array_equal(
|
|
225
|
+
mapping._apply_quantized(values, 32),
|
|
226
|
+
quantize_samples(expected, 32),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_simd_batches_preserve_error_extrapolation_and_nonfinite_checks():
|
|
231
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
232
|
+
[-1.0, 0.0, 1.0], [-0.5, 0.0, 0.5], table_size=257,
|
|
233
|
+
)
|
|
234
|
+
for bad_value in (-1.01, 1.01, np.nan, np.inf, -np.inf):
|
|
235
|
+
values = np.linspace(-0.9, 0.9, 32)
|
|
236
|
+
values[19] = bad_value
|
|
237
|
+
with pytest.raises(ValueError, match="outside its domain"):
|
|
238
|
+
mapping(values)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_simd_quantized_pipeline_spans_multiple_cache_blocks():
|
|
242
|
+
mapping = wf.NonlinearMap.from_samples(
|
|
243
|
+
[-1.0, -0.25, 0.3, 1.0], [-0.9, -0.2, 0.4, 0.95],
|
|
244
|
+
table_size=1025,
|
|
245
|
+
)
|
|
246
|
+
values = 0.99 * np.sin(np.linspace(-70.0, 70.0, 12_345))
|
|
247
|
+
mapped = mapping(values)
|
|
248
|
+
assert np.array_equal(
|
|
249
|
+
mapping._apply_quantized(values, 16),
|
|
250
|
+
quantize_samples(mapped, 16),
|
|
251
|
+
)
|
|
252
|
+
assert np.array_equal(
|
|
253
|
+
mapping._apply_quantized(values, 32),
|
|
254
|
+
quantize_samples(mapped, 32),
|
|
255
|
+
)
|
|
@@ -15,7 +15,7 @@ from waveforms._waveform import (
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
PUBLIC_NAMES = {
|
|
18
|
-
"ComplexWaveform", "ComplexWaveVStack", "D", "RealWaveform",
|
|
18
|
+
"ComplexWaveform", "ComplexWaveVStack", "D", "NonlinearMap", "RealWaveform",
|
|
19
19
|
"RealWaveVStack", "Waveform", "WaveVStack",
|
|
20
20
|
"chirp", "const", "cos", "cosh",
|
|
21
21
|
"coshPulse", "cosPulse", "cut", "drag", "drag_sin", "drag_sinx",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from numpy import e, pi
|
|
2
2
|
|
|
3
3
|
from .version import __version__
|
|
4
|
+
from .nonlinear import NonlinearMap
|
|
4
5
|
from .waveform import (
|
|
5
6
|
ComplexWaveform,
|
|
6
7
|
ComplexWaveVStack,
|
|
@@ -51,7 +52,7 @@ from .waveform import (
|
|
|
51
52
|
)
|
|
52
53
|
|
|
53
54
|
__all__ = [
|
|
54
|
-
"ComplexWaveform", "ComplexWaveVStack", "D", "RealWaveform",
|
|
55
|
+
"ComplexWaveform", "ComplexWaveVStack", "D", "NonlinearMap", "RealWaveform",
|
|
55
56
|
"RealWaveVStack", "Waveform", "WaveVStack",
|
|
56
57
|
"chirp", "const", "cos", "cosh",
|
|
57
58
|
"coshPulse", "cosPulse", "cut", "drag", "drag_sin", "drag_sinx",
|