pynumerics 2.3.0__py3-none-any.whl

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 (46) hide show
  1. pynumerics/__init__.py +73 -0
  2. pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  3. pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
  4. pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
  5. pynumerics/_bin/Numerics.dll +0 -0
  6. pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
  7. pynumerics/_coverage.py +3825 -0
  8. pynumerics/_util.py +237 -0
  9. pynumerics/_version.py +40 -0
  10. pynumerics/bootstrap.py +593 -0
  11. pynumerics/callbacks.py +791 -0
  12. pynumerics/copulas.py +910 -0
  13. pynumerics/data.py +2233 -0
  14. pynumerics/differentiation.py +348 -0
  15. pynumerics/distributions.py +4043 -0
  16. pynumerics/download.py +623 -0
  17. pynumerics/enums.py +119 -0
  18. pynumerics/errors.py +245 -0
  19. pynumerics/functions.py +1142 -0
  20. pynumerics/goodness_of_fit.py +699 -0
  21. pynumerics/graphs.py +556 -0
  22. pynumerics/hypothesis_tests.py +445 -0
  23. pynumerics/identity.py +144 -0
  24. pynumerics/integration.py +677 -0
  25. pynumerics/linalg.py +1408 -0
  26. pynumerics/machine_learning.py +1335 -0
  27. pynumerics/marshal.py +733 -0
  28. pynumerics/mathematics.py +1053 -0
  29. pynumerics/mcmc.py +1406 -0
  30. pynumerics/multivariate.py +1539 -0
  31. pynumerics/ode.py +156 -0
  32. pynumerics/optimization.py +1736 -0
  33. pynumerics/probability.py +966 -0
  34. pynumerics/py.typed +0 -0
  35. pynumerics/regional.py +522 -0
  36. pynumerics/roots.py +274 -0
  37. pynumerics/runtime.py +384 -0
  38. pynumerics/sampling.py +1758 -0
  39. pynumerics/special.py +1079 -0
  40. pynumerics/statistics.py +1958 -0
  41. pynumerics/timeseries.py +1326 -0
  42. pynumerics-2.3.0.dist-info/METADATA +142 -0
  43. pynumerics-2.3.0.dist-info/RECORD +46 -0
  44. pynumerics-2.3.0.dist-info/WHEEL +5 -0
  45. pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
  46. pynumerics-2.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1053 @@
