waveforms 3.3.2__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.2/waveforms.egg-info → waveforms-3.4.0}/PKG-INFO +1 -1
  2. {waveforms-3.3.2 → waveforms-3.4.0}/tests/test_waveform.py +14 -5
  3. {waveforms-3.3.2 → waveforms-3.4.0}/tests/test_wavevstack.py +47 -1
  4. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/__init__.py +9 -2
  5. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/_cwaveform.c +270 -138
  6. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/_cwaveform.h +2 -0
  7. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/_waveform.pyx +12 -0
  8. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/version.py +1 -1
  9. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/waveform.py +86 -35
  10. {waveforms-3.3.2 → waveforms-3.4.0/waveforms.egg-info}/PKG-INFO +1 -1
  11. {waveforms-3.3.2 → waveforms-3.4.0}/LICENSE +0 -0
  12. {waveforms-3.3.2 → waveforms-3.4.0}/MANIFEST.in +0 -0
  13. {waveforms-3.3.2 → waveforms-3.4.0}/README.md +0 -0
  14. {waveforms-3.3.2 → waveforms-3.4.0}/pyproject.toml +0 -0
  15. {waveforms-3.3.2 → waveforms-3.4.0}/setup.cfg +0 -0
  16. {waveforms-3.3.2 → waveforms-3.4.0}/setup.py +0 -0
  17. {waveforms-3.3.2 → waveforms-3.4.0}/tests/test_core.py +0 -0
  18. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/Waveform.g4 +0 -0
  19. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/__main__.py +0 -0
  20. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/_cwaveform.md +0 -0
  21. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/_waveform.pyi +0 -0
  22. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/distortion.py +0 -0
  23. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/utils.py +0 -0
  24. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms/waveform_parser.py +0 -0
  25. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms.egg-info/SOURCES.txt +0 -0
  26. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms.egg-info/dependency_links.txt +0 -0
  27. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms.egg-info/entry_points.txt +0 -0
  28. {waveforms-3.3.2 → waveforms-3.4.0}/waveforms.egg-info/requires.txt +0 -0
  29. {waveforms-3.3.2 → 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.2
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>
@@ -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),
@@ -498,8 +502,13 @@ def test_fixed_width_quantization_supported_sampling_and_fallback():
498
502
  assert np.array_equal(chunks, whole)
499
503
 
500
504
  odd_rate = 7_000_000_000
501
- legacy_grid = np.arange(wav.start, wav.stop, 1 / odd_rate)
502
- 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)
503
512
 
504
513
 
505
514
  def test_simd_node_evaluation_matches_scalar_boundaries():
@@ -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
  ]
@@ -87,6 +87,18 @@ typedef struct wf_node {
87
87
  int64_t upper;
88
88
  } wf_node;
89
89
 
90
+ typedef struct wf_drag_sin_kernel {
91
+ size_t order;
92
+ size_t power;
93
+ size_t basis_count;
94
+ double angular;
95
+ double normalization;
96
+ double *transform;
97
+ double *derivatives;
98
+ double *left_polynomial;
99
+ double *right_polynomial;
100
+ } wf_drag_sin_kernel;
101
+
90
102
  struct cwaveform_wave {
91
103
  uint32_t references;
92
104
  uint32_t node_count;
@@ -97,6 +109,7 @@ struct cwaveform_wave {
97
109
  wf_node *nodes;
98
110
  double *parameters;
99
111
  size_t parameter_count;
112
+ wf_drag_sin_kernel **drag_sin_kernels;
100
113
  };
101
114
 
102
115
  struct cwaveform_stack {
@@ -107,6 +120,8 @@ struct cwaveform_stack {
107
120
  uint32_t *template_ids;
108
121
  int64_t *delays;
109
122
  double *scales;
123
+ int64_t lower_tick;
124
+ int64_t upper_tick;
110
125
  uint64_t hash;
111
126
  size_t data_size;
112
127
  uint8_t *data;
@@ -147,6 +162,8 @@ struct cwaveform_sample_plan {
147
162
 
148
163
  static double wf_node_parameter(const cwaveform_wave *wave,
149
164
  const wf_node *node, size_t index);
165
+ static int wf_wave_prepare_drag_sin_kernels(cwaveform_wave *wave);
166
+ static void wf_wave_clear_drag_sin_kernels(cwaveform_wave *wave);
150
167
 
151
168
  static double wf_hypot(double x, double y) {
152
169
  double high = fabs(x);
@@ -474,6 +491,10 @@ static cwaveform_wave *wf_wave_from_parts(const wf_node *nodes,
474
491
  + (size_t)index * sizeof(double), parameters[index]);
475
492
  }
476
493
  wave->hash = wf_hash_bytes(wave->data, size);
494
+ if (wf_wave_prepare_drag_sin_kernels(wave) != 0) {
495
+ cwaveform_wave_release(wave);
496
+ return NULL;
497
+ }
477
498
  return wave;
478
499
  }
479
500
 
@@ -1716,6 +1737,10 @@ cwaveform_wave *cwaveform_wave_from_bytes(const uint8_t *data, size_t size) {
1716
1737
  wave->parameter_count = parameter_count;
1717
1738
  wave->data_size = size;
1718
1739
  wave->hash = wf_hash_bytes(data, size);
1740
+ if (wf_wave_prepare_drag_sin_kernels(wave) != 0) {
1741
+ cwaveform_wave_release(wave);
1742
+ return NULL;
1743
+ }
1719
1744
  return wave;
1720
1745
  }
1721
1746
 
@@ -1957,6 +1982,7 @@ void cwaveform_wave_release(cwaveform_wave *wave) {
1957
1982
  free(wave->data);
1958
1983
  free(wave->nodes);
1959
1984
  free(wave->parameters);
1985
+ wf_wave_clear_drag_sin_kernels(wave);
1960
1986
  free(wave);
1961
1987
  }
1962
1988
 
@@ -2172,198 +2198,272 @@ static double wf_polynomial_derivative_value(const double *coefficients,
2172
2198
  return value;
2173
2199
  }
2174
2200
 
2175
- static double wf_drag_sin_value(const cwaveform_wave *wave,
2176
- const wf_node *node, double local,
2177
- int sinx) {
2201
+ static void wf_drag_sin_kernel_free(wf_drag_sin_kernel *kernel) {
2202
+ if (kernel == NULL) return;
2203
+ free(kernel->transform);
2204
+ free(kernel->derivatives);
2205
+ free(kernel->left_polynomial);
2206
+ free(kernel->right_polynomial);
2207
+ free(kernel);
2208
+ }
2209
+
2210
+ static wf_drag_sin_kernel *wf_drag_sin_kernel_create(
2211
+ const cwaveform_wave *wave, const wf_node *node, int sinx) {
2178
2212
  size_t total = (size_t)node->parameter_count + 1;
2179
2213
  size_t block_count = total - 7;
2180
2214
  size_t order = block_count + 1;
2181
2215
  size_t power = ((block_count + 2) >> 1) << 1;
2182
2216
  size_t basis_count;
2183
- double t0 = wf_node_parameter(wave, node, 0);
2184
- double frequency = wf_node_parameter(wave, node, 1);
2185
2217
  double width = wf_node_parameter(wave, node, 2);
2186
2218
  double delta = wf_node_parameter(wave, node, 3);
2187
- double phase = wf_node_parameter(wave, node, 4);
2188
- double plateau = wf_node_parameter(wave, node, 5);
2189
2219
  double tab = wf_node_parameter(wave, node, 6);
2190
- double angular;
2191
- double *transform;
2192
- double *derivatives;
2193
- double *basis;
2194
- double *values;
2195
- double components[2] = {0.0, 0.0};
2196
- double normalization = 1.0;
2220
+ wf_drag_sin_kernel *kernel;
2221
+ double basis[67];
2222
+ double values[65];
2197
2223
  size_t index;
2198
- if (!(width > 0.0) || block_count > 64) return NAN;
2224
+ if (!(width > 0.0) || block_count > 64) return NULL;
2199
2225
  if (power < 2) power = 2;
2200
2226
  basis_count = power + 1;
2201
- angular = 3.14159265358979323846 / width;
2202
- transform = (double *)calloc(order * 4, sizeof(double));
2203
- derivatives = (double *)calloc(order * basis_count, sizeof(double));
2204
- basis = (double *)calloc(basis_count, sizeof(double));
2205
- values = (double *)calloc(order, sizeof(double));
2206
- if (transform == NULL || derivatives == NULL || basis == NULL
2207
- || values == NULL) {
2208
- free(transform); free(derivatives); free(basis); free(values);
2209
- return NAN;
2210
- }
2211
- transform[0] = 1.0;
2212
- transform[3] = 1.0;
2227
+ kernel = (wf_drag_sin_kernel *)calloc(1, sizeof(*kernel));
2228
+ if (kernel == NULL) return NULL;
2229
+ kernel->order = order;
2230
+ kernel->power = power;
2231
+ kernel->basis_count = basis_count;
2232
+ kernel->angular = 3.14159265358979323846 / width;
2233
+ kernel->normalization = 1.0;
2234
+ kernel->transform = (double *)calloc(order * 4, sizeof(double));
2235
+ kernel->derivatives = (double *)calloc(
2236
+ order * basis_count, sizeof(double));
2237
+ if (sinx) {
2238
+ kernel->left_polynomial = (double *)calloc(
2239
+ 2 * order, sizeof(double));
2240
+ kernel->right_polynomial = (double *)calloc(
2241
+ 2 * order, sizeof(double));
2242
+ }
2243
+ if (kernel->transform == NULL || kernel->derivatives == NULL
2244
+ || (sinx && (kernel->left_polynomial == NULL
2245
+ || kernel->right_polynomial == NULL))) {
2246
+ wf_drag_sin_kernel_free(kernel);
2247
+ return NULL;
2248
+ }
2249
+ kernel->transform[0] = 1.0;
2250
+ kernel->transform[3] = 1.0;
2213
2251
  for (index = 0; index < block_count; ++index) {
2214
2252
  double block_frequency = wf_node_parameter(wave, node, 7 + index);
2215
2253
  double coefficient = 1.0 / (2.0 * 3.14159265358979323846
2216
2254
  * (block_frequency - delta));
2217
2255
  size_t row = index + 1;
2218
2256
  while (row-- != 0) {
2219
- double *target = transform + (row + 1) * 4;
2220
- const double *source = transform + row * 4;
2257
+ double *target = kernel->transform + (row + 1) * 4;
2258
+ const double *source = kernel->transform + row * 4;
2221
2259
  target[0] += -coefficient * source[1];
2222
2260
  target[1] += coefficient * source[0];
2223
2261
  target[2] += -coefficient * source[3];
2224
2262
  target[3] += coefficient * source[2];
2225
2263
  }
2226
2264
  }
2227
- derivatives[power] = 1.0;
2265
+ kernel->derivatives[power] = 1.0;
2228
2266
  for (index = 1; index < order; ++index) {
2229
2267
  size_t exponent;
2230
2268
  if (index & 1) {
2231
2269
  for (exponent = 0; exponent < power; ++exponent)
2232
- derivatives[index * basis_count + exponent] =
2233
- derivatives[(index - 1) * basis_count + exponent + 1]
2234
- * (double)(exponent + 1) * angular;
2270
+ kernel->derivatives[index * basis_count + exponent] =
2271
+ kernel->derivatives[
2272
+ (index - 1) * basis_count + exponent + 1]
2273
+ * (double)(exponent + 1) * kernel->angular;
2235
2274
  } else {
2236
2275
  for (exponent = 0; exponent + 2 <= power; ++exponent)
2237
- derivatives[index * basis_count + exponent] =
2238
- derivatives[(index - 2) * basis_count + exponent + 2]
2276
+ kernel->derivatives[index * basis_count + exponent] =
2277
+ kernel->derivatives[
2278
+ (index - 2) * basis_count + exponent + 2]
2239
2279
  * (double)(exponent + 1) * (double)(exponent + 2);
2240
2280
  for (exponent = 0; exponent <= power; ++exponent)
2241
- derivatives[index * basis_count + exponent] -=
2242
- derivatives[(index - 2) * basis_count + exponent]
2281
+ kernel->derivatives[index * basis_count + exponent] -=
2282
+ kernel->derivatives[
2283
+ (index - 2) * basis_count + exponent]
2243
2284
  * (double)(exponent * exponent);
2244
2285
  for (exponent = 0; exponent <= power; ++exponent)
2245
- derivatives[index * basis_count + exponent]
2246
- *= angular * angular;
2286
+ kernel->derivatives[index * basis_count + exponent]
2287
+ *= kernel->angular * kernel->angular;
2247
2288
  }
2248
2289
  }
2249
- {
2250
- double midpoint = t0 + width / 2.0;
2251
- double plateau_stop = midpoint + plateau;
2252
- double adjusted = local >= plateau_stop ? local - plateau : local;
2253
- double angle = angular * (adjusted - t0);
2254
- double sine = sin(angle);
2255
- double cosine = cos(angle);
2256
- int in_plateau = local > midpoint && local < plateau_stop;
2257
- size_t exponent;
2258
- for (exponent = 0; exponent <= power; ++exponent) {
2259
- basis[exponent] = pow(sine, (double)exponent);
2260
- if (exponent & 1) basis[exponent] *= cosine;
2261
- if (in_plateau) basis[exponent] = 0.0;
2262
- }
2263
- for (index = 0; index < order; ++index) {
2264
- for (exponent = 0; exponent <= power; ++exponent)
2265
- values[index] += derivatives[index * basis_count + exponent]
2266
- * basis[exponent];
2267
- }
2268
- if (in_plateau) values[0] = 1.0;
2269
- if (sinx) {
2270
- int left = local >= midpoint - tab * width / 2.0
2271
- && local <= midpoint;
2272
- int right = local >= plateau_stop
2273
- && local <= plateau_stop + tab * width / 2.0;
2274
- if (left || right) {
2275
- double boundary_angle = angular
2276
- * ((left ? (1.0 - tab) : (1.0 + tab)) * width / 2.0);
2277
- double boundary_sine = sin(boundary_angle);
2278
- double boundary_cosine = cos(boundary_angle);
2279
- double *boundary_basis = basis;
2280
- double *edge_values = (double *)calloc(order, sizeof(double));
2281
- double *polynomial = (double *)calloc(2 * order, sizeof(double));
2282
- double edge_x = (left ? -tab : tab) * width / 2.0;
2283
- double x = left ? local - midpoint : local - plateau_stop;
2284
- if (edge_values == NULL || polynomial == NULL) {
2285
- free(edge_values); free(polynomial);
2286
- free(transform); free(derivatives); free(basis); free(values);
2287
- return NAN;
2288
- }
2289
- for (exponent = 0; exponent <= power; ++exponent) {
2290
- boundary_basis[exponent] = pow(boundary_sine,
2291
- (double)exponent);
2292
- if (exponent & 1) boundary_basis[exponent]
2293
- *= boundary_cosine;
2294
- }
2295
- for (index = 0; index < order; ++index)
2296
- for (exponent = 0; exponent <= power; ++exponent)
2297
- edge_values[index] +=
2298
- derivatives[index * basis_count + exponent]
2299
- * boundary_basis[exponent];
2300
- if (wf_edge_polynomial(edge_values, order, edge_x,
2301
- polynomial) != 0) {
2302
- free(edge_values); free(polynomial);
2303
- free(transform); free(derivatives); free(basis); free(values);
2304
- return NAN;
2305
- }
2306
- for (index = 0; index < order; ++index)
2307
- values[index] = wf_polynomial_derivative_value(
2308
- polynomial, 2 * order, index, x);
2309
- free(edge_values);
2310
- free(polynomial);
2290
+
2291
+ if (sinx) {
2292
+ int side;
2293
+ for (side = 0; side < 2; ++side) {
2294
+ double boundary_angle = kernel->angular
2295
+ * ((side == 0 ? (1.0 - tab) : (1.0 + tab))
2296
+ * width / 2.0);
2297
+ double boundary_sine = sin(boundary_angle);
2298
+ double boundary_cosine = cos(boundary_angle);
2299
+ double edge_x = (side == 0 ? -tab : tab) * width / 2.0;
2300
+ double *polynomial = side == 0
2301
+ ? kernel->left_polynomial : kernel->right_polynomial;
2302
+ double sine_power = 1.0;
2303
+ size_t exponent;
2304
+ memset(values, 0, order * sizeof(double));
2305
+ for (exponent = 0; exponent <= power; ++exponent) {
2306
+ basis[exponent] = sine_power;
2307
+ if (exponent & 1) basis[exponent] *= boundary_cosine;
2308
+ sine_power *= boundary_sine;
2309
+ }
2310
+ for (index = 0; index < order; ++index)
2311
+ for (exponent = 0; exponent <= power; ++exponent)
2312
+ values[index] += kernel->derivatives[
2313
+ index * basis_count + exponent] * basis[exponent];
2314
+ if (wf_edge_polynomial(
2315
+ values, order, edge_x, polynomial) != 0) {
2316
+ wf_drag_sin_kernel_free(kernel);
2317
+ return NULL;
2311
2318
  }
2312
2319
  }
2313
- }
2314
- if (!sinx) {
2315
- double peak_components[2] = {0.0, 0.0};
2320
+ } else {
2321
+ double components[2] = {0.0, 0.0};
2316
2322
  size_t exponent;
2317
2323
  memset(basis, 0, basis_count * sizeof(double));
2324
+ memset(values, 0, order * sizeof(double));
2318
2325
  for (exponent = 0; exponent <= power; exponent += 2)
2319
2326
  basis[exponent] = 1.0;
2320
- memset(values, 0, order * sizeof(double));
2321
2327
  for (index = 0; index < order; ++index)
2322
2328
  for (exponent = 0; exponent <= power; ++exponent)
2323
- values[index] += derivatives[index * basis_count + exponent]
2324
- * basis[exponent];
2329
+ values[index] += kernel->derivatives[
2330
+ index * basis_count + exponent] * basis[exponent];
2325
2331
  for (index = 0; index < order; ++index) {
2326
- peak_components[0] += transform[index * 4 + 0] * values[index];
2327
- peak_components[1] += transform[index * 4 + 2] * values[index];
2332
+ components[0] += kernel->transform[index * 4] * values[index];
2333
+ components[1] += kernel->transform[index * 4 + 2]
2334
+ * values[index];
2328
2335
  }
2329
- normalization = wf_hypot(peak_components[0], peak_components[1]);
2330
- /* Restore the actual derivatives after the peak calculation. */
2331
- {
2332
- double midpoint = t0 + width / 2.0;
2333
- double plateau_stop = midpoint + plateau;
2334
- double adjusted = local >= plateau_stop ? local - plateau : local;
2335
- double angle = angular * (adjusted - t0);
2336
- double sine = sin(angle), cosine = cos(angle);
2336
+ kernel->normalization = wf_hypot(components[0], components[1]);
2337
+ }
2338
+ return kernel;
2339
+ }
2340
+
2341
+ static int wf_wave_prepare_drag_sin_kernels(cwaveform_wave *wave) {
2342
+ uint32_t index;
2343
+ int needed = 0;
2344
+ for (index = 0; index < wave->node_count; ++index) {
2345
+ uint8_t op = wave->nodes[index].op;
2346
+ if (op == WF_OP_DRAG_SIN || op == WF_OP_DRAG_SINX) {
2347
+ needed = 1;
2348
+ break;
2349
+ }
2350
+ }
2351
+ if (!needed) return 0;
2352
+ wave->drag_sin_kernels = (wf_drag_sin_kernel **)calloc(
2353
+ wave->node_count, sizeof(*wave->drag_sin_kernels));
2354
+ if (wave->drag_sin_kernels == NULL) return -1;
2355
+ for (index = 0; index < wave->node_count; ++index) {
2356
+ const wf_node *node = wave->nodes + index;
2357
+ if (node->op != WF_OP_DRAG_SIN && node->op != WF_OP_DRAG_SINX)
2358
+ continue;
2359
+ wave->drag_sin_kernels[index] = wf_drag_sin_kernel_create(
2360
+ wave, node, node->op == WF_OP_DRAG_SINX);
2361
+ if (wave->drag_sin_kernels[index] == NULL) {
2362
+ wf_wave_clear_drag_sin_kernels(wave);
2363
+ return -1;
2364
+ }
2365
+ }
2366
+ return 0;
2367
+ }
2368
+
2369
+ static void wf_wave_clear_drag_sin_kernels(cwaveform_wave *wave) {
2370
+ uint32_t index;
2371
+ if (wave->drag_sin_kernels == NULL) return;
2372
+ for (index = 0; index < wave->node_count; ++index)
2373
+ wf_drag_sin_kernel_free(wave->drag_sin_kernels[index]);
2374
+ free(wave->drag_sin_kernels);
2375
+ wave->drag_sin_kernels = NULL;
2376
+ }
2377
+
2378
+ static double wf_drag_sin_value(const cwaveform_wave *wave,
2379
+ uint32_t node_index,
2380
+ const wf_node *node, double local,
2381
+ int sinx) {
2382
+ const wf_drag_sin_kernel *kernel = wave->drag_sin_kernels == NULL
2383
+ ? NULL : wave->drag_sin_kernels[node_index];
2384
+ double t0 = wf_node_parameter(wave, node, 0);
2385
+ double frequency = wf_node_parameter(wave, node, 1);
2386
+ double width = wf_node_parameter(wave, node, 2);
2387
+ double delta = wf_node_parameter(wave, node, 3);
2388
+ double phase = wf_node_parameter(wave, node, 4);
2389
+ double plateau = wf_node_parameter(wave, node, 5);
2390
+ double tab = wf_node_parameter(wave, node, 6);
2391
+ double basis[67];
2392
+ double values[65];
2393
+ double components[2] = {0.0, 0.0};
2394
+ double midpoint;
2395
+ double plateau_stop;
2396
+ size_t index;
2397
+ if (kernel == NULL) return NAN;
2398
+ midpoint = t0 + width / 2.0;
2399
+ plateau_stop = midpoint + plateau;
2400
+ memset(values, 0, kernel->order * sizeof(double));
2401
+ if (sinx) {
2402
+ int left = local >= midpoint - tab * width / 2.0
2403
+ && local <= midpoint;
2404
+ int right = local >= plateau_stop
2405
+ && local <= plateau_stop + tab * width / 2.0;
2406
+ if (left || right) {
2407
+ double x = left ? local - midpoint : local - plateau_stop;
2408
+ const double *polynomial = left
2409
+ ? kernel->left_polynomial : kernel->right_polynomial;
2410
+ for (index = 0; index < kernel->order; ++index)
2411
+ values[index] = wf_polynomial_derivative_value(
2412
+ polynomial, 2 * kernel->order, index, x);
2413
+ } else {
2414
+ double adjusted = local >= plateau_stop
2415
+ ? local - plateau : local;
2416
+ double angle = kernel->angular * (adjusted - t0);
2417
+ double sine = sin(angle);
2418
+ double cosine = cos(angle);
2419
+ double sine_power = 1.0;
2337
2420
  int in_plateau = local > midpoint && local < plateau_stop;
2338
- memset(values, 0, order * sizeof(double));
2339
- for (index = 0; index <= power; ++index) {
2340
- basis[index] = pow(sine, (double)index);
2341
- if (index & 1) basis[index] *= cosine;
2342
- if (in_plateau) basis[index] = 0.0;
2421
+ size_t exponent;
2422
+ for (exponent = 0; exponent <= kernel->power; ++exponent) {
2423
+ basis[exponent] = sine_power;
2424
+ if (exponent & 1) basis[exponent] *= cosine;
2425
+ if (in_plateau) basis[exponent] = 0.0;
2426
+ sine_power *= sine;
2343
2427
  }
2344
- for (index = 0; index < order; ++index) {
2345
- size_t exponent;
2346
- for (exponent = 0; exponent <= power; ++exponent)
2347
- values[index] += derivatives[index * basis_count + exponent]
2428
+ for (index = 0; index < kernel->order; ++index)
2429
+ for (exponent = 0; exponent <= kernel->power; ++exponent)
2430
+ values[index] += kernel->derivatives[
2431
+ index * kernel->basis_count + exponent]
2348
2432
  * basis[exponent];
2349
- }
2350
2433
  if (in_plateau) values[0] = 1.0;
2351
2434
  }
2435
+ } else {
2436
+ double adjusted = local >= plateau_stop ? local - plateau : local;
2437
+ double angle = kernel->angular * (adjusted - t0);
2438
+ double sine = sin(angle);
2439
+ double cosine = cos(angle);
2440
+ double sine_power = 1.0;
2441
+ int in_plateau = local > midpoint && local < plateau_stop;
2442
+ size_t exponent;
2443
+ for (exponent = 0; exponent <= kernel->power; ++exponent) {
2444
+ basis[exponent] = sine_power;
2445
+ if (exponent & 1) basis[exponent] *= cosine;
2446
+ if (in_plateau) basis[exponent] = 0.0;
2447
+ sine_power *= sine;
2448
+ }
2449
+ for (index = 0; index < kernel->order; ++index)
2450
+ for (exponent = 0; exponent <= kernel->power; ++exponent)
2451
+ values[index] += kernel->derivatives[
2452
+ index * kernel->basis_count + exponent]
2453
+ * basis[exponent];
2454
+ if (in_plateau) values[0] = 1.0;
2352
2455
  }
2353
- for (index = 0; index < order; ++index) {
2354
- components[0] += transform[index * 4 + 0] * values[index];
2355
- components[1] += transform[index * 4 + 2] * values[index];
2456
+ for (index = 0; index < kernel->order; ++index) {
2457
+ components[0] += kernel->transform[index * 4] * values[index];
2458
+ components[1] += kernel->transform[index * 4 + 2] * values[index];
2356
2459
  }
2357
- components[0] /= normalization;
2358
- components[1] /= normalization;
2460
+ components[0] /= kernel->normalization;
2461
+ components[1] /= kernel->normalization;
2359
2462
  {
2360
2463
  double carrier = 2.0 * 3.14159265358979323846
2361
2464
  * (frequency + delta) * local
2362
2465
  - (2.0 * 3.14159265358979323846 * delta * t0 + phase);
2363
- double result = components[0] * cos(carrier)
2364
- + components[1] * sin(carrier);
2365
- free(transform); free(derivatives); free(basis); free(values);
2366
- return result;
2466
+ return components[0] * cos(carrier) + components[1] * sin(carrier);
2367
2467
  }
2368
2468
  }
2369
2469
 
@@ -2521,11 +2621,13 @@ static double wf_evaluate_one(const cwaveform_wave *wave, double position,
2521
2621
  }
2522
2622
  case WF_OP_DRAG_SIN:
2523
2623
  local = position - (double)node->shift / ticks;
2524
- values[index] = wf_drag_sin_value(wave, node, local, 0);
2624
+ values[index] = wf_drag_sin_value(
2625
+ wave, index, node, local, 0);
2525
2626
  break;
2526
2627
  case WF_OP_DRAG_SINX:
2527
2628
  local = position - (double)node->shift / ticks;
2528
- values[index] = wf_drag_sin_value(wave, node, local, 1);
2629
+ values[index] = wf_drag_sin_value(
2630
+ wave, index, node, local, 1);
2529
2631
  break;
2530
2632
  case WF_OP_ADD:
2531
2633
  values[index] = values[node->left] + values[node->right];
@@ -3405,6 +3507,8 @@ cwaveform_stack *cwaveform_stack_create(
3405
3507
  stack = (cwaveform_stack *)calloc(1, sizeof(*stack));
3406
3508
  if (stack == NULL) return NULL;
3407
3509
  stack->references = 1;
3510
+ stack->lower_tick = INT64_MIN;
3511
+ stack->upper_tick = INT64_MAX;
3408
3512
  stack->template_count = template_count;
3409
3513
  stack->event_count = event_count;
3410
3514
  stack->templates = (cwaveform_wave **)calloc(template_count,
@@ -3435,6 +3539,26 @@ cwaveform_stack *cwaveform_stack_create(
3435
3539
  stack->delays[index] = delay_ticks[index];
3436
3540
  stack->scales[index] = scales[index];
3437
3541
  }
3542
+ if (event_count != 0) {
3543
+ int have_support = 0;
3544
+ int64_t lower_tick = INT64_MAX;
3545
+ int64_t upper_tick = INT64_MIN;
3546
+ for (index = 0; index < event_count; ++index) {
3547
+ cwaveform_wave *wave = stack->templates[stack->template_ids[index]];
3548
+ int64_t lower = wf_add_tick(
3549
+ wave->nodes[wave->root].lower, stack->delays[index]);
3550
+ int64_t upper = wf_add_tick(
3551
+ wave->nodes[wave->root].upper, stack->delays[index]);
3552
+ if (lower >= upper) continue;
3553
+ if (!have_support || lower < lower_tick) lower_tick = lower;
3554
+ if (!have_support || upper > upper_tick) upper_tick = upper;
3555
+ have_support = 1;
3556
+ }
3557
+ if (have_support) {
3558
+ stack->lower_tick = lower_tick;
3559
+ stack->upper_tick = upper_tick;
3560
+ }
3561
+ }
3438
3562
  if (wf_stack_encode(stack) != 0) {
3439
3563
  cwaveform_stack_release(stack);
3440
3564
  return NULL;
@@ -3727,6 +3851,14 @@ size_t cwaveform_stack_template_count(const cwaveform_stack *stack) {
3727
3851
  return stack == NULL ? 0 : stack->template_count;
3728
3852
  }
3729
3853
 
3854
+ int64_t cwaveform_stack_lower_tick(const cwaveform_stack *stack) {
3855
+ return stack == NULL ? INT64_MIN : stack->lower_tick;
3856
+ }
3857
+
3858
+ int64_t cwaveform_stack_upper_tick(const cwaveform_stack *stack) {
3859
+ return stack == NULL ? INT64_MAX : stack->upper_tick;
3860
+ }
3861
+
3730
3862
  static size_t wf_lower_bound(const double *values, size_t count, double target) {
3731
3863
  size_t first = 0;
3732
3864
  while (count != 0) {
@@ -145,6 +145,8 @@ CWAVEFORM_API const uint8_t *cwaveform_stack_bytes(const cwaveform_stack *stack,
145
145
  CWAVEFORM_API uint64_t cwaveform_stack_hash(const cwaveform_stack *stack);
146
146
  CWAVEFORM_API size_t cwaveform_stack_event_count(const cwaveform_stack *stack);
147
147
  CWAVEFORM_API size_t cwaveform_stack_template_count(const cwaveform_stack *stack);
148
+ CWAVEFORM_API int64_t cwaveform_stack_lower_tick(const cwaveform_stack *stack);
149
+ CWAVEFORM_API int64_t cwaveform_stack_upper_tick(const cwaveform_stack *stack);
148
150
  CWAVEFORM_API int cwaveform_stack_evaluate(
149
151
  const cwaveform_stack *stack, const double *positions, size_t count,
150
152
  int64_t global_shift, double offset, double *output);
@@ -521,6 +521,8 @@ cdef extern from "_cwaveform.h":
521
521
  uint64_t cwaveform_stack_hash(const cwaveform_stack *) noexcept nogil
522
522
  size_t cwaveform_stack_event_count(const cwaveform_stack *) noexcept nogil
523
523
  size_t cwaveform_stack_template_count(const cwaveform_stack *) noexcept nogil
524
+ int64_t cwaveform_stack_lower_tick(const cwaveform_stack *) noexcept nogil
525
+ int64_t cwaveform_stack_upper_tick(const cwaveform_stack *) noexcept nogil
524
526
  int cwaveform_stack_evaluate(
525
527
  const cwaveform_stack *, const double *, size_t, int64_t, double,
526
528
  double *) noexcept nogil
@@ -1040,6 +1042,16 @@ cdef class CWaveformStackCore:
1040
1042
  self._ensure_pointer()
1041
1043
  return cwaveform_stack_template_count(self._pointer)
1042
1044
 
1045
+ @property
1046
+ def lower_tick(self):
1047
+ self._ensure_pointer()
1048
+ return cwaveform_stack_lower_tick(self._pointer)
1049
+
1050
+ @property
1051
+ def upper_tick(self):
1052
+ self._ensure_pointer()
1053
+ return cwaveform_stack_upper_tick(self._pointer)
1054
+
1043
1055
  @property
1044
1056
  def hash64(self):
1045
1057
  self._ensure_pointer()
@@ -1,2 +1,2 @@
1
1
  """Define version number here and read it from setup.py automatically"""
2
- __version__ = "3.3.2"
2
+ __version__ = "3.4.0"
@@ -39,18 +39,6 @@ _ONE_EXPR = ((((), ()),), (1.0,))
39
39
  _COMPLEX_WAVE_MAGIC = b"CWF1"
40
40
  _COMPLEX_STACK_MAGIC = b"CWS1"
41
41
  _COMPLEX_HEADER = struct.Struct("<4sII")
42
- _SUPPORTED_SAMPLE_RATES = frozenset({
43
- 500_000_000,
44
- 1_000_000_000,
45
- 1_200_000_000,
46
- 2_000_000_000,
47
- 2_400_000_000,
48
- 2_500_000_000,
49
- 4_000_000_000,
50
- 6_000_000_000,
51
- 8_000_000_000,
52
- 10_000_000_000,
53
- })
54
42
  _INF_TICK = np.iinfo(np.int64).max
55
43
  _C_CLOCK_ACTIVE = get_time_resolution() == 1 / _CORE_TICKS_PER_SECOND
56
44
  _C_CLOCK_LOCKED = False
@@ -206,12 +194,12 @@ def _is_tick_aligned(value, tick):
206
194
 
207
195
 
208
196
  def _sampling_plan(start, stop, sample_rate):
209
- """Return an integer/rational tick plan for a supported device rate."""
197
+ """Return an integer/rational tick plan for an integral-Hz device rate."""
210
198
  rate = float(sample_rate)
211
199
  if not np.isfinite(rate) or rate <= 0:
212
200
  raise ValueError("sample_rate must be a finite positive number")
213
201
  integer_rate = int(rate)
214
- if rate != integer_rate or integer_rate not in _SUPPORTED_SAMPLE_RATES:
202
+ if rate != integer_rate:
215
203
  return None
216
204
  start_tick = time_to_tick(start)
217
205
  stop_tick = time_to_tick(stop)
@@ -357,6 +345,10 @@ class Waveform(metaclass=_WaveformMeta):
357
345
  sample_rate: float | None
358
346
  filters: tuple[np.ndarray, float] | None
359
347
 
348
+ def is_zero(self):
349
+ """Return whether this object has no non-zero waveform support."""
350
+ return self.begin >= self.end
351
+
360
352
  @classmethod
361
353
  def from_bytes(cls, data):
362
354
  """Restore any waveform kind from its canonical binary block."""
@@ -809,6 +801,17 @@ class WaveVStack(Waveform, metaclass=_WaveVStackMeta):
809
801
  return RealWaveVStack.from_bytes(data)
810
802
  return RealWaveVStack.from_bytes(data)
811
803
 
804
+ @classmethod
805
+ def from_events(cls, templates, template_ids, delay_ticks, scales):
806
+ """Build a real stack from compiler-native event columns.
807
+
808
+ ``delay_ticks`` uses the process-wide integer clock. This entry point
809
+ avoids constructing one temporary Python waveform per event.
810
+ """
811
+ return RealWaveVStack.from_events(
812
+ templates, template_ids, delay_ticks, scales
813
+ )
814
+
812
815
 
813
816
  class _RealWaveVStackBase(WaveVStack):
814
817
  """Common real-stack type; concrete storage is provided by the C core."""
@@ -1160,6 +1163,9 @@ class RealWaveform(_RealWaveformBase):
1160
1163
  return (self._scale == 0.0
1161
1164
  or self._core.lower_tick >= self._core.upper_tick)
1162
1165
 
1166
+ def is_zero(self):
1167
+ return self._is_zero()
1168
+
1163
1169
  @property
1164
1170
  def bounds(self):
1165
1171
  lower = self.begin
@@ -1468,19 +1474,28 @@ class RealWaveVStack(_RealWaveVStackBase):
1468
1474
  ids = []
1469
1475
  delays = []
1470
1476
  scales = []
1477
+ event_waves = []
1471
1478
  for wave in waves:
1472
1479
  if not isinstance(wave, RealWaveform):
1473
1480
  raise TypeError("RealWaveVStack accepts RealWaveform objects")
1474
- key = id(wave._core)
1475
- template_id = template_map.get(key)
1481
+ if wave._is_zero():
1482
+ continue
1483
+ key = wave._core.hash64
1484
+ template_id = None
1485
+ for candidate in template_map.get(key, ()):
1486
+ if templates[candidate] == wave._core:
1487
+ template_id = candidate
1488
+ break
1476
1489
  if template_id is None:
1477
1490
  template_id = len(templates)
1478
- template_map[key] = template_id
1491
+ template_map.setdefault(key, []).append(template_id)
1479
1492
  templates.append(wave._core)
1480
1493
  ids.append(template_id)
1481
1494
  delays.append(wave._delay_tick)
1482
1495
  scales.append(wave._scale)
1496
+ event_waves.append(wave)
1483
1497
  _core = _CWaveformStackCore.from_events(templates, ids, delays, scales)
1498
+ waves = tuple(event_waves)
1484
1499
  self._core = _core
1485
1500
  self.start = None
1486
1501
  self.stop = None
@@ -1494,6 +1509,19 @@ class RealWaveVStack(_RealWaveVStackBase):
1494
1509
  self._waves_cache = tuple(waves) if waves else None
1495
1510
  self._eval_core_cache = None
1496
1511
 
1512
+ @classmethod
1513
+ def from_events(cls, templates, template_ids, delay_ticks, scales):
1514
+ _ensure_c_clock_locked()
1515
+ templates = tuple(templates)
1516
+ if not all(isinstance(template, RealWaveform)
1517
+ for template in templates):
1518
+ raise TypeError("templates must contain RealWaveform objects")
1519
+ cores = [template._materialized_core() for template in templates]
1520
+ core = _CWaveformStackCore.from_events(
1521
+ cores, template_ids, delay_ticks, scales
1522
+ )
1523
+ return cls(_core=core)
1524
+
1497
1525
  @property
1498
1526
  def shift(self):
1499
1527
  return _c_tick_to_time(self._shift_tick)
@@ -1510,20 +1538,29 @@ class RealWaveVStack(_RealWaveVStackBase):
1510
1538
 
1511
1539
  @property
1512
1540
  def begin(self):
1513
- if self.wlist:
1514
- value = min((wave >> self.shift).begin for wave in self.wlist)
1515
- else:
1516
- value = -inf
1541
+ tick = self._core.lower_tick
1542
+ value = (-inf if tick == np.iinfo(np.int64).min
1543
+ else _c_tick_to_time(tick + self._shift_tick))
1517
1544
  return value if self.start is None else max(self.start, value)
1518
1545
 
1519
1546
  @property
1520
1547
  def end(self):
1521
- if self.wlist:
1522
- value = max((wave >> self.shift).end for wave in self.wlist)
1523
- else:
1524
- value = inf
1548
+ tick = self._core.upper_tick
1549
+ value = (inf if tick == np.iinfo(np.int64).max
1550
+ else _c_tick_to_time(tick + self._shift_tick))
1525
1551
  return value if self.stop is None else min(self.stop, value)
1526
1552
 
1553
+ @property
1554
+ def event_count(self):
1555
+ return self._core.event_count
1556
+
1557
+ @property
1558
+ def template_count(self):
1559
+ return self._core.template_count
1560
+
1561
+ def __len__(self):
1562
+ return self._core.event_count
1563
+
1527
1564
  def __call__(self, x, out=None, accumulate=False, **kwargs):
1528
1565
  scalar = isinstance(x, (int, float, np.number))
1529
1566
  raw_positions = np.asarray([x] if scalar else x)
@@ -1962,9 +1999,8 @@ def sinh(w):
1962
1999
  return RealWaveform._from_core(_scalar(SINH, w))
1963
2000
 
1964
2001
 
1965
- def coshPulse(width, eps=1.0, plateau=0.0):
1966
- if width <= 0 and plateau <= 0:
1967
- return zero()
2002
+ @lru_cache(maxsize=1024)
2003
+ def _cosh_pulse_core(width, eps, plateau):
1968
2004
  w = eps / width
1969
2005
  amplitude = np.cosh(eps / 2)
1970
2006
  scale = -1 / (amplitude - 1)
@@ -1975,12 +2011,25 @@ def coshPulse(width, eps=1.0, plateau=0.0):
1975
2011
  offset=amplitude / (amplitude - 1),
1976
2012
  )
1977
2013
 
1978
- if plateau == 0 or quantize_time(-plateau / 2) == quantize_time(plateau / 2):
1979
- return _piecewise((-width / 2, width / 2, inf), 0, edge(0), 0)
1980
- return _piecewise(
1981
- (-width / 2 - plateau / 2, -plateau / 2, plateau / 2,
1982
- width / 2 + plateau / 2, inf),
1983
- 0, edge(-plateau / 2), 1, edge(plateau / 2), 0,
2014
+ if (plateau == 0
2015
+ or quantize_time(-plateau / 2) == quantize_time(plateau / 2)):
2016
+ waveform = _piecewise(
2017
+ (-width / 2, width / 2, inf), 0, edge(0), 0
2018
+ )
2019
+ else:
2020
+ waveform = _piecewise(
2021
+ (-width / 2 - plateau / 2, -plateau / 2, plateau / 2,
2022
+ width / 2 + plateau / 2, inf),
2023
+ 0, edge(-plateau / 2), 1, edge(plateau / 2), 0,
2024
+ )
2025
+ return waveform._materialized_core()
2026
+
2027
+
2028
+ def coshPulse(width, eps=1.0, plateau=0.0):
2029
+ if width <= 0 and plateau <= 0:
2030
+ return zero()
2031
+ return RealWaveform._from_core(
2032
+ _cosh_pulse_core(float(width), float(eps), float(plateau))
1984
2033
  )
1985
2034
 
1986
2035
 
@@ -2236,6 +2285,8 @@ __all__ = [
2236
2285
  "gaussian", "general_cosine", "get_time_resolution", "hanning",
2237
2286
  "interp", "mixing", "mollifier", "one", "play", "poly",
2238
2287
  "registerBaseFunc", "registerDerivative", "samplingPoints",
2239
- "set_time_resolution", "sign", "sin", "sinc", "sinh", "slepian",
2288
+ "sample_clock", "sample_grid", "quantize_time", "time_to_tick",
2289
+ "tick_to_time", "set_time_resolution", "sign", "sin", "sinc",
2290
+ "sinh", "slepian",
2240
2291
  "square", "step", "t", "wave_eval", "zero", "e", "inf", "pi",
2241
2292
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waveforms
3
- Version: 3.3.2
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>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes