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
pynumerics/marshal.py ADDED
@@ -0,0 +1,733 @@
1
+ """Copy arrays between NumPy and .NET in bulk, preserving every bit.
2
+
3
+ Each non-empty transfer is one bulk copy through the Python buffer protocol, never
4
+ element-by-element indexing, so values keep their exact bit patterns (NaN payloads, signed
5
+ zeros, infinities and subnormals included) and large arrays copy at memory speed.
6
+
7
+ pythonnet exports a .NET array of a primitive type (``Double``, ``Single``, ``Int32``,
8
+ ``Int64``, ``Byte``, ``SByte``, of any rank) through the buffer protocol. Acquiring the buffer
9
+ pins the array with a ``GCHandle`` and releasing it frees the handle; every copy here holds the
10
+ buffer in a ``with memoryview(...)`` block, so the handle is freed when the block ends, also
11
+ when the copy raises. A write allocates the .NET array and assigns the NumPy values into a
12
+ ``numpy.frombuffer`` view of its storage; a read copies such a view into a new NumPy array.
13
+ Both are one ``memcpy`` for contiguous data, the same bits as
14
+ ``System.Runtime.InteropServices.Marshal.Copy`` at less than half of its call cost. Callbacks
15
+ rely on this, since every evaluation copies its array arguments.
16
+
17
+ Two-dimensional arrays (``double[,]``, which Numerics returns from samplers such as
18
+ ``LatinHypercube.Random`` and takes as covariance matrices, and the ``int[,]`` and ``float[,]``
19
+ tables of the graph, optimization and probability wrappers) copy directly into or out of the
20
+ rectangular .NET array: a .NET rectangular array is stored in row major order, the same layout
21
+ as a C-order NumPy array, so element ``[i, j]`` maps exactly.
22
+
23
+ Space-time blocks cross as a flat ``System.Single[]`` in C order together with explicit
24
+ ``(steps, rows, columns)`` dimensions: ``flat[(t * rows + r) * columns + c]`` equals
25
+ ``block[t, r, c]``.
26
+
27
+ pythonnet describes a ``System.Boolean[]`` buffer with the four-byte marshalled size of
28
+ ``Boolean`` rather than its one-byte storage, so ``Boolean[]`` arrays cross as one buffer copy
29
+ into a ``System.Byte[]`` followed by one ``System.Buffer.BlockCopy``, which reinterprets the
30
+ bytes without conversion (a .NET ``Boolean`` occupies one byte, as a NumPy ``bool`` does).
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import math
36
+ from typing import Any
37
+
38
+ import numpy as np
39
+
40
+ from pynumerics.runtime import initialize
41
+
42
+ _INT32_MIN = -(2**31)
43
+ _INT32_MAX = 2**31 - 1
44
+ _INT64_MIN = -(2**63)
45
+ _INT64_MAX = 2**63 - 1
46
+ _INT8_MIN = -(2**7)
47
+ _INT8_MAX = 2**7 - 1
48
+
49
+
50
+ def _clr() -> tuple[Any, Any]:
51
+ """Return the (System, Marshal) handles after guaranteeing runtime initialization."""
52
+ initialize()
53
+ import System # noqa: PLC0415 - resolvable only after runtime initialization
54
+ from System.Runtime.InteropServices import Marshal # noqa: PLC0415
55
+
56
+ return System, Marshal
57
+
58
+
59
+ _BOUND: dict[str, Any] = {}
60
+
61
+
62
+ def _bound() -> dict[str, Any]:
63
+ """Return the array constructors and the ``Marshal.Copy`` read overloads, resolved once per process.
64
+
65
+ pythonnet resolves an overloaded method group on every call; ``Marshal.Copy`` has more than a
66
+ dozen overloads and ``Array.CreateInstance`` six, and resolving them costs many times the copy
67
+ of a short array. Binding the exact overloads once keeps a small transfer in the microseconds.
68
+ The ``Marshal.Copy`` overloads serve only reads of an argument that is not an array of the
69
+ expected element type, which they convert or refuse as they always have.
70
+ """
71
+ if not _BOUND:
72
+ System, Marshal = _clr()
73
+ import clr # noqa: PLC0415 - resolvable only after runtime initialization
74
+
75
+ clr_type = clr.GetClrType
76
+ pointer, length = System.IntPtr, System.Int32
77
+ doubles, ints, singles = System.Array[System.Double], System.Array[System.Int32], System.Array[System.Single]
78
+ longs, raw = System.Array[System.Int64], System.Array[System.Byte]
79
+ create_matrix = System.Array.CreateInstance.Overloads[System.Type, length, length]
80
+ _BOUND.update({
81
+ "doubles": doubles, "ints": ints, "singles": singles, "pointer": pointer,
82
+ "longs": longs, "bytes": raw, "sbytes": System.Array[System.SByte], "bools": System.Array[System.Boolean],
83
+ "block_copy": System.Buffer.BlockCopy, "double_type": clr_type(System.Double),
84
+ "create_matrix": create_matrix, "objects": System.Array[System.Object],
85
+ "element_types": {np.dtype(np.float64): clr_type(System.Double), np.dtype(np.int32): clr_type(System.Int32),
86
+ np.dtype(np.float32): clr_type(System.Single)},
87
+ "doubles_matrix": type(create_matrix(clr_type(System.Double), 0, 0)),
88
+ "doubles_out": Marshal.Copy.Overloads[doubles, length, pointer, length],
89
+ "ints_out": Marshal.Copy.Overloads[ints, length, pointer, length],
90
+ "singles_out": Marshal.Copy.Overloads[singles, length, pointer, length],
91
+ "longs_out": Marshal.Copy.Overloads[longs, length, pointer, length],
92
+ })
93
+ return _BOUND
94
+
95
+
96
+ def _fill(target: Any, array: np.ndarray) -> Any:
97
+ """Copy ``array`` into the storage of ``target``, a new .NET primitive array of the same size, in C order.
98
+
99
+ ``array`` must already have the native NumPy dtype of the element type of ``target`` (float64
100
+ for ``Double``, int32 for ``Int32`` and so on), and ``target`` must hold ``array.size``
101
+ elements, at any rank. Acquiring the buffer pins ``target``; the ``with`` block releases it,
102
+ also when the copy raises. A same-dtype NumPy assignment copies the bits unchanged: one
103
+ ``memcpy`` for contiguous input, a C-order strided copy otherwise. Returns ``target``.
104
+ """
105
+ if array.size:
106
+ with memoryview(target) as view:
107
+ if array.ndim == 1:
108
+ np.frombuffer(view, dtype=array.dtype)[:] = array
109
+ else:
110
+ np.frombuffer(view, dtype=array.dtype).reshape(array.shape)[...] = array
111
+ return target
112
+
113
+
114
+ def _read(values: Any, dtype: Any) -> np.ndarray:
115
+ """Copy a .NET primitive array whose element type matches ``dtype`` into a new NumPy array of its shape.
116
+
117
+ One ``memcpy`` of the pinned .NET storage; the ``with`` block releases the buffer. The caller
118
+ guarantees the element type, since the buffer bytes are read as ``dtype``.
119
+ """
120
+ with memoryview(values) as view:
121
+ shape = view.shape
122
+ if len(shape) == 1:
123
+ return np.frombuffer(view, dtype=dtype).copy()
124
+ return np.frombuffer(view, dtype=dtype).reshape(shape).copy()
125
+
126
+
127
+ def _flat_doubles(array: np.ndarray) -> Any:
128
+ """Copy a float64 array of any shape (contiguous, strided or broadcast) into a new ``System.Double[]`` in C order.
129
+
130
+ The values cross in one assignment, with no intermediate contiguous copy; the elements are
131
+ those of ``to_net_doubles(array.ravel())``, bit for bit.
132
+ """
133
+ return _fill(_bound()["doubles"](array.size), array)
134
+
135
+
136
+ def _net_rectangular(array: np.ndarray) -> Any:
137
+ """Copy a two-dimensional float64, int32 or float32 array into a new .NET rectangular array of that element type."""
138
+ bound = _bound()
139
+ rows, cols = (int(dimension) for dimension in array.shape)
140
+ return _fill(bound["create_matrix"](bound["element_types"][array.dtype], rows, cols), array)
141
+
142
+
143
+ _BUFFER_FORMATS = {np.dtype(np.float64): "d", np.dtype(np.int32): "i", np.dtype(np.float32): "f"}
144
+ """The buffer-protocol format pythonnet reports for a .NET ``Double``, ``Int32`` and ``Single`` array."""
145
+
146
+
147
+ def _numpy_rectangular(values: Any, dtype: Any) -> np.ndarray:
148
+ """Copy a .NET rectangular array (``double[,]``, ``int[,]`` or ``float[,]``) into a new 2-D array of ``dtype``.
149
+
150
+ Raises
151
+ ------
152
+ TypeError
153
+ If ``values`` is not a rank-2 array whose element type matches ``dtype``.
154
+ """
155
+ with memoryview(values) as view:
156
+ if view.ndim != 2 or view.format != _BUFFER_FORMATS[np.dtype(dtype)]:
157
+ raise TypeError(f"Expected a rank-2 .NET array of {np.dtype(dtype)}; got format {view.format!r} "
158
+ f"and rank {view.ndim}.")
159
+ return np.frombuffer(view, dtype=dtype).reshape(view.shape).copy()
160
+
161
+
162
+ def to_net_doubles(values: Any) -> Any:
163
+ """Copy a one-dimensional array into a new ``System.Double[]``, preserving every bit.
164
+
165
+ The input is converted with ``numpy.ascontiguousarray(values, dtype=numpy.float64)``
166
+ first. That conversion is exact for float32 values and for integers up to 2**53 in
167
+ magnitude, and it turns a scalar into a one-element array. Starts the .NET runtime if
168
+ it is not already running.
169
+
170
+ Parameters
171
+ ----------
172
+ values : array_like
173
+ Values to copy, one-dimensional after conversion.
174
+
175
+ Returns
176
+ -------
177
+ System.Double[]
178
+ A new .NET array holding the float64 values.
179
+
180
+ Raises
181
+ ------
182
+ ValueError
183
+ If ``values`` is not one-dimensional after conversion.
184
+ """
185
+ bound = _bound()
186
+ array = np.ascontiguousarray(values, dtype=np.float64)
187
+ if array.ndim == 0:
188
+ array = array.reshape(1)
189
+ if array.ndim != 1:
190
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
191
+ return _fill(bound["doubles"](array.size), array)
192
+
193
+
194
+ def _optional_doubles(values: Any) -> Any:
195
+ """Copy values into a ``System.Double[]``, passing None through as a null reference."""
196
+ return None if values is None else to_net_doubles(values)
197
+
198
+
199
+ _LINEAR_ALGEBRA: list[Any] = []
200
+
201
+
202
+ def _linear_algebra() -> Any:
203
+ """Return the ``Numerics.Mathematics.LinearAlgebra`` namespace after starting the runtime, imported once."""
204
+ if not _LINEAR_ALGEBRA:
205
+ initialize()
206
+ import importlib # noqa: PLC0415
207
+
208
+ _LINEAR_ALGEBRA.append(importlib.import_module("Numerics.Mathematics.LinearAlgebra"))
209
+ return _LINEAR_ALGEBRA[0]
210
+
211
+
212
+ _CONSTRUCTORS: dict[str, Any] = {}
213
+
214
+
215
+ def _construct(name: str, argument: Any) -> Any:
216
+ """Construct a Numerics ``Matrix`` from a ``double[,]`` or a ``Vector`` from a ``double[]`` (``name``).
217
+
218
+ pythonnet resolves a constructor overload on every call, about 30 microseconds for the
219
+ ``Matrix`` constructors; invoking the one ``ConstructorInfo``, found once per process, runs
220
+ the same constructor at about a quarter of that cost. Both constructors throw only for a null
221
+ argument, and the callers pass a new array, so no exception reaches the reflection wrapper.
222
+ """
223
+ invoke = _CONSTRUCTORS.get(name)
224
+ if invoke is None:
225
+ bound = _bound() # starts the runtime before the System import
226
+ import clr # noqa: PLC0415
227
+ import System # noqa: PLC0415
228
+
229
+ owner = getattr(_linear_algebra(), name)
230
+ argument_type = clr.GetClrType(bound["doubles_matrix"] if name == "Matrix" else bound["doubles"])
231
+ constructor = clr.GetClrType(owner).GetConstructor(System.Array[System.Type]([argument_type]))
232
+ invoke = _CONSTRUCTORS[name] = constructor.Invoke.Overloads[bound["objects"]]
233
+ return invoke(_bound()["objects"]([argument]))
234
+
235
+
236
+ def _numerics_vector(values: Any, *, unwrap: bool = False, pass_through: bool = False) -> Any:
237
+ """Return a Numerics ``Vector`` for ``values``, by default a new one copied from a 1-D array.
238
+
239
+ With ``unwrap``, a wrapper whose ``net`` attribute holds a Numerics ``Vector`` (such as
240
+ :class:`pynumerics.linalg.Vector`) gives that vector, not a copy. With ``pass_through``, a
241
+ Numerics ``Vector`` is returned as it is, None gives None (a null reference), and any other
242
+ input is flattened before the copy. Otherwise the values are copied with :func:`to_net_doubles`.
243
+ """
244
+ linear_algebra = _linear_algebra()
245
+ if unwrap and isinstance(getattr(values, "net", None), linear_algebra.Vector):
246
+ return values.net
247
+ if pass_through:
248
+ if values is None or isinstance(values, linear_algebra.Vector):
249
+ return values
250
+ values = np.ravel(np.asarray(values, dtype=np.float64))
251
+ return _construct("Vector", to_net_doubles(values))
252
+
253
+
254
+ def to_numpy_doubles(values: Any) -> np.ndarray:
255
+ """Copy a ``System.Double[]`` into a new float64 NumPy array, preserving every bit.
256
+
257
+ The result is always a copy, so it never aliases .NET state. A ``System.Double[]`` is read
258
+ with one copy through the buffer protocol. A .NET collection of doubles that is not an array
259
+ (``List<double>``, ``ReadOnlyCollection<double>``) is first copied into a ``System.Double[]``
260
+ with one ``ICollection<double>.CopyTo`` call, never element by element.
261
+
262
+ Parameters
263
+ ----------
264
+ values : System.Double[] or System.Collections.Generic.ICollection[System.Double]
265
+ .NET array or collection to copy.
266
+
267
+ Returns
268
+ -------
269
+ numpy.ndarray
270
+ A new one-dimensional float64 array with one element per value.
271
+ """
272
+ bound = _bound()
273
+ doubles = bound["doubles"]
274
+ if type(values) is not doubles:
275
+ # An interface wrapper (IList<double>) exposes the object behind it.
276
+ values = getattr(values, "__implementation__", values)
277
+ if type(values) is doubles:
278
+ with memoryview(values) as view:
279
+ return np.frombuffer(view, dtype=np.float64).copy()
280
+ if not hasattr(values, "Length"):
281
+ array = doubles(int(values.Count))
282
+ values.CopyTo(array, 0)
283
+ return _read(array, np.float64)
284
+ length = int(values.Length)
285
+ out = np.empty(length, dtype=np.float64)
286
+ if length:
287
+ bound["doubles_out"](values, 0, bound["pointer"](out.ctypes.data), length)
288
+ return out
289
+
290
+
291
+ def to_net_matrix(values: Any) -> Any:
292
+ """Copy a two-dimensional array into a new ``System.Double[,]``, preserving every bit.
293
+
294
+ Parameters
295
+ ----------
296
+ values : array_like
297
+ Values to copy, two-dimensional after conversion to C-order float64.
298
+
299
+ Returns
300
+ -------
301
+ System.Double[,]
302
+ A new rectangular .NET array with the same shape.
303
+
304
+ Raises
305
+ ------
306
+ ValueError
307
+ If ``values`` is not two-dimensional after conversion.
308
+ """
309
+ array = np.ascontiguousarray(values, dtype=np.float64)
310
+ if array.ndim != 2:
311
+ raise ValueError(f"Expected a 2-D array; got shape {array.shape}.")
312
+ return _net_rectangular(array)
313
+
314
+
315
+ def _numerics_matrix(values: Any, *, unwrap: bool = False, pass_through: bool = False) -> Any:
316
+ """Return a Numerics ``Matrix`` for ``values``, by default a new one copied from a 2-D array.
317
+
318
+ With ``unwrap``, a wrapper whose ``net`` attribute holds a Numerics ``Matrix`` (such as
319
+ :class:`pynumerics.linalg.Matrix`) gives that matrix, not a copy. With ``pass_through``, a
320
+ Numerics ``Matrix`` is returned as it is and any other input is promoted to at least two
321
+ dimensions before the copy, so a scalar gives a 1 x 1 matrix. Otherwise the values are copied
322
+ with :func:`to_net_matrix`.
323
+ """
324
+ linear_algebra = _linear_algebra()
325
+ if unwrap and isinstance(getattr(values, "net", None), linear_algebra.Matrix):
326
+ return values.net
327
+ if pass_through:
328
+ if isinstance(values, linear_algebra.Matrix):
329
+ return values
330
+ values = np.atleast_2d(np.asarray(values, dtype=np.float64))
331
+ return _construct("Matrix", to_net_matrix(values))
332
+
333
+
334
+ def _parameter_set_arrays(sets: Any) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
335
+ """Read a .NET ``ParameterSet[]`` into ``(values, fitness, weight)`` arrays, one row or entry per set.
336
+
337
+ ``values`` has one row per set and as many columns as the longest set. A set without values
338
+ (a null ``Values`` array) gives a row of NaN, and a shorter set is padded with NaN, so every
339
+ value Numerics returned is kept. Numerics fills a failed replicate with NaN values of the full
340
+ length, so such a row reads as NaN throughout.
341
+ """
342
+ count = len(sets)
343
+ width = max((len(item.Values) for item in sets if item.Values is not None), default=0)
344
+ values = np.full((count, width), np.nan)
345
+ fitness, weight = np.empty(count), np.empty(count)
346
+ for index, item in enumerate(sets):
347
+ if item.Values is not None:
348
+ row = to_numpy_doubles(item.Values)
349
+ values[index, :row.size] = row
350
+ fitness[index], weight[index] = float(item.Fitness), float(item.Weight)
351
+ return values, fitness, weight
352
+
353
+
354
+ def to_numpy_matrix(values: Any) -> np.ndarray:
355
+ """Copy a ``System.Double[,]`` into a new two-dimensional float64 array, preserving every bit.
356
+
357
+ Parameters
358
+ ----------
359
+ values : System.Double[,]
360
+ Rectangular .NET array to copy.
361
+
362
+ Returns
363
+ -------
364
+ numpy.ndarray
365
+ A new C-order float64 array of shape ``(values.GetLength(0), values.GetLength(1))``.
366
+
367
+ Raises
368
+ ------
369
+ ValueError
370
+ If ``values`` is not a rank-2 array.
371
+ """
372
+ bound = _bound()
373
+ if type(values) is bound["doubles_matrix"]:
374
+ return _read(values, np.float64)
375
+ System, _ = _clr()
376
+ if int(values.Rank) != 2:
377
+ raise ValueError(f"Expected a rank-2 .NET array; got rank {int(values.Rank)}.")
378
+ rows, cols = int(values.GetLength(0)), int(values.GetLength(1))
379
+ size = rows * cols
380
+ if size == 0:
381
+ return np.empty((rows, cols), dtype=np.float64)
382
+ if values.GetType().GetElementType() == bound["double_type"]:
383
+ return _read(values, np.float64)
384
+ flat = System.Array.CreateInstance(System.Double, size)
385
+ System.Buffer.BlockCopy(values, 0, flat, 0, size * 8)
386
+ return to_numpy_doubles(flat).reshape((rows, cols))
387
+
388
+
389
+ def _range_error(label: str, minimum: int, maximum: int, low: object, high: object) -> OverflowError:
390
+ """Return the error for values from ``low`` to ``high`` that leave the ``label`` range."""
391
+ return OverflowError(f"Values must lie in the {label} range [{minimum}, {maximum}]; got values from {low} to "
392
+ f"{high}.")
393
+
394
+
395
+ def _checked_integers(values: Any, dtype: type, label: str, minimum: int, maximum: int) -> np.ndarray:
396
+ """Convert values to a contiguous integer array of ``dtype``, refusing any value outside its range.
397
+
398
+ Booleans cross as 0 and 1 and floating-point values truncate toward zero; a value outside
399
+ ``[minimum, maximum]``, or a floating-point value that is not finite, raises ``OverflowError``
400
+ instead of wrapping. Complex, date, time, text and other values raise ``TypeError``.
401
+ """
402
+ source = np.asarray(values)
403
+ kind = source.dtype.kind
404
+ if kind == "O":
405
+ items = source.ravel().tolist()
406
+ if not all(isinstance(item, (int, np.integer)) for item in items):
407
+ raise TypeError(f"Values must be booleans, integers, or floating-point numbers to cross as {label}; got "
408
+ "an object array holding another type.")
409
+ if items and (min(items) < minimum or max(items) > maximum):
410
+ raise _range_error(label, minimum, maximum, min(items), max(items))
411
+ return np.ascontiguousarray(np.array(items, dtype=np.int64).reshape(source.shape), dtype=dtype)
412
+ if kind not in "biuf":
413
+ raise TypeError(f"Values must be booleans, integers, or floating-point numbers to cross as {label}; got dtype "
414
+ f"{source.dtype}.")
415
+ if kind in "iuf" and source.size:
416
+ if kind == "f":
417
+ truncated = np.trunc(source)
418
+ low, high = float(truncated.min()), float(truncated.max())
419
+ if not (math.isfinite(low) and math.isfinite(high)):
420
+ raise OverflowError(f"Values must lie in the {label} range [{minimum}, {maximum}]; "
421
+ "got a value that is not finite.")
422
+ else:
423
+ low, high = int(source.min()), int(source.max())
424
+ if low < minimum or high > maximum:
425
+ raise _range_error(label, minimum, maximum, low, high)
426
+ return np.ascontiguousarray(source, dtype=dtype)
427
+
428
+
429
+ def _int32_array(values: Any) -> np.ndarray:
430
+ """Convert values to a contiguous int32 array, refusing any value outside the int32 range."""
431
+ return _checked_integers(values, np.int32, "Int32", _INT32_MIN, _INT32_MAX)
432
+
433
+
434
+ def to_net_ints(values: Any) -> Any:
435
+ """Copy a one-dimensional array into a new ``System.Int32[]``, preserving every bit.
436
+
437
+ A value outside the int32 range raises ``OverflowError`` instead of wrapping,
438
+ whether it arrives as a Python integer, a wider NumPy integer, or a floating-point
439
+ value. Floating-point values inside the range truncate toward zero. Booleans cross
440
+ as 0 and 1; complex, date, time, text and other values are refused.
441
+
442
+ Parameters
443
+ ----------
444
+ values : array_like
445
+ Integer values to copy, one-dimensional after conversion.
446
+
447
+ Returns
448
+ -------
449
+ System.Int32[]
450
+ A new .NET array holding the int32 values.
451
+
452
+ Raises
453
+ ------
454
+ OverflowError
455
+ If a value lies outside the int32 range or is not finite; raised before the
456
+ runtime starts.
457
+ TypeError
458
+ If the values are not booleans, integers, or floating-point numbers.
459
+ ValueError
460
+ If ``values`` is not one-dimensional after conversion.
461
+ """
462
+ array = _int32_array(values)
463
+ bound = _bound()
464
+ if array.ndim != 1:
465
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
466
+ return _fill(bound["ints"](array.size), array)
467
+
468
+
469
+ def to_numpy_ints(values: Any) -> np.ndarray:
470
+ """Copy a ``System.Int32[]`` into a new int32 NumPy array, preserving every bit.
471
+
472
+ Parameters
473
+ ----------
474
+ values : System.Int32[]
475
+ .NET array to copy.
476
+
477
+ Returns
478
+ -------
479
+ numpy.ndarray
480
+ A new one-dimensional int32 array with ``values.Length`` elements.
481
+ """
482
+ bound = _bound()
483
+ if type(values) is bound["ints"]:
484
+ return _read(values, np.int32)
485
+ length = int(values.Length)
486
+ out = np.empty(length, dtype=np.int32)
487
+ if length:
488
+ bound["ints_out"](values, 0, bound["pointer"](out.ctypes.data), length)
489
+ return out
490
+
491
+
492
+ def to_net_longs(values: Any) -> Any:
493
+ """Copy a one-dimensional array into a new ``System.Int64[]``, preserving every bit.
494
+
495
+ A value outside the int64 range raises ``OverflowError`` instead of wrapping, whether
496
+ it arrives as a Python integer, an unsigned 64-bit NumPy integer, or a floating-point
497
+ value. Floating-point values inside the range truncate toward zero. Booleans cross as
498
+ 0 and 1; complex, date, time, text and other values are refused.
499
+
500
+ Parameters
501
+ ----------
502
+ values : array_like
503
+ Integer values to copy, one-dimensional after conversion.
504
+
505
+ Returns
506
+ -------
507
+ System.Int64[]
508
+ A new .NET array holding the int64 values.
509
+
510
+ Raises
511
+ ------
512
+ OverflowError
513
+ If a value lies outside the int64 range or is not finite; raised before the
514
+ runtime starts.
515
+ TypeError
516
+ If the values are not booleans, integers, or floating-point numbers.
517
+ ValueError
518
+ If ``values`` is not one-dimensional after conversion.
519
+ """
520
+ array = _checked_integers(values, np.int64, "Int64", _INT64_MIN, _INT64_MAX)
521
+ bound = _bound()
522
+ if array.ndim != 1:
523
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
524
+ return _fill(bound["longs"](array.size), array)
525
+
526
+
527
+ def to_numpy_longs(values: Any) -> np.ndarray:
528
+ """Copy a ``System.Int64[]`` into a new int64 NumPy array, preserving every bit.
529
+
530
+ Parameters
531
+ ----------
532
+ values : System.Int64[]
533
+ .NET array to copy, for example the ticks of a ``DateTime`` series.
534
+
535
+ Returns
536
+ -------
537
+ numpy.ndarray
538
+ A new one-dimensional int64 array with ``values.Length`` elements.
539
+ """
540
+ bound = _bound()
541
+ if type(values) is bound["longs"]:
542
+ return _read(values, np.int64)
543
+ length = int(values.Length)
544
+ out = np.empty(length, dtype=np.int64)
545
+ if length:
546
+ bound["longs_out"](values, 0, bound["pointer"](out.ctypes.data), length)
547
+ return out
548
+
549
+
550
+ def _bytes_to_net(array: np.ndarray, kind: str) -> Any:
551
+ """Copy a contiguous one-byte-per-element array into a new .NET array of ``kind`` through one byte copy."""
552
+ bound = _bound()
553
+ target = bound[kind](array.size)
554
+ if kind != "bools":
555
+ return _fill(target, array)
556
+ if array.size:
557
+ raw = _fill(bound["bytes"](array.size), array.view(np.uint8))
558
+ bound["block_copy"](raw, 0, target, 0, array.size)
559
+ return target
560
+
561
+
562
+ def _bytes_from_net(values: Any, dtype: type) -> np.ndarray:
563
+ """Copy a one-byte-per-element .NET array into a new NumPy array of ``dtype`` through one byte copy."""
564
+ bound = _bound()
565
+ if type(values) is bound["bytes"] or type(values) is bound["sbytes"]:
566
+ return _read(values, dtype)
567
+ length = int(values.Length)
568
+ raw = bound["bytes"](length)
569
+ if length:
570
+ bound["block_copy"](values, 0, raw, 0, length)
571
+ return _read(raw, dtype)
572
+
573
+
574
+ def to_net_sbytes(values: Any) -> Any:
575
+ """Copy a one-dimensional array into a new ``System.SByte[]``, preserving every bit.
576
+
577
+ A value outside the int8 range raises ``OverflowError`` instead of wrapping.
578
+ Floating-point values inside the range truncate toward zero. Booleans cross as 0 and 1;
579
+ complex, date, time, text and other values are refused.
580
+
581
+ Parameters
582
+ ----------
583
+ values : array_like
584
+ Integer values to copy, one-dimensional after conversion.
585
+
586
+ Returns
587
+ -------
588
+ System.SByte[]
589
+ A new .NET array holding the int8 values.
590
+
591
+ Raises
592
+ ------
593
+ OverflowError
594
+ If a value lies outside the int8 range or is not finite; raised before the
595
+ runtime starts.
596
+ TypeError
597
+ If the values are not booleans, integers, or floating-point numbers.
598
+ ValueError
599
+ If ``values`` is not one-dimensional after conversion.
600
+ """
601
+ array = _checked_integers(values, np.int8, "SByte", _INT8_MIN, _INT8_MAX)
602
+ if array.ndim != 1:
603
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
604
+ return _bytes_to_net(array, "sbytes")
605
+
606
+
607
+ def to_numpy_sbytes(values: Any) -> np.ndarray:
608
+ """Copy a ``System.SByte[]`` into a new int8 NumPy array, preserving every bit.
609
+
610
+ Parameters
611
+ ----------
612
+ values : System.SByte[]
613
+ .NET array to copy.
614
+
615
+ Returns
616
+ -------
617
+ numpy.ndarray
618
+ A new one-dimensional int8 array with ``values.Length`` elements.
619
+ """
620
+ return _bytes_from_net(values, np.int8)
621
+
622
+
623
+ def to_net_bools(values: Any) -> Any:
624
+ """Copy a one-dimensional boolean array into a new ``System.Boolean[]``.
625
+
626
+ Only boolean input is accepted: integers or floating-point values are refused rather
627
+ than interpreted as truth values. An empty input of any type gives an empty array.
628
+
629
+ Parameters
630
+ ----------
631
+ values : array_like
632
+ Boolean values to copy, one-dimensional after conversion.
633
+
634
+ Returns
635
+ -------
636
+ System.Boolean[]
637
+ A new .NET array holding the values.
638
+
639
+ Raises
640
+ ------
641
+ TypeError
642
+ If the values are not booleans; raised before the runtime starts.
643
+ ValueError
644
+ If ``values`` is not one-dimensional after conversion.
645
+ """
646
+ source = np.asarray(values)
647
+ if source.dtype.kind != "b" and source.size:
648
+ raise TypeError(f"Values must be booleans to cross as Boolean; got dtype {source.dtype}.")
649
+ array = np.ascontiguousarray(source, dtype=np.bool_)
650
+ if array.ndim != 1:
651
+ raise ValueError(f"Expected a 1-D array; got shape {array.shape}.")
652
+ return _bytes_to_net(array, "bools")
653
+
654
+
655
+ def to_numpy_bools(values: Any) -> np.ndarray:
656
+ """Copy a ``System.Boolean[]`` into a new boolean NumPy array.
657
+
658
+ Parameters
659
+ ----------
660
+ values : System.Boolean[]
661
+ .NET array to copy.
662
+
663
+ Returns
664
+ -------
665
+ numpy.ndarray
666
+ A new one-dimensional bool array with ``values.Length`` elements; any nonzero
667
+ .NET byte reads as True.
668
+ """
669
+ return _bytes_from_net(values, np.uint8) != 0
670
+
671
+
672
+ def to_net_singles(block: Any) -> tuple[Any, tuple[int, int, int]]:
673
+ """Flatten a ``(steps, rows, columns)`` block into a ``System.Single[]`` and its shape.
674
+
675
+ The block is converted with ``numpy.ascontiguousarray(block, dtype=numpy.float32)``
676
+ first, which rounds float64 values to the nearest float32, and is then copied in C
677
+ order, so ``flat[(t * rows + r) * columns + c]`` equals ``block[t, r, c]``.
678
+
679
+ Parameters
680
+ ----------
681
+ block : array_like
682
+ Block with shape ``(steps, rows, columns)``.
683
+
684
+ Returns
685
+ -------
686
+ flat : System.Single[]
687
+ A new .NET array holding the ``steps * rows * columns`` float32 values.
688
+ dims : tuple of int
689
+ The block shape ``(steps, rows, columns)``.
690
+
691
+ Raises
692
+ ------
693
+ ValueError
694
+ If ``block`` is not three-dimensional after conversion.
695
+ """
696
+ array = np.ascontiguousarray(block, dtype=np.float32)
697
+ if array.ndim != 3:
698
+ raise ValueError(f"Expected a (steps, rows, cols) block; got shape {array.shape}.")
699
+ steps, rows, cols = (int(dimension) for dimension in array.shape)
700
+ return _fill(_bound()["singles"](array.size), array), (steps, rows, cols)
701
+
702
+
703
+ def to_numpy_singles(values: Any, dims: tuple[int, int, int]) -> np.ndarray:
704
+ """Copy a flat ``System.Single[]`` back into a ``(steps, rows, columns)`` float32 block.
705
+
706
+ Parameters
707
+ ----------
708
+ values : System.Single[]
709
+ Flat C-order .NET array, as produced by :func:`to_net_singles`.
710
+ dims : tuple of int
711
+ Block shape ``(steps, rows, columns)``.
712
+
713
+ Returns
714
+ -------
715
+ numpy.ndarray
716
+ A new float32 array with shape ``dims``, preserving every bit.
717
+
718
+ Raises
719
+ ------
720
+ ValueError
721
+ If ``values.Length`` differs from ``steps * rows * columns``.
722
+ """
723
+ bound = _bound()
724
+ steps, rows, cols = (int(dimension) for dimension in dims)
725
+ length = int(values.Length)
726
+ if length != steps * rows * cols:
727
+ raise ValueError(f"Flat length {length} does not match dims {dims}.")
728
+ if type(values) is bound["singles"]:
729
+ return _read(values, np.float32).reshape((steps, rows, cols))
730
+ out = np.empty(length, dtype=np.float32)
731
+ if length:
732
+ bound["singles_out"](values, 0, bound["pointer"](out.ctypes.data), length)
733
+ return out.reshape((steps, rows, cols))