1
+ """Numerical utilities of ``Numerics.Tools`` and fast Fourier transforms of ``Numerics.Mathematics.Fourier``.
2
+
3
+ ``expm1`` and ``log1p`` call ``Tools.Expm1`` and ``Tools.Log1p``; an array crosses once through
4
+ their ``IList<double>`` overloads. The fast Fourier transforms (``fft``, ``real_fft``,
5
+ ``bluestein_fft``, ``fft_convolve``, ``fft_correlation`` and ``fft_autocorrelation``) call
6
+ ``Numerics.Mathematics.Fourier``, and the other utilities of ``Numerics.Tools`` (``log_sum_exp``,
7
+ distances, scaling, masked reductions, sequences, compression and the :data:`constants`) are
8
+ wrapped one to one. Special functions are in :mod:`pynumerics.special`, linear algebra in
9
+ :mod:`pynumerics.linalg`, root finding in :mod:`pynumerics.roots`, optimization in
10
+ :mod:`pynumerics.optimization` and link functions in :mod:`pynumerics.functions`.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any
16
+
17
+ import numpy as np
18
+
19
+ from pynumerics._util import (
20
+ bind,
21
+ bind_list,
22
+ broadcast_loop,
23
+ cached_overload,
24
+ evaluate,
25
+ loop,
26
+ namespace,
27
+ overload,
28
+ )
29
+ from pynumerics.errors import translate_public
30
+ from pynumerics.marshal import (
31
+ _bytes_from_net,
32
+ _bytes_to_net,
33
+ to_net_doubles,
34
+ to_net_ints,
35
+ to_net_matrix,
36
+ to_numpy_doubles,
37
+ to_numpy_ints,
38
+ to_numpy_matrix,
39
+ )
40
+
41
+
42
+ def _tools(name: str, x: Any) -> Any:
43
+ """Evaluate ``Tools.Expm1`` or ``Tools.Log1p`` on a scalar or an array.
44
+
45
+ The scalar overload is bound once per process; an array crosses once into the
46
+ ``IList<double>`` overload. Every value is the scalar member's.
47
+ """
48
+ scalar = cached_overload(f"Tools.{name}(double)", lambda: bind(getattr(namespace("Numerics").Tools, name)))
49
+ vector = cached_overload(f"Tools.{name}(IList<double>)",
50
+ lambda: bind_list(getattr(namespace("Numerics").Tools, name)))
51
+ return evaluate(x, scalar, vector)
52
+
53
+
54
+ def expm1(x: Any) -> Any:
55
+ """Return exp(x) − 1 accurately near zero (``Tools.Expm1``).
56
+
57
+ Parameters
58
+ ----------
59
+ x : float or array_like
60
+ Arguments.
61
+
62
+ Returns
63
+ -------
64
+ float or numpy.ndarray
65
+ The values exp(x) − 1, shaped like ``x``.
66
+ """
67
+ return _tools("Expm1", x)
68
+
69
+
70
+ def log1p(x: Any) -> Any:
71
+ """Return log(1 + x) accurately near zero (``Tools.Log1p``).
72
+
73
+ Parameters
74
+ ----------
75
+ x : float or array_like
76
+ Arguments.
77
+
78
+ Returns
79
+ -------
80
+ float or numpy.ndarray
81
+ The values log(1 + x), shaped like ``x``.
82
+ """
83
+ return _tools("Log1p", x)
84
+
85
+
86
+ def _tools_member(name: str, *types: str) -> Any:
87
+ """Return the ``Numerics.Tools`` overload of ``name`` taking ``types``, bound once per process."""
88
+ return cached_overload(f"Tools.{name}({', '.join(types)})",
89
+ lambda: overload(getattr(namespace("Numerics").Tools, name), *types))
90
+
91
+
92
+ class NumericsConstants:
93
+ """The numerical constants of ``Numerics.Tools``.
94
+
95
+ Each property reads the .NET constant, so the values are the ones the loaded assembly uses.
96
+ Use the module instance ``constants``.
97
+ """
98
+
99
+ @property
100
+ def double_machine_epsilon(self) -> float:
101
+ """``Tools.DoubleMachineEpsilon``, the unit roundoff 2**-53 as Numerics writes it."""
102
+ return float(namespace("Numerics").Tools.DoubleMachineEpsilon)
103
+
104
+ @property
105
+ def single_machine_epsilon(self) -> float:
106
+ """``Tools.SingleMachineEpsilon``, the single-precision unit roundoff 2**-24 widened from float32."""
107
+ return float(namespace("Numerics").Tools.SingleMachineEpsilon)
108
+
109
+ @property
110
+ def euler(self) -> float:
111
+ """``Tools.Euler``, the Euler-Mascheroni constant."""
112
+ return float(namespace("Numerics").Tools.Euler)
113
+
114
+ @property
115
+ def golden_ratio(self) -> float:
116
+ """``Tools.GoldenRatio``, (1 + sqrt(5)) / 2."""
117
+ return float(namespace("Numerics").Tools.GoldenRatio)
118
+
119
+ @property
120
+ def log_sqrt_2pi(self) -> float:
121
+ """``Tools.LogSqrt2PI``, log(sqrt(2 pi))."""
122
+ return float(namespace("Numerics").Tools.LogSqrt2PI)
123
+
124
+ @property
125
+ def sqrt_2pi(self) -> float:
126
+ """``Tools.Sqrt2PI``, sqrt(2 pi)."""
127
+ return float(namespace("Numerics").Tools.Sqrt2PI)
128
+
129
+ @property
130
+ def sqrt2(self) -> float:
131
+ """``Tools.Sqrt2``, sqrt(2)."""
132
+ return float(namespace("Numerics").Tools.Sqrt2)
133
+
134
+ @property
135
+ def log2(self) -> float:
136
+ """``Tools.Log2``, log(2)."""
137
+ return float(namespace("Numerics").Tools.Log2)
138
+
139
+
140
+ constants = NumericsConstants()
141
+
142
+
143
+ def copy_sign(x: Any, y: Any) -> Any:
144
+ """Return the magnitude of ``x`` with the sign of ``y`` (``Tools.CopySign``).
145
+
146
+ Unlike ``math.copysign``, the sign test is ``y >= 0``, so ``y = -0.0`` gives a positive result.
147
+
148
+ Parameters
149
+ ----------
150
+ x, y : float or array_like
151
+ Magnitudes and sign sources; arrays broadcast.
152
+
153
+ Returns
154
+ -------
155
+ float or numpy.ndarray
156
+ The values, in the broadcast shape.
157
+ """
158
+ return broadcast_loop(_tools_member("CopySign", "Double", "Double"), x, y)
159
+
160
+
161
+ def is_finite(x: float) -> bool:
162
+ """Return whether ``x`` is neither NaN nor infinite (``Tools.IsFinite``).
163
+
164
+ Parameters
165
+ ----------
166
+ x : float
167
+ The value.
168
+
169
+ Returns
170
+ -------
171
+ bool
172
+ True for a finite value.
173
+ """
174
+ return bool(_tools_member("IsFinite", "Double")(float(x)))
175
+
176
+
177
+ def is_power_of_two(n: int) -> bool:
178
+ """Return whether ``n`` is a positive power of 2 (``Tools.IsPowerOfTwo``).
179
+
180
+ Parameters
181
+ ----------
182
+ n : int
183
+ A 32-bit integer.
184
+
185
+ Returns
186
+ -------
187
+ bool
188
+ True when ``n`` is 1, 2, 4, ...
189
+ """
190
+ return bool(_tools_member("IsPowerOfTwo", "Int32")(int(n)))
191
+
192
+
193
+ def next_power_of_two(n: int) -> int:
194
+ """Return the smallest power of 2 at least ``n``, or 1 for ``n < 1`` (``Tools.NextPowerOfTwo``).
195
+
196
+ Parameters
197
+ ----------
198
+ n : int
199
+ A 32-bit integer.
200
+
201
+ Returns
202
+ -------
203
+ int
204
+ The power of 2.
205
+ """
206
+ return int(_tools_member("NextPowerOfTwo", "Int32")(int(n)))
207
+
208
+
209
+ def clamp(x: Any, minimum: float, maximum: float) -> Any:
210
+ """Clamp values to the inclusive range [minimum, maximum] (``Tools.Clamp``).
211
+
212
+ Parameters
213
+ ----------
214
+ x : float or array_like
215
+ The values.
216
+ minimum, maximum : float
217
+ The bounds.
218
+
219
+ Returns
220
+ -------
221
+ float or numpy.ndarray
222
+ The clamped values, shaped like ``x``; NaN passes through.
223
+ """
224
+ member = _tools_member("Clamp", "Double", "Double", "Double")
225
+ lo, hi = float(minimum), float(maximum)
226
+ return loop(x, lambda v: member(v, lo, hi))
227
+
228
+
229
+ def hypot(a: Any, b: Any) -> Any:
230
+ """Return sqrt(a**2 + b**2) without destructive underflow or overflow (``Tools.Hypot``).
231
+
232
+ Parameters
233
+ ----------
234
+ a, b : float or array_like
235
+ The two values; arrays broadcast.
236
+
237
+ Returns
238
+ -------
239
+ float or numpy.ndarray
240
+ The Euclidean norms, in the broadcast shape; within 2 ulps of the exact value. Special values follow
241
+ IEEE 754 and ``numpy.hypot``: an infinite argument gives ``inf`` even when the other is NaN.
242
+ """
243
+ return broadcast_loop(_tools_member("Hypot", "Double", "Double"), a, b)
244
+
245
+
246
+ def sign(a: Any, b: Any) -> Any:
247
+ """Return the magnitude of ``a`` with the sign of ``b`` (``Tools.Sign``, the Fortran ``SIGN``).
248
+
249
+ Parameters
250
+ ----------
251
+ a, b : float or array_like
252
+ Magnitudes and sign sources (``b >= 0`` counts as positive); arrays broadcast.
253
+
254
+ Returns
255
+ -------
256
+ float or numpy.ndarray
257
+ The values, in the broadcast shape.
258
+ """
259
+ return broadcast_loop(_tools_member("Sign", "Double", "Double"), a, b)
260
+
261
+
262
+ def sqr(a: Any) -> Any:
263
+ """Return ``a * a`` (``Tools.Sqr``).
264
+
265
+ Parameters
266
+ ----------
267
+ a : float or array_like
268
+ The values.
269
+
270
+ Returns
271
+ -------
272
+ float or numpy.ndarray
273
+ The squares, shaped like ``a``.
274
+ """
275
+ return loop(a, _tools_member("Sqr", "Double"))
276
+
277
+
278
+ def power(a: Any, b: Any) -> Any:
279
+ """Raise ``a`` to the integer power ``b`` by repeated squaring (``Tools.Pow``).
280
+
281
+ Parameters
282
+ ----------
283
+ a : float or array_like
284
+ Bases.
285
+ b : int or array_like of int
286
+ Integer exponents; arrays broadcast with ``a``.
287
+
288
+ Returns
289
+ -------
290
+ float or numpy.ndarray
291
+ The powers; ``0 ** -k`` is +inf.
292
+ """
293
+ return broadcast_loop(_tools_member("Pow", "Double", "Int32"), a, b, kinds=(float, int))
294
+
295
+
296
+ def log10(x: Any) -> Any:
297
+ """Return the guarded base-10 logarithm (``Tools.Log10``).
298
+
299
+ Values in [0, 1e-16) are raised to 1e-16, so ``log10(0)`` is -16 rather than -inf.
300
+
301
+ Parameters
302
+ ----------
303
+ x : float or array_like
304
+ The values.
305
+
306
+ Returns
307
+ -------
308
+ float or numpy.ndarray
309
+ The logarithms, shaped like ``x``.
310
+ """
311
+ return loop(x, _tools_member("Log10", "Double"))
312
+
313
+
314
+ def log(x: Any) -> Any:
315
+ """Return the guarded natural logarithm (``Tools.Log``).
316
+
317
+ Values in [0, 1e-16), including -0.0, are raised to 1e-16, so ``log(0)`` is about -36.84
318
+ rather than -inf.
319
+
320
+ Parameters
321
+ ----------
322
+ x : float or array_like
323
+ The values.
324
+
325
+ Returns
326
+ -------
327
+ float or numpy.ndarray
328
+ The logarithms, shaped like ``x``.
329
+ """
330
+ return loop(x, _tools_member("Log", "Double"))
331
+
332
+
333
+ def point_distance(x1: float, y1: float, x2: float, y2: float) -> float:
334
+ """Return the Euclidean distance between two points of the plane (``Tools.Distance`` of four doubles).
335
+
336
+ Parameters
337
+ ----------
338
+ x1, y1 : float
339
+ The first point.
340
+ x2, y2 : float
341
+ The second point.
342
+
343
+ Returns
344
+ -------
345
+ float
346
+ The distance sqrt((x2 - x1)**2 + (y2 - y1)**2).
347
+ """
348
+ member = _tools_member("Distance", "Double", "Double", "Double", "Double")
349
+ return float(member(float(x1), float(y1), float(x2), float(y2)))
350
+
351
+
352
+ def distance(x: Any, y: Any) -> float:
353
+ """Return the Euclidean distance ||x - y|| (``Tools.Distance`` of two lists).
354
+
355
+ Parameters
356
+ ----------
357
+ x, y : array_like
358
+ The points, one coordinate per element.
359
+
360
+ Returns
361
+ -------
362
+ float
363
+ The distance.
364
+ """
365
+ member = _tools_member("Distance", "IList[Double]", "IList[Double]")
366
+ return float(member(to_net_doubles(x), to_net_doubles(y)))
367
+
368
+
369
+ def normalized_distance(x: Any, y: Any, lower: Any, upper: Any) -> float:
370
+ """Return the Euclidean distance after min-max scaling each coordinate (``Tools.NormalizedDistance``).
371
+
372
+ A coordinate with ``upper - lower <= 0`` (or NaN) contributes nothing.
373
+
374
+ Parameters
375
+ ----------
376
+ x, y : array_like
377
+ The points.
378
+ lower, upper : array_like
379
+ The bounds of each coordinate.
380
+
381
+ Returns
382
+ -------
383
+ float
384
+ The normalized distance.
385
+ """
386
+ tools = namespace("Numerics").Tools
387
+ return float(tools.NormalizedDistance(to_net_doubles(x), to_net_doubles(y), to_net_doubles(lower),
388
+ to_net_doubles(upper)))
389
+
390
+
391
+ def weighted_distance(x: Any, y: Any, weights: Any) -> float:
392
+ """Return the weighted Euclidean distance sqrt(sum w (x - y)**2) (``Tools.WeightedDistance``).
393
+
394
+ Parameters
395
+ ----------
396
+ x, y : array_like
397
+ The points.
398
+ weights : array_like
399
+ One weight per coordinate.
400
+
401
+ Returns
402
+ -------
403
+ float
404
+ The weighted distance.
405
+ """
406
+ tools = namespace("Numerics").Tools
407
+ return float(tools.WeightedDistance(to_net_doubles(x), to_net_doubles(y), to_net_doubles(weights)))
408
+
409
+
410
+ def normalize(values: Any) -> np.ndarray:
411
+ """Scale values to [0, 1] by their minimum and maximum (``Tools.Normalize``).
412
+
413
+ Empty input, input holding NaN, or constant input gives zeros.
414
+
415
+ Parameters
416
+ ----------
417
+ values : array_like
418
+ The values.
419
+
420
+ Returns
421
+ -------
422
+ numpy.ndarray
423
+ (values - min) / (max - min).
424
+ """
425
+ return to_numpy_doubles(namespace("Numerics").Tools.Normalize(to_net_doubles(values)))
426
+
427
+
428
+ def denormalize(values: Any, minimum: float, maximum: float) -> np.ndarray:
429
+ """Invert :func:`normalize` with the original minimum and maximum (``Tools.Denormalize``).
430
+
431
+ Parameters
432
+ ----------
433
+ values : array_like
434
+ Normalized values.
435
+ minimum, maximum : float
436
+ The minimum and maximum of the original data.
437
+
438
+ Returns
439
+ -------
440
+ numpy.ndarray
441
+ The values times (maximum - minimum) plus minimum.
442
+ """
443
+ return to_numpy_doubles(namespace("Numerics").Tools.Denormalize(to_net_doubles(values), float(minimum),
444
+ float(maximum)))
445
+
446
+
447
+ def standardize(values: Any) -> np.ndarray:
448
+ """Return the z-scores (values - mean) / sd with the sample standard deviation (``Tools.Standardize``).
449
+
450
+ A zero or NaN standard deviation gives zeros.
451
+
452
+ Parameters
453
+ ----------
454
+ values : array_like
455
+ The values.
456
+
457
+ Returns
458
+ -------
459
+ numpy.ndarray
460
+ The standardized values.
461
+ """
462
+ return to_numpy_doubles(namespace("Numerics").Tools.Standardize(to_net_doubles(values)))
463
+
464
+
465
+ def destandardize(values: Any, mean: float, standard_deviation: float) -> np.ndarray:
466
+ """Invert :func:`standardize` with the original mean and standard deviation (``Tools.Destandardize``).
467
+
468
+ Parameters
469
+ ----------
470
+ values : array_like
471
+ Standardized values.
472
+ mean, standard_deviation : float
473
+ The moments of the original data.
474
+
475
+ Returns
476
+ -------
477
+ numpy.ndarray
478
+ The values times standard_deviation plus mean.
479
+ """
480
+ return to_numpy_doubles(namespace("Numerics").Tools.Destandardize(to_net_doubles(values), float(mean),
481
+ float(standard_deviation)))
482
+
483
+
484
+ def _masked(name: str, values: Any, indicators: Any, use_complement: bool) -> float:
485
+ """Call ``Tools.<name>`` on doubles, or on doubles with 0/1 indicators and the complement flag."""
486
+ if indicators is None:
487
+ return float(_tools_member(name, "IList[Double]")(to_net_doubles(values)))
488
+ member = _tools_member(name, "IList[Double]", "IList[Int32]", "Boolean")
489
+ return float(member(to_net_doubles(values), to_net_ints(indicators), bool(use_complement)))
490
+
491
+
492
+ def sum_values(values: Any, indicators: Any = None, use_complement: bool = False) -> float:
493
+ """Return the sum of the values, optionally of those an indicator selects (``Tools.Sum``).
494
+
495
+ Integer input without indicators is summed by the ``IList<int>`` overload.
496
+
497
+ Parameters
498
+ ----------
499
+ values : array_like
500
+ The values.
501
+ indicators : array_like of int, optional
502
+ 0 or 1 per value; with ``use_complement`` False the values marked 1 are summed, with it True
503
+ those marked 0. None sums every value.
504
+ use_complement : bool, optional
505
+ Select the values marked 0; default False.
506
+
507
+ Returns
508
+ -------
509
+ float
510
+ The sum.
511
+ """
512
+ if indicators is None and np.asarray(values).dtype.kind in "iub":
513
+ return float(_tools_member("Sum", "IList[Int32]")(to_net_ints(values)))
514
+ return _masked("Sum", values, indicators, use_complement)
515
+
516
+
517
+ def _compensated_sum_member(*types: str) -> Any:
518
+ """Return the ``Tools.CompensatedSum`` overload taking ``types``, bound once per process."""
519
+ return cached_overload(
520
+ f"Tools.CompensatedSum({', '.join(types)})",
521
+ lambda: overload(namespace("Numerics").Tools.CompensatedSum, *types))
522
+
523
+
524
+ def compensated_sum(values: Any, indicators: Any = None, use_complement: bool = False, *, indices: Any = None,
525
+ start: int = 0, count: Any = None, kahan: bool = False) -> float:
526
+ """Return the compensated (Neumaier) sum of the values (``Tools.CompensatedSum``).
527
+
528
+ Optionally of those an indicator selects, or of the gathered values ``values[indices[i]]``
529
+ for i in [``start``, ``start + count``).
530
+
531
+ Parameters
532
+ ----------
533
+ values : array_like
534
+ The values.
535
+ indicators : array_like of int, optional
536
+ 0 or 1 per value, as for :func:`sum_values`. None sums every value.
537
+ use_complement : bool, optional
538
+ Select the values marked 0; default False.
539
+ indices : array_like of int, optional
540
+ Zero-based positions in ``values`` to gather and sum, in this order; a position may repeat.
541
+ Not combined with ``indicators``.
542
+ start : int, optional
543
+ The first entry of ``indices`` to use; default 0.
544
+ count : int, optional
545
+ The number of entries of ``indices`` to use; default all from ``start`` on.
546
+ kahan : bool, optional
547
+ Use classic Kahan summation instead of Neumaier's method; default False. Kahan's
548
+ ``a = v - c; f = t + a; c = (f - t) - a; t = f`` is reproduced bit for bit, for reproducing
549
+ results of the classic Kahan loop; Neumaier's method is the more accurate. A final infinite
550
+ sum is returned as is, and any value added after the sum becomes infinite gives NaN.
551
+
552
+ Returns
553
+ -------
554
+ float
555
+ The compensated sum. As for :func:`sum_values`, an empty input (or an empty range of
556
+ ``indices``) gives NaN, where :func:`math.fsum` gives 0, and indicators of a different
557
+ length from the values give NaN. A NaN, or both infinities, give NaN and a single
558
+ infinity is returned, the IEEE sum; :func:`math.fsum` raises ``ValueError`` for both
559
+ infinities and ``OverflowError`` for an intermediate overflow, which returns the infinity here.
560
+
561
+ Raises
562
+ ------
563
+ ValueError
564
+ If both ``indicators`` and ``indices`` are given, or the range or a gathered index is out of bounds.
565
+
566
+ Notes
567
+ -----
568
+ Neumaier's improvement of Kahan summation keeps a running correction of the exact rounding
569
+ error of each addition, so the error is bounded by 2u|s| + O(n u²) Σ|x_i| and stays small when
570
+ a term exceeds the running sum. The input crosses into Numerics once.
571
+ """
572
+ extra = ("Boolean",) if kahan else ()
573
+ flag = (True,) if kahan else ()
574
+ if indices is not None:
575
+ if indicators is not None:
576
+ raise ValueError("Give either indicators or indices, not both.")
577
+ net_indices = to_net_ints(indices)
578
+ take = int(net_indices.Length) - int(start) if count is None else int(count)
579
+ member = _compensated_sum_member("IList[Double]", "IList[Int32]", "Int32", "Int32", *extra)
580
+ return float(member(to_net_doubles(values), net_indices, int(start), take, *flag))
581
+ if indicators is None:
582
+ return float(_compensated_sum_member("IList[Double]", *extra)(to_net_doubles(values), *flag))
583
+ member = _compensated_sum_member("IList[Double]", "IList[Int32]", "Boolean", *extra)
584
+ return float(member(to_net_doubles(values), to_net_ints(indicators), bool(use_complement), *flag))
585
+
586
+
587
+ def compensated_cumulative_sum(values: Any, kahan: bool = False) -> np.ndarray:
588
+ """Return the compensated (Neumaier) running sums of the values (``Tools.CompensatedCumulativeSum``).
589
+
590
+ Parameters
591
+ ----------
592
+ values : array_like
593
+ The values.
594
+ kahan : bool, optional
595
+ Use classic Kahan summation instead of Neumaier's method, as in :func:`compensated_sum`;
596
+ entry i is then the Kahan running sum t after value i. Default False.
597
+
598
+ Returns
599
+ -------
600
+ numpy.ndarray
601
+ Entry i is :func:`compensated_sum` of the first i + 1 values, bit for bit; empty for empty input.
602
+ """
603
+ types = ("IList[Double]", "Boolean") if kahan else ("IList[Double]",)
604
+ member = cached_overload(
605
+ f"Tools.CompensatedCumulativeSum({', '.join(types)})",
606
+ lambda: overload(namespace("Numerics").Tools.CompensatedCumulativeSum, *types))
607
+ return to_numpy_doubles(member(to_net_doubles(values), True) if kahan else member(to_net_doubles(values)))
608
+
609
+
610
+ def sum_product(values1: Any, values2: Any) -> float:
611
+ """Return sum(values1 * values2) (``Tools.SumProduct``).
612
+
613
+ Parameters
614
+ ----------
615
+ values1, values2 : array_like
616
+ Equal-length values.
617
+
618
+ Returns
619
+ -------
620
+ float
621
+ The sum of the products.
622
+ """
623
+ return float(namespace("Numerics").Tools.SumProduct(to_net_doubles(values1), to_net_doubles(values2)))
624
+
625
+
626
+ def mean_values(values: Any, indicators: Any = None, use_complement: bool = False) -> float:
627
+ """Return the arithmetic mean, optionally of the values an indicator selects (``Tools.Mean``).
628
+
629
+ Parameters
630
+ ----------
631
+ values : array_like
632
+ The values.
633
+ indicators : array_like of int, optional
634
+ 0 or 1 per value, as for :func:`sum_values`.
635
+ use_complement : bool, optional
636
+ Select the values marked 0; default False.
637
+
638
+ Returns
639
+ -------
640
+ float
641
+ The mean.
642
+ """
643
+ return _masked("Mean", values, indicators, use_complement)
644
+
645
+
646
+ def product(values: Any, indicators: Any = None, use_complement: bool = False) -> float:
647
+ """Return the product, optionally of the values an indicator selects (``Tools.Product``).
648
+
649
+ Parameters
650
+ ----------
651
+ values : array_like
652
+ The values.
653
+ indicators : array_like of int, optional
654
+ 0 or 1 per value, as for :func:`sum_values`.
655
+ use_complement : bool, optional
656
+ Select the values marked 0; default False.
657
+
658
+ Returns
659
+ -------
660
+ float
661
+ The product.
662
+ """
663
+ return _masked("Product", values, indicators, use_complement)
664
+
665
+
666
+ def min_value(values: Any, indicators: Any = None, use_complement: bool = False) -> float:
667
+ """Return the smallest value, optionally of those an indicator selects (``Tools.Min``).
668
+
669
+ Empty input or input holding NaN gives NaN.
670
+
671
+ Parameters
672
+ ----------
673
+ values : array_like
674
+ The values.
675
+ indicators : array_like of int, optional
676
+ 0 or 1 per value, as for :func:`sum_values`.
677
+ use_complement : bool, optional
678
+ Select the values marked 0; default False.
679
+
680
+ Returns
681
+ -------
682
+ float
683
+ The minimum.
684
+ """
685
+ return _masked("Min", values, indicators, use_complement)
686
+
687
+
688
+ def max_value(values: Any, indicators: Any = None, use_complement: bool = False) -> float:
689
+ """Return the largest value, optionally of those an indicator selects (``Tools.Max``).
690
+
691
+ Empty input or input holding NaN gives NaN.
692
+
693
+ Parameters
694
+ ----------
695
+ values : array_like
696
+ The values.
697
+ indicators : array_like of int, optional
698
+ 0 or 1 per value, as for :func:`sum_values`.
699
+ use_complement : bool, optional
700
+ Select the values marked 0; default False.
701
+
702
+ Returns
703
+ -------
704
+ float
705
+ The maximum.
706
+ """
707
+ return _masked("Max", values, indicators, use_complement)
708
+
709
+
710
+ def min_max(values: Any) -> tuple[float, float]:
711
+ """Return the minimum and the maximum in one pass (``Tools.MinMax``).
712
+
713
+ Parameters
714
+ ----------
715
+ values : array_like
716
+ The values.
717
+
718
+ Returns
719
+ -------
720
+ tuple of float
721
+ ``(minimum, maximum)``; both NaN for empty input or input holding NaN.
722
+ """
723
+ low, high = namespace("Numerics").Tools.MinMax(to_net_doubles(values), 0.0, 0.0)
724
+ return float(low), float(high)
725
+
726
+
727
+ def arg_min(values: Any) -> int:
728
+ """Return the index of the smallest value, ignoring NaN (``Tools.ArgMin``).
729
+
730
+ Parameters
731
+ ----------
732
+ values : array_like
733
+ The values.
734
+
735
+ Returns
736
+ -------
737
+ int
738
+ The zero-based index of the first minimum, or -1 when every value is NaN or there are none.
739
+ """
740
+ return int(namespace("Numerics").Tools.ArgMin(to_net_doubles(values)))
741
+
742
+
743
+ def arg_max(values: Any) -> int:
744
+ """Return the index of the largest value, ignoring NaN (``Tools.ArgMax``).
745
+
746
+ Parameters
747
+ ----------
748
+ values : array_like
749
+ The values.
750
+
751
+ Returns
752
+ -------
753
+ int
754
+ The zero-based index of the first maximum, or -1 when every value is NaN or there are none.
755
+ """
756
+ return int(namespace("Numerics").Tools.ArgMax(to_net_doubles(values)))
757
+
758
+
759
+ def log_sum_exp(values: Any, v: Any = None, *, axis: int | None = None) -> Any:
760
+ """Return log(sum(exp(values))) without overflow (``Tools.LogSumExp``).
761
+
762
+ With one argument the whole array is reduced in one crossing by the ``IList<double>``
763
+ overload; with ``axis`` a two-dimensional array is reduced along that axis in one crossing by
764
+ the ``(double[,], int)`` overload; with two arguments, log(exp(values) + exp(v)) is evaluated
765
+ pairwise by the ``(double, double)`` overload, broadcasting arrays. The evaluation and the
766
+ special values are those of ``scipy.special.logsumexp``.
767
+
768
+ Parameters
769
+ ----------
770
+ values : array_like or float
771
+ Log values to reduce, or the first log value of each pair.
772
+ v : float or array_like, optional
773
+ The second log value of each pair.
774
+ axis : int, optional
775
+ 0 or 1 (or -2 or -1): reduce a two-dimensional ``values`` over its rows, giving one value
776
+ per column, or over its columns, giving one value per row. None, the default, reduces
777
+ every value.
778
+
779
+ Returns
780
+ -------
781
+ float or numpy.ndarray
782
+ The reduction, the reductions along ``axis``, or the pairwise values. An empty reduction
783
+ and a reduction of -inf values give -inf, any +inf gives +inf, and NaN propagates.
784
+
785
+ Raises
786
+ ------
787
+ ValueError
788
+ If ``axis`` is given with ``v``, or ``values`` is not two-dimensional, or ``axis`` is not
789
+ one of 0, 1, -2 and -1.
790
+ TypeError
791
+ If ``axis`` is a bool or not an integer.
792
+ """
793
+ if axis is not None:
794
+ if v is not None:
795
+ raise ValueError("Give either v or axis, not both.")
796
+ if isinstance(axis, bool) or not isinstance(axis, (int, np.integer)):
797
+ raise TypeError(f"axis must be an integer; got {type(axis).__name__}.")
798
+ if axis not in (0, 1, -2, -1):
799
+ raise ValueError(f"axis must be 0, 1, -2 or -1; got {axis}.")
800
+ array = np.asarray(values, dtype=np.float64)
801
+ if array.ndim != 2:
802
+ raise ValueError(f"axis needs a 2-D array; got shape {array.shape}.")
803
+ return to_numpy_doubles(namespace("Numerics").Tools.LogSumExp(to_net_matrix(array), int(axis) % 2))
804
+ if v is None:
805
+ return float(_tools_member("LogSumExp", "IList[Double]")(to_net_doubles(values)))
806
+ return broadcast_loop(_tools_member("LogSumExp", "Double", "Double"), values, v)
807
+
808
+
809
+ def sequence(start: float, end: float, step: float = 1) -> np.ndarray:
810
+ """Return start, start + step, ... up to and including ``end`` (``Tools.Sequence``).
811
+
812
+ Integer arguments use the ``int`` overload and give int32 values; any float argument uses
813
+ the ``double`` overload, which accumulates ``v += step``. A zero step gives an empty array.
814
+
815
+ Parameters
816
+ ----------
817
+ start, end : int or float
818
+ The first value and the inclusive limit.
819
+ step : int or float, optional
820
+ The increment (negative counts down); default 1.
821
+
822
+ Returns
823
+ -------
824
+ numpy.ndarray
825
+ The sequence, int32 or float64.
826
+ """
827
+ if all(isinstance(a, (int, np.integer)) and not isinstance(a, bool) for a in (start, end, step)):
828
+ member = _tools_member("Sequence", "Int32", "Int32", "Int32")
829
+ return to_numpy_ints(member(int(start), int(end), int(step)))
830
+ member = _tools_member("Sequence", "Double", "Double", "Double")
831
+ return to_numpy_doubles(member(float(start), float(end), float(step)))
832
+
833
+
834
+ def compress(data: bytes) -> bytes:
835
+ """Compress bytes with the .NET raw deflate stream at the optimal level (``Tools.Compress``).
836
+
837
+ The output is a raw deflate stream, which ``zlib.decompress(out, -15)`` also reads.
838
+
839
+ Parameters
840
+ ----------
841
+ data : bytes
842
+ The bytes to compress.
843
+
844
+ Returns
845
+ -------
846
+ bytes
847
+ The compressed bytes.
848
+ """
849
+ source = _bytes_to_net(np.frombuffer(bytes(data), dtype=np.uint8), "bytes")
850
+ return _bytes_from_net(namespace("Numerics").Tools.Compress(source), np.uint8).tobytes()
851
+
852
+
853
+ def decompress(data: bytes) -> bytes:
854
+ """Inflate a raw deflate stream (``Tools.Decompress``).
855
+
856
+ Parameters
857
+ ----------
858
+ data : bytes
859
+ Bytes from :func:`compress` or any raw deflate stream.
860
+
861
+ Returns
862
+ -------
863
+ bytes
864
+ The decompressed bytes.
865
+ """
866
+ source = _bytes_to_net(np.frombuffer(bytes(data), dtype=np.uint8), "bytes")
867
+ return _bytes_from_net(namespace("Numerics").Tools.Decompress(source), np.uint8).tobytes()
868
+
869
+
870
+ def _complex_to_net(values: Any) -> Any:
871
+ """Copy a 1-D complex array into a ``System.Double[]`` of interleaved real and imaginary parts."""
872
+ array = np.ascontiguousarray(values, dtype=np.complex128)
873
+ if array.ndim != 1:
874
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
875
+ return to_net_doubles(array.view(np.float64))
876
+
877
+
878
+ def _fourier() -> Any:
879
+ """Return ``Numerics.Mathematics.Fourier`` after starting the runtime."""
880
+ return namespace("Numerics.Mathematics").Fourier
881
+
882
+
883
+ def fft(values: Any, inverse: bool = False) -> np.ndarray:
884
+ """Return the discrete Fourier transform of a power-of-2 complex sequence (``Fourier.FFT``).
885
+
886
+ Numerics uses the Numerical Recipes conventions: the forward transform is
887
+ H(k) = sum h(j) exp(+2 pi i j k / n), which equals ``n * numpy.fft.ifft(values)``, and the
888
+ inverse transform returns n times the inverse DFT, which equals ``numpy.fft.fft(values)``.
889
+ The array crosses once each way as interleaved doubles.
890
+
891
+ Parameters
892
+ ----------
893
+ values : array_like of complex
894
+ The sequence; its length must be a power of 2.
895
+ inverse : bool, optional
896
+ Compute the (unnormalized) inverse transform; default False.
897
+
898
+ Returns
899
+ -------
900
+ numpy.ndarray
901
+ The complex transform.
902
+
903
+ Raises
904
+ ------
905
+ ValueError
906
+ If the length is not a power of 2 (raised by Numerics).
907
+ """
908
+ data = _complex_to_net(values)
909
+ _fourier().FFT(data, bool(inverse))
910
+ return to_numpy_doubles(data).view(np.complex128)
911
+
912
+
913
+ def bluestein_fft(values: Any, inverse: bool = False) -> np.ndarray:
914
+ """Return the discrete Fourier transform of a complex sequence of any length (``Fourier.BluesteinFFT``).
915
+
916
+ The conventions are those of :func:`fft`; a length that is not a power of 2 is transformed
917
+ with Bluestein's chirp-z convolution.
918
+
919
+ Parameters
920
+ ----------
921
+ values : array_like of complex
922
+ The sequence, of any positive length.
923
+ inverse : bool, optional
924
+ Compute the (unnormalized) inverse transform; default False.
925
+
926
+ Returns
927
+ -------
928
+ numpy.ndarray
929
+ The complex transform.
930
+
931
+ Raises
932
+ ------
933
+ ValueError
934
+ If the sequence is empty (raised by Numerics).
935
+ """
936
+ member = _fourier().BluesteinFFT
937
+ data = _complex_to_net(values)
938
+ member(data, bool(inverse))
939
+ return to_numpy_doubles(data).view(np.complex128)
940
+
941
+
942
+ def real_fft(values: Any, inverse: bool = False) -> np.ndarray:
943
+ """Return the packed transform of a power-of-2 real sequence (``Fourier.RealFFT``).
944
+
945
+ The forward result holds the positive-frequency half of the :func:`fft` of the real input:
946
+ element 0 is H(0), element 1 is H(n/2) (both real), and elements 2k, 2k + 1 are the real and
947
+ imaginary parts of H(k) for k = 1 .. n/2 - 1. With ``inverse`` a packed array is turned back
948
+ into the real sequence times n/2.
949
+
950
+ Parameters
951
+ ----------
952
+ values : array_like
953
+ The real sequence (or packed transform); its length must be a power of 2.
954
+ inverse : bool, optional
955
+ Compute the inverse transform; default False.
956
+
957
+ Returns
958
+ -------
959
+ numpy.ndarray
960
+ The packed transform, or the unscaled inverse.
961
+
962
+ Raises
963
+ ------
964
+ ValueError
965
+ If the length is not a power of 2 (raised by Numerics).
966
+ """
967
+ data = to_net_doubles(values)
968
+ _fourier().RealFFT(data, bool(inverse))
969
+ return to_numpy_doubles(data)
970
+
971
+
972
+ def fft_convolve(values1: Any, values2: Any) -> np.ndarray:
973
+ """Return the full linear convolution of two real sequences by FFT (``Fourier.Convolve``).
974
+
975
+ The same quantity as ``numpy.convolve(values1, values2, mode="full")``, with an absolute
976
+ round-off of about machine epsilon times log2(n) times the largest output.
977
+
978
+ Parameters
979
+ ----------
980
+ values1, values2 : array_like
981
+ The sequences, each of length at least 1.
982
+
983
+ Returns
984
+ -------
985
+ numpy.ndarray
986
+ The convolution, of length ``len(values1) + len(values2) - 1``.
987
+
988
+ Raises
989
+ ------
990
+ ValueError
991
+ If either sequence is empty (raised by Numerics).
992
+ """
993
+ member = _fourier().Convolve
994
+ return to_numpy_doubles(member(to_net_doubles(values1), to_net_doubles(values2)))
995
+
996
+
997
+ def fft_correlation(values1: Any, values2: Any) -> np.ndarray:
998
+ """Return the circular cross-correlation of two real power-of-2 sequences (``Fourier.Correlation``).
999
+
1000
+ Element k is the sum over j of values1[(j + k) mod n] * values2[j], in wraparound order: lags
1001
+ 0 .. n/2 - 1 first, then the negative lags -n/2 .. -1. This is not a normalized correlation
1002
+ coefficient.
1003
+
1004
+ Parameters
1005
+ ----------
1006
+ values1, values2 : array_like
1007
+ Equal-length real sequences whose length is a power of 2.
1008
+
1009
+ Returns
1010
+ -------
1011
+ numpy.ndarray
1012
+ The correlation at each lag.
1013
+
1014
+ Raises
1015
+ ------
1016
+ ValueError
1017
+ If the lengths are not equal powers of 2 (raised by Numerics).
1018
+ """
1019
+ return to_numpy_doubles(_fourier().Correlation(to_net_doubles(values1), to_net_doubles(values2)))
1020
+
1021
+
1022
+ def fft_autocorrelation(series: Any, lag_max: int) -> np.ndarray:
1023
+ """Return the sample autocorrelation function by FFT (``Fourier.Autocorrelation``).
1024
+
1025
+ r(k) = sum (x_t - mean)(x_(t+k) - mean) / sum (x_t - mean)**2, the linear (not circular)
1026
+ estimate that ``statsmodels.tsa.stattools.acf`` also computes.
1027
+
1028
+ Parameters
1029
+ ----------
1030
+ series : array_like
1031
+ The series.
1032
+ lag_max : int
1033
+ The largest lag.
1034
+
1035
+ Returns
1036
+ -------
1037
+ numpy.ndarray
1038
+ A ``(lag_max + 1, 2)`` array: the lags 0 .. lag_max in column 0 and r(k) in column 1.
1039
+ """
1040
+ return to_numpy_matrix(_fourier().Autocorrelation(to_net_doubles(series), int(lag_max)))
1041
+
1042
+
1043
+ __all__ = [
1044
+ "NumericsConstants", "arg_max", "arg_min", "bluestein_fft", "clamp", "compensated_cumulative_sum",
1045
+ "compensated_sum", "compress", "constants", "copy_sign",
1046
+ "decompress", "denormalize", "destandardize", "distance", "expm1", "fft", "fft_autocorrelation", "fft_convolve",
1047
+ "fft_correlation", "hypot", "is_finite", "is_power_of_two", "log", "log10", "log1p", "log_sum_exp", "max_value",
1048
+ "mean_values", "min_max", "min_value", "next_power_of_two", "normalize", "normalized_distance", "point_distance",
1049
+ "power", "product", "real_fft", "sequence", "sign", "sqr", "standardize", "sum_product", "sum_values",
1050
+ "weighted_distance",
1051
+ ]
1052
+
1053
+ translate_public(globals())