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/linalg.py ADDED
@@ -0,0 +1,1408 @@
1
+ """Matrices, vectors and decompositions from ``Numerics.Mathematics.LinearAlgebra``.
2
+
3
+ :class:`Matrix` and :class:`Vector` hold a .NET ``Matrix`` or ``Vector`` (``.net``) and expose
4
+ its members; matrix-valued results stay in .NET as new wrappers, and ``numpy.asarray`` copies
5
+ the values out. The decompositions (:class:`CholeskyDecomposition`, :class:`LUDecomposition`,
6
+ :class:`QRDecomposition`, :class:`SingularValueDecomposition` and
7
+ :class:`EigenValueDecomposition`) take any 2-D array or a :class:`Matrix` and return their
8
+ factors and solutions as NumPy arrays. :func:`regularize` and :func:`make_symmetric_positive_definite`
9
+ wrap ``MatrixRegularization``. Every array crosses in one bulk copy each way, bit for bit.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from collections.abc import Callable, Sequence
15
+ from typing import Any
16
+
17
+ import numpy as np
18
+
19
+ from pynumerics._util import cached_overload, net_enum, overload
20
+ from pynumerics.callbacks import scalar_function
21
+ from pynumerics.enums import NetEnum
22
+ from pynumerics.errors import translate_public
23
+ from pynumerics.marshal import (
24
+ _construct,
25
+ _linear_algebra,
26
+ _numerics_matrix,
27
+ _numerics_vector,
28
+ to_net_doubles,
29
+ to_net_matrix,
30
+ to_numpy_doubles,
31
+ to_numpy_matrix,
32
+ )
33
+
34
+
35
+ def _numpy_matrix(net: Any) -> np.ndarray:
36
+ """Copy a .NET ``Matrix`` into a new 2-D float64 array."""
37
+ return to_numpy_matrix(net.Array)
38
+
39
+
40
+ def _numpy_vector(net: Any) -> np.ndarray:
41
+ """Copy a .NET ``Vector`` into a new 1-D float64 array."""
42
+ return to_numpy_doubles(net.Array)
43
+
44
+
45
+ def _is_matrix_operand(values: Any) -> bool:
46
+ """Return whether an operand is a :class:`Matrix` or a 2-D array (otherwise a vector)."""
47
+ return isinstance(values, Matrix) or (not isinstance(values, Vector) and np.ndim(values) == 2)
48
+
49
+
50
+ def _solve(net: Any, b: Any, *extra: float) -> np.ndarray:
51
+ """Call ``Solve`` with a vector or a matrix right-hand side and copy the solution out."""
52
+ if _is_matrix_operand(b):
53
+ return _numpy_matrix(net.Solve(_numerics_matrix(b, unwrap=True), *extra))
54
+ return _numpy_vector(net.Solve(_numerics_vector(b, unwrap=True), *extra))
55
+
56
+
57
+ class DecompositionMethod(NetEnum):
58
+ """Matrix decomposition choices (``Numerics.Mathematics.LinearAlgebra.DecompositionMethod``)."""
59
+
60
+ CHOLESKY = "Cholesky"
61
+ SINGULAR_VALUE = "SingularValue"
62
+
63
+
64
+ class EigenSolverMethod(NetEnum):
65
+ """Symmetric eigen-decomposition algorithms (``Numerics.Mathematics.LinearAlgebra.EigenSolverMethod``)."""
66
+
67
+ JACOBI = "Jacobi"
68
+ QL = "QL"
69
+
70
+
71
+ class Matrix:
72
+ """A dense matrix of doubles (``Numerics.Mathematics.LinearAlgebra.Matrix``).
73
+
74
+ Parameters
75
+ ----------
76
+ values : array_like or int or Vector
77
+ A 2-D array; a 1-D array or a :class:`Vector` (a single column); or the number of rows
78
+ of a zero matrix (square unless ``columns`` is given). With ``by_column``, a sequence of
79
+ 1-D arrays.
80
+ columns : int, optional
81
+ The number of columns of a zero matrix.
82
+ by_column : bool, optional
83
+ Build from a sequence of 1-D arrays, as columns (True) or rows (False).
84
+
85
+ Attributes
86
+ ----------
87
+ net : Numerics.Mathematics.LinearAlgebra.Matrix
88
+ The wrapped .NET matrix.
89
+ """
90
+
91
+ def __init__(self, values: Any, columns: int | None = None, *, by_column: bool | None = None) -> None:
92
+ la = _linear_algebra()
93
+ if isinstance(values, Vector):
94
+ self.net = la.Matrix(values.net)
95
+ elif isinstance(values, (int, np.integer)) and not isinstance(values, bool):
96
+ self.net = la.Matrix(int(values)) if columns is None else la.Matrix(int(values), int(columns))
97
+ elif by_column is not None:
98
+ import System # noqa: PLC0415 - started by _linear_algebra()
99
+ from System.Collections.Generic import List # noqa: PLC0415
100
+
101
+ arrays = List[System.Array[System.Double]]()
102
+ for item in values:
103
+ arrays.Add(to_net_doubles(item))
104
+ self.net = la.Matrix(arrays, bool(by_column))
105
+ elif np.ndim(values) == 1:
106
+ self.net = la.Matrix(to_net_doubles(values))
107
+ else:
108
+ self.net = _construct("Matrix", to_net_matrix(values))
109
+
110
+ @classmethod
111
+ def from_net(cls, net: Any) -> Matrix:
112
+ """Wrap an existing .NET matrix without copying it.
113
+
114
+ Parameters
115
+ ----------
116
+ net : Numerics.Mathematics.LinearAlgebra.Matrix
117
+ The .NET matrix.
118
+
119
+ Returns
120
+ -------
121
+ Matrix
122
+ The wrapper.
123
+ """
124
+ matrix = cls.__new__(cls)
125
+ matrix.net = net
126
+ return matrix
127
+
128
+ @classmethod
129
+ def from_xml(cls, xml: str) -> Matrix:
130
+ """Read a matrix written by :meth:`to_xml` (the ``Matrix(XElement)`` constructor).
131
+
132
+ Parameters
133
+ ----------
134
+ xml : str
135
+ The ``<Matrix>`` element text.
136
+
137
+ Returns
138
+ -------
139
+ Matrix
140
+ The matrix.
141
+ """
142
+ from System.Xml.Linq import XElement # noqa: PLC0415
143
+
144
+ _linear_algebra()
145
+ return cls.from_net(_linear_algebra().Matrix(XElement.Parse(str(xml))))
146
+
147
+ def __array__(self, dtype: Any = None, copy: Any = None) -> np.ndarray:
148
+ values = _numpy_matrix(self.net)
149
+ return values if dtype is None else values.astype(dtype)
150
+
151
+ def __getitem__(self, index: tuple[int, int]) -> float:
152
+ row, column = index
153
+ return float(self.net[int(row), int(column)])
154
+
155
+ def __setitem__(self, index: tuple[int, int], value: float) -> None:
156
+ row, column = index
157
+ self.net[int(row), int(column)] = float(value)
158
+
159
+ def __repr__(self) -> str:
160
+ return f"Matrix({self.to_array()!r})"
161
+
162
+ @property
163
+ def number_of_rows(self) -> int:
164
+ """The number of rows (``NumberOfRows``)."""
165
+ return int(self.net.NumberOfRows)
166
+
167
+ @property
168
+ def number_of_columns(self) -> int:
169
+ """The number of columns (``NumberOfColumns``)."""
170
+ return int(self.net.NumberOfColumns)
171
+
172
+ @property
173
+ def shape(self) -> tuple[int, int]:
174
+ """``(number_of_rows, number_of_columns)``."""
175
+ return self.number_of_rows, self.number_of_columns
176
+
177
+ @property
178
+ def header(self) -> list[str] | None:
179
+ """The column header text (``Header``)."""
180
+ header = self.net.Header
181
+ return None if header is None else [str(h) for h in header]
182
+
183
+ @header.setter
184
+ def header(self, value: Sequence[str] | None) -> None:
185
+ import System # noqa: PLC0415 - the matrix exists, so the runtime is running
186
+
187
+ self.net.Header = None if value is None else System.Array[System.String]([str(v) for v in value])
188
+
189
+ @property
190
+ def is_symmetric(self) -> bool:
191
+ """Whether every pair agrees to 1e-12 times the largest magnitude (``IsSymmetric``)."""
192
+ return bool(self.net.IsSymmetric)
193
+
194
+ @property
195
+ def is_square(self) -> bool:
196
+ """Whether the matrix is square (``IsSquare``)."""
197
+ return bool(self.net.IsSquare)
198
+
199
+ @property
200
+ def array(self) -> np.ndarray:
201
+ """A copy of the underlying ``double[,]`` (``Array``)."""
202
+ return _numpy_matrix(self.net)
203
+
204
+ def to_array(self) -> np.ndarray:
205
+ """Return the values as a new 2-D array (``ToArray``).
206
+
207
+ Returns
208
+ -------
209
+ numpy.ndarray
210
+ The values.
211
+ """
212
+ return to_numpy_matrix(self.net.ToArray())
213
+
214
+ def to_xml(self) -> str:
215
+ """Return the matrix as XML text (``ToXElement``).
216
+
217
+ Returns
218
+ -------
219
+ str
220
+ The ``<Matrix>`` element, values written with round-trip precision.
221
+ """
222
+ return str(self.net.ToXElement().ToString())
223
+
224
+ def clone(self) -> Matrix:
225
+ """Return a copy (``Clone``).
226
+
227
+ Returns
228
+ -------
229
+ Matrix
230
+ The copy.
231
+ """
232
+ return Matrix.from_net(self.net.Clone())
233
+
234
+ def row(self, index: int) -> np.ndarray:
235
+ """Return a copy of a row (``Row``).
236
+
237
+ Parameters
238
+ ----------
239
+ index : int
240
+ The zero-based row index.
241
+
242
+ Returns
243
+ -------
244
+ numpy.ndarray
245
+ The row.
246
+ """
247
+ return to_numpy_doubles(self.net.Row(int(index)))
248
+
249
+ def column(self, index: int) -> np.ndarray:
250
+ """Return a copy of a column (``Column``).
251
+
252
+ Parameters
253
+ ----------
254
+ index : int
255
+ The zero-based column index.
256
+
257
+ Returns
258
+ -------
259
+ numpy.ndarray
260
+ The column.
261
+ """
262
+ return to_numpy_doubles(self.net.Column(int(index)))
263
+
264
+ def diagonal(self) -> np.ndarray:
265
+ """Return the min(rows, columns) diagonal elements (``Diagonal()``).
266
+
267
+ Returns
268
+ -------
269
+ numpy.ndarray
270
+ The diagonal.
271
+ """
272
+ return to_numpy_doubles(self.net.Diagonal())
273
+
274
+ @staticmethod
275
+ def diagonal_matrix(values: Any) -> Matrix:
276
+ """Return a diagonal matrix (the static ``Matrix.Diagonal``).
277
+
278
+ Parameters
279
+ ----------
280
+ values : Matrix or Vector or array_like
281
+ A square matrix (its diagonal is kept) or a 1-D array of diagonal entries.
282
+
283
+ Returns
284
+ -------
285
+ Matrix
286
+ The diagonal matrix.
287
+ """
288
+ la = _linear_algebra()
289
+ if _is_matrix_operand(values):
290
+ return Matrix.from_net(la.Matrix.Diagonal.Overloads[la.Matrix](_numerics_matrix(values, unwrap=True)))
291
+ return Matrix.from_net(la.Matrix.Diagonal.Overloads[la.Vector](_numerics_vector(values, unwrap=True)))
292
+
293
+ def upper_triangle(self) -> Matrix:
294
+ """Return the upper triangle, with zeros below the diagonal (``UpperTriangle``).
295
+
296
+ Returns
297
+ -------
298
+ Matrix
299
+ The upper-triangular matrix.
300
+ """
301
+ return Matrix.from_net(self.net.UpperTriangle())
302
+
303
+ def lower_triangle(self) -> Matrix:
304
+ """Return the lower triangle, with zeros above the diagonal (``LowerTriangle``).
305
+
306
+ Returns
307
+ -------
308
+ Matrix
309
+ The lower-triangular matrix.
310
+ """
311
+ return Matrix.from_net(self.net.LowerTriangle())
312
+
313
+ def determinant(self) -> float:
314
+ """Return the determinant, by LU decomposition (``Determinant``).
315
+
316
+ Returns
317
+ -------
318
+ float
319
+ The determinant det(A).
320
+ """
321
+ return float(self.net.Determinant())
322
+
323
+ def inverse(self) -> Matrix:
324
+ """Return the inverse, by LU decomposition (``Inverse``).
325
+
326
+ Returns
327
+ -------
328
+ Matrix
329
+ A^-1.
330
+ """
331
+ return Matrix.from_net(self.net.Inverse())
332
+
333
+ def transpose(self) -> Matrix:
334
+ """Return the transpose (``Transpose``).
335
+
336
+ Returns
337
+ -------
338
+ Matrix
339
+ A^T.
340
+ """
341
+ return Matrix.from_net(self.net.Transpose())
342
+
343
+ def trace(self) -> float:
344
+ """Return the sum of the diagonal (``Trace``).
345
+
346
+ Returns
347
+ -------
348
+ float
349
+ The trace tr(A).
350
+ """
351
+ return float(self.net.Trace())
352
+
353
+ @staticmethod
354
+ def identity(size: int) -> Matrix:
355
+ """Return the identity matrix (``Identity``).
356
+
357
+ Parameters
358
+ ----------
359
+ size : int
360
+ The number of rows and columns.
361
+
362
+ Returns
363
+ -------
364
+ Matrix
365
+ I.
366
+ """
367
+ return Matrix.from_net(_linear_algebra().Matrix.Identity(int(size)))
368
+
369
+ @staticmethod
370
+ def outer(x: Any, y: Any) -> Matrix:
371
+ """Return the outer product x y^T (``Outer``).
372
+
373
+ Parameters
374
+ ----------
375
+ x, y : Vector or array_like
376
+ 1-D operands.
377
+
378
+ Returns
379
+ -------
380
+ Matrix
381
+ The ``len(x)`` by ``len(y)`` product.
382
+ """
383
+ return Matrix.from_net(_linear_algebra().Matrix.Outer(_numerics_vector(x, unwrap=True),
384
+ _numerics_vector(y, unwrap=True)))
385
+
386
+ def column_means(self) -> np.ndarray:
387
+ """Return the mean of each column (``ColumnMeans``).
388
+
389
+ Returns
390
+ -------
391
+ numpy.ndarray
392
+ One mean per column.
393
+ """
394
+ return _numpy_vector(self.net.ColumnMeans())
395
+
396
+ def apply(self, func: Callable[[float], float] | str) -> None:
397
+ """Replace every element ``a`` by ``func(a)`` in place (``Apply``).
398
+
399
+ Parameters
400
+ ----------
401
+ func : callable or str
402
+ A Python callable (one crossing per element) or C# lambda source compiled with
403
+ Roslyn (no crossing per element), as :func:`pynumerics.callbacks.scalar_function`
404
+ accepts.
405
+ """
406
+ self.net.Apply(scalar_function(func))
407
+
408
+ def sqrt(self) -> None:
409
+ """Take the square root of every element in place (``Sqrt``)."""
410
+ self.net.Sqrt()
411
+
412
+ def sqr(self) -> None:
413
+ """Square every element in place (``Sqr``)."""
414
+ self.net.Sqr()
415
+
416
+ def log(self) -> None:
417
+ """Take the natural logarithm of every element in place (``Log``)."""
418
+ self.net.Log()
419
+
420
+ def exp(self) -> None:
421
+ """Exponentiate every element in place (``Exp``)."""
422
+ self.net.Exp()
423
+
424
+ def sum(self) -> float:
425
+ """Return the sum of every element (``Sum``).
426
+
427
+ Returns
428
+ -------
429
+ float
430
+ The sum.
431
+ """
432
+ return float(self.net.Sum())
433
+
434
+ def multiply(self, other: Any) -> Matrix | Vector:
435
+ """Return the product with a matrix, a vector or a scalar (``Multiply``).
436
+
437
+ Parameters
438
+ ----------
439
+ other : Matrix or Vector or array_like or float
440
+ A matrix (or 2-D array), a vector (or 1-D array) or a scalar.
441
+
442
+ Returns
443
+ -------
444
+ Matrix or Vector
445
+ A B or A s (a :class:`Matrix`), or A x (a :class:`Vector`).
446
+ """
447
+ if _is_matrix_operand(other):
448
+ member = cached_overload("Matrix.Multiply(Matrix)",
449
+ lambda: _linear_algebra().Matrix.Multiply.Overloads[_linear_algebra().Matrix])
450
+ return Matrix.from_net(member(self.net, _numerics_matrix(other, unwrap=True)))
451
+ if isinstance(other, Vector) or np.ndim(other) == 1:
452
+ member = cached_overload("Matrix.Multiply(Vector)",
453
+ lambda: _linear_algebra().Matrix.Multiply.Overloads[_linear_algebra().Vector])
454
+ return Vector.from_net(member(self.net, _numerics_vector(other, unwrap=True)))
455
+ return Matrix.from_net(overload(self.net.Multiply, "Double")(float(other)))
456
+
457
+ def transpose_multiply(self, other: Any) -> Matrix:
458
+ """Return A^T B without forming the transpose (``TransposeMultiply``).
459
+
460
+ Parameters
461
+ ----------
462
+ other : Matrix or array_like
463
+ B, with as many rows as this matrix.
464
+
465
+ Returns
466
+ -------
467
+ Matrix
468
+ A^T B; exactly symmetric when B is A.
469
+ """
470
+ return Matrix.from_net(self.net.TransposeMultiply(_numerics_matrix(other, unwrap=True)))
471
+
472
+ def divide(self, scalar: float) -> Matrix:
473
+ """Return the matrix divided by a scalar (``Divide``).
474
+
475
+ Parameters
476
+ ----------
477
+ scalar : float
478
+ The divisor.
479
+
480
+ Returns
481
+ -------
482
+ Matrix
483
+ A / s.
484
+ """
485
+ return Matrix.from_net(self.net.Divide(float(scalar)))
486
+
487
+ def add(self, other: Any) -> Matrix:
488
+ """Return the elementwise sum (``Add``).
489
+
490
+ Parameters
491
+ ----------
492
+ other : Matrix or array_like
493
+ A matrix of the same shape.
494
+
495
+ Returns
496
+ -------
497
+ Matrix
498
+ A + B.
499
+ """
500
+ return Matrix.from_net(self.net.Add(_numerics_matrix(other, unwrap=True)))
501
+
502
+ def subtract(self, other: Any) -> Matrix:
503
+ """Return the elementwise difference (``Subtract``).
504
+
505
+ Parameters
506
+ ----------
507
+ other : Matrix or array_like
508
+ A matrix of the same shape.
509
+
510
+ Returns
511
+ -------
512
+ Matrix
513
+ A - B.
514
+ """
515
+ return Matrix.from_net(self.net.Subtract(_numerics_matrix(other, unwrap=True)))
516
+
517
+
518
+ class Vector:
519
+ """A dense vector of doubles (``Numerics.Mathematics.LinearAlgebra.Vector``).
520
+
521
+ Parameters
522
+ ----------
523
+ values : array_like or int
524
+ The 1-D values, or the length of a new vector.
525
+ fill : float, optional
526
+ The value of every element of a new vector of length ``values``; None gives zeros.
527
+
528
+ Attributes
529
+ ----------
530
+ net : Numerics.Mathematics.LinearAlgebra.Vector
531
+ The wrapped .NET vector.
532
+ """
533
+
534
+ def __init__(self, values: Any, fill: float | None = None) -> None:
535
+ la = _linear_algebra()
536
+ if isinstance(values, (int, np.integer)) and not isinstance(values, bool):
537
+ self.net = la.Vector(int(values)) if fill is None else la.Vector(int(values), float(fill))
538
+ else:
539
+ self.net = _construct("Vector", to_net_doubles(values))
540
+
541
+ @classmethod
542
+ def from_net(cls, net: Any) -> Vector:
543
+ """Wrap an existing .NET vector without copying it.
544
+
545
+ Parameters
546
+ ----------
547
+ net : Numerics.Mathematics.LinearAlgebra.Vector
548
+ The .NET vector.
549
+
550
+ Returns
551
+ -------
552
+ Vector
553
+ The wrapper.
554
+ """
555
+ vector = cls.__new__(cls)
556
+ vector.net = net
557
+ return vector
558
+
559
+ def __array__(self, dtype: Any = None, copy: Any = None) -> np.ndarray:
560
+ values = _numpy_vector(self.net)
561
+ return values if dtype is None else values.astype(dtype)
562
+
563
+ def __len__(self) -> int:
564
+ return int(self.net.Length)
565
+
566
+ def __getitem__(self, index: int) -> float:
567
+ return float(self.net[int(index)])
568
+
569
+ def __setitem__(self, index: int, value: float) -> None:
570
+ self.net[int(index)] = float(value)
571
+
572
+ def __repr__(self) -> str:
573
+ return f"Vector({self.to_array()!r})"
574
+
575
+ @property
576
+ def length(self) -> int:
577
+ """The number of elements (``Length``)."""
578
+ return int(self.net.Length)
579
+
580
+ @property
581
+ def header(self) -> str | None:
582
+ """The header text (``Header``)."""
583
+ header = self.net.Header
584
+ return None if header is None else str(header)
585
+
586
+ @header.setter
587
+ def header(self, value: str | None) -> None:
588
+ self.net.Header = None if value is None else str(value)
589
+
590
+ @property
591
+ def array(self) -> np.ndarray:
592
+ """A copy of the underlying ``double[]`` (``Array``)."""
593
+ return _numpy_vector(self.net)
594
+
595
+ def to_array(self) -> np.ndarray:
596
+ """Return the values as a new 1-D array (``ToArray``).
597
+
598
+ Returns
599
+ -------
600
+ numpy.ndarray
601
+ The values.
602
+ """
603
+ return to_numpy_doubles(self.net.ToArray())
604
+
605
+ def to_list(self) -> list[float]:
606
+ """Return the values as a Python list (``ToList``).
607
+
608
+ Returns
609
+ -------
610
+ list of float
611
+ The values.
612
+ """
613
+ return to_numpy_doubles(self.net.ToList()).tolist()
614
+
615
+ def clone(self) -> Vector:
616
+ """Return a copy (``Clone``).
617
+
618
+ Returns
619
+ -------
620
+ Vector
621
+ The copy.
622
+ """
623
+ return Vector.from_net(self.net.Clone())
624
+
625
+ def clear(self) -> None:
626
+ """Set every element to zero in place (``Clear``)."""
627
+ self.net.Clear()
628
+
629
+ def copy_from(self, other: Any) -> None:
630
+ """Overwrite the elements with those of another vector of the same length (``CopyFrom``).
631
+
632
+ Parameters
633
+ ----------
634
+ other : Vector or array_like
635
+ The source.
636
+ """
637
+ self.net.CopyFrom(_numerics_vector(other, unwrap=True))
638
+
639
+ def sum(self) -> float:
640
+ """Return the sum of the elements (``Sum``).
641
+
642
+ Returns
643
+ -------
644
+ float
645
+ The sum.
646
+ """
647
+ return float(self.net.Sum())
648
+
649
+ def norm(self) -> float:
650
+ """Return the Euclidean norm (``Norm``).
651
+
652
+ Returns
653
+ -------
654
+ float
655
+ ||v||.
656
+ """
657
+ return float(self.net.Norm())
658
+
659
+ def norm_squared(self) -> float:
660
+ """Return the squared Euclidean norm (``NormSquared``).
661
+
662
+ Returns
663
+ -------
664
+ float
665
+ ||v||**2.
666
+ """
667
+ return float(self.net.NormSquared())
668
+
669
+ @staticmethod
670
+ def distance(a: Any, b: Any) -> float:
671
+ """Return the Euclidean distance ||a - b|| (``Distance``).
672
+
673
+ Parameters
674
+ ----------
675
+ a, b : Vector or array_like
676
+ Equal-length vectors.
677
+
678
+ Returns
679
+ -------
680
+ float
681
+ The distance.
682
+ """
683
+ return float(_linear_algebra().Vector.Distance(_numerics_vector(a, unwrap=True),
684
+ _numerics_vector(b, unwrap=True)))
685
+
686
+ @staticmethod
687
+ def dot_product(a: Any, b: Any) -> float:
688
+ """Return the dot product a . b (``DotProduct``).
689
+
690
+ Parameters
691
+ ----------
692
+ a, b : Vector or array_like
693
+ Equal-length vectors.
694
+
695
+ Returns
696
+ -------
697
+ float
698
+ The dot product.
699
+ """
700
+ return float(_linear_algebra().Vector.DotProduct(_numerics_vector(a, unwrap=True),
701
+ _numerics_vector(b, unwrap=True)))
702
+
703
+ @staticmethod
704
+ def project(a: Any, b: Any) -> Vector:
705
+ """Return the projection of a onto b, (a . b / b . b) b (``Project``).
706
+
707
+ Parameters
708
+ ----------
709
+ a, b : Vector or array_like
710
+ Equal-length vectors.
711
+
712
+ Returns
713
+ -------
714
+ Vector
715
+ The projection.
716
+ """
717
+ return Vector.from_net(_linear_algebra().Vector.Project(_numerics_vector(a, unwrap=True),
718
+ _numerics_vector(b, unwrap=True)))
719
+
720
+ def add(self, other: Any) -> Vector:
721
+ """Return the elementwise sum (``Add``).
722
+
723
+ Parameters
724
+ ----------
725
+ other : Vector or array_like
726
+ An equal-length vector.
727
+
728
+ Returns
729
+ -------
730
+ Vector
731
+ The sum v + w.
732
+ """
733
+ if isinstance(other, Vector):
734
+ return Vector.from_net(self.net.Add.Overloads[_linear_algebra().Vector](other.net))
735
+ return Vector.from_net(overload(self.net.Add, "Double[]")(to_net_doubles(other)))
736
+
737
+ def subtract(self, other: Any) -> Vector:
738
+ """Return the elementwise difference (``Subtract``).
739
+
740
+ Parameters
741
+ ----------
742
+ other : Vector or array_like
743
+ An equal-length vector.
744
+
745
+ Returns
746
+ -------
747
+ Vector
748
+ The difference v - w.
749
+ """
750
+ if isinstance(other, Vector):
751
+ return Vector.from_net(self.net.Subtract.Overloads[_linear_algebra().Vector](other.net))
752
+ return Vector.from_net(overload(self.net.Subtract, "Double[]")(to_net_doubles(other)))
753
+
754
+ def multiply(self, other: Any) -> Vector:
755
+ """Return a matrix times this vector, the elementwise product, or the scaled vector (``Multiply``).
756
+
757
+ Parameters
758
+ ----------
759
+ other : Matrix or Vector or array_like or float
760
+ A matrix M (or 2-D array) whose column count is the vector length, for M v; a vector
761
+ (or 1-D array) for the elementwise product; or a scalar.
762
+
763
+ Returns
764
+ -------
765
+ Vector
766
+ The product.
767
+ """
768
+ la = _linear_algebra()
769
+ if _is_matrix_operand(other):
770
+ return Vector.from_net(self.net.Multiply.Overloads[la.Matrix](_numerics_matrix(other, unwrap=True)))
771
+ if isinstance(other, Vector):
772
+ return Vector.from_net(self.net.Multiply.Overloads[la.Vector](other.net))
773
+ if np.ndim(other) == 1:
774
+ return Vector.from_net(overload(self.net.Multiply, "Double[]")(to_net_doubles(other)))
775
+ return Vector.from_net(overload(self.net.Multiply, "Double")(float(other)))
776
+
777
+ def divide(self, scalar: float) -> Vector:
778
+ """Return the vector divided by a scalar (``Divide``).
779
+
780
+ Parameters
781
+ ----------
782
+ scalar : float
783
+ The divisor.
784
+
785
+ Returns
786
+ -------
787
+ Vector
788
+ The quotient v / s.
789
+ """
790
+ return Vector.from_net(self.net.Divide(float(scalar)))
791
+
792
+
793
+ class CholeskyDecomposition:
794
+ """The Cholesky factorization A = L L^T of a symmetric positive-definite matrix (``CholeskyDecomposition``).
795
+
796
+ Only the upper triangle and the diagonal of A are read. A pivot is rejected when it is at or
797
+ below ``relative_tolerance`` times the corresponding diagonal entry.
798
+
799
+ Parameters
800
+ ----------
801
+ matrix : Matrix or array_like
802
+ The symmetric positive-definite matrix.
803
+ relative_tolerance : float, optional
804
+ The scale-relative pivot tolerance in [0, 1); None uses
805
+ :meth:`default_relative_tolerance` of the dimension.
806
+
807
+ Attributes
808
+ ----------
809
+ net : Numerics.Mathematics.LinearAlgebra.CholeskyDecomposition
810
+ The wrapped .NET decomposition.
811
+
812
+ Raises
813
+ ------
814
+ RuntimeError
815
+ If the matrix is not positive definite (raised by Numerics).
816
+ ValueError
817
+ If the matrix is not square or the tolerance is outside [0, 1) (raised by Numerics).
818
+ """
819
+
820
+ def __init__(self, matrix: Any, relative_tolerance: float | None = None) -> None:
821
+ la = _linear_algebra()
822
+ self.net = (la.CholeskyDecomposition(_numerics_matrix(matrix, unwrap=True)) if relative_tolerance is None
823
+ else la.CholeskyDecomposition(_numerics_matrix(matrix, unwrap=True), float(relative_tolerance)))
824
+
825
+ @classmethod
826
+ def try_decompose(cls, matrix: Any, minimum_relative_jitter: float | None = None,
827
+ maximum_relative_jitter: float | None = None) -> tuple[bool, CholeskyDecomposition | None,
828
+ float]:
829
+ """Factor A, retrying on A + jitter I with an escalating jitter (``TryCholesky``).
830
+
831
+ The jitters are s * minimum * 10**k up to s * maximum, with s the mean diagonal of A
832
+ (or 1); the defaults are the Numerics ones, 1e-10 and 1e-2.
833
+
834
+ Parameters
835
+ ----------
836
+ matrix : Matrix or array_like
837
+ The symmetric matrix.
838
+ minimum_relative_jitter, maximum_relative_jitter : float, optional
839
+ The jitter range relative to s; give both or neither.
840
+
841
+ Returns
842
+ -------
843
+ success : bool
844
+ Whether A or a jittered A was factored.
845
+ decomposition : CholeskyDecomposition or None
846
+ The factorization of A + jitter I on success.
847
+ jitter : float
848
+ The absolute jitter added: 0 when A factors as given, NaN on failure.
849
+ """
850
+ member = _linear_algebra().CholeskyDecomposition.TryCholesky
851
+ if minimum_relative_jitter is None and maximum_relative_jitter is None:
852
+ ok, net, jitter = member(_numerics_matrix(matrix, unwrap=True), None, 0.0)
853
+ else:
854
+ ok, net, jitter = member(_numerics_matrix(matrix, unwrap=True), float(minimum_relative_jitter),
855
+ float(maximum_relative_jitter), None, 0.0)
856
+ if not ok:
857
+ return False, None, float(jitter)
858
+ decomposition = cls.__new__(cls)
859
+ decomposition.net = net
860
+ return True, decomposition, float(jitter)
861
+
862
+ @staticmethod
863
+ def default_relative_tolerance(dimension: int) -> float:
864
+ """Return the default pivot tolerance for a dimension (``DefaultRelativeTolerance``).
865
+
866
+ Parameters
867
+ ----------
868
+ dimension : int
869
+ The matrix order n.
870
+
871
+ Returns
872
+ -------
873
+ float
874
+ The tolerance (2 n eps).
875
+ """
876
+ return float(_linear_algebra().CholeskyDecomposition.DefaultRelativeTolerance(int(dimension)))
877
+
878
+ @property
879
+ def relative_tolerance(self) -> float:
880
+ """The pivot tolerance used (``RelativeTolerance``)."""
881
+ return float(self.net.RelativeTolerance)
882
+
883
+ @property
884
+ def l(self) -> np.ndarray: # noqa: E743 - the factor's conventional name
885
+ """The lower-triangular factor L (``L``)."""
886
+ return _numpy_matrix(self.net.L)
887
+
888
+ @property
889
+ def a(self) -> np.ndarray:
890
+ """The decomposed matrix (``A``; A + jitter I after :meth:`try_decompose`)."""
891
+ return _numpy_matrix(self.net.A)
892
+
893
+ @property
894
+ def is_positive_definite(self) -> bool:
895
+ """Whether the factorization succeeded (``IsPositiveDefinite``)."""
896
+ return bool(self.net.IsPositiveDefinite)
897
+
898
+ def solve(self, b: Any) -> np.ndarray:
899
+ """Solve A x = b (``Solve``).
900
+
901
+ Parameters
902
+ ----------
903
+ b : Vector or array_like
904
+ The right-hand side.
905
+
906
+ Returns
907
+ -------
908
+ numpy.ndarray
909
+ The solution x.
910
+ """
911
+ return _numpy_vector(self.net.Solve(_numerics_vector(b, unwrap=True)))
912
+
913
+ def forward(self, b: Any) -> np.ndarray:
914
+ """Solve L y = b by forward substitution (``Forward``).
915
+
916
+ Parameters
917
+ ----------
918
+ b : Vector or array_like
919
+ The right-hand side.
920
+
921
+ Returns
922
+ -------
923
+ numpy.ndarray
924
+ The solution y.
925
+ """
926
+ return _numpy_vector(self.net.Forward(_numerics_vector(b, unwrap=True)))
927
+
928
+ def backward(self, y: Any) -> np.ndarray:
929
+ """Solve L^T x = y by back substitution (``Backward``).
930
+
931
+ Parameters
932
+ ----------
933
+ y : Vector or array_like
934
+ The right-hand side.
935
+
936
+ Returns
937
+ -------
938
+ numpy.ndarray
939
+ The solution x.
940
+ """
941
+ return _numpy_vector(self.net.Backward(_numerics_vector(y, unwrap=True)))
942
+
943
+ def inverse(self) -> np.ndarray:
944
+ """Return A^-1 from the factor (``InverseA``).
945
+
946
+ Returns
947
+ -------
948
+ numpy.ndarray
949
+ The inverse.
950
+ """
951
+ return _numpy_matrix(self.net.InverseA())
952
+
953
+ def determinant(self) -> float:
954
+ """Return det(A) from the factor (``Determinant``).
955
+
956
+ Returns
957
+ -------
958
+ float
959
+ The determinant.
960
+ """
961
+ return float(self.net.Determinant())
962
+
963
+ def log_determinant(self) -> float:
964
+ """Return ln det(A) = 2 sum ln L[i, i] (``LogDeterminant``).
965
+
966
+ Returns
967
+ -------
968
+ float
969
+ The log determinant.
970
+ """
971
+ return float(self.net.LogDeterminant())
972
+
973
+
974
+ class LUDecomposition:
975
+ """The LU decomposition with partial pivoting of a square matrix (``LUDecomposition``).
976
+
977
+ Parameters
978
+ ----------
979
+ matrix : Matrix or array_like
980
+ The square matrix; it is copied, not modified.
981
+
982
+ Attributes
983
+ ----------
984
+ net : Numerics.Mathematics.LinearAlgebra.LUDecomposition
985
+ The wrapped .NET decomposition.
986
+
987
+ Raises
988
+ ------
989
+ ValueError
990
+ If the matrix is not square (raised by Numerics).
991
+ """
992
+
993
+ def __init__(self, matrix: Any) -> None:
994
+ self.net = _linear_algebra().LUDecomposition(_numerics_matrix(matrix, unwrap=True))
995
+
996
+ @property
997
+ def lu(self) -> np.ndarray:
998
+ """The packed factors of the row-permuted matrix (``LU``; unit diagonal of L implied)."""
999
+ return _numpy_matrix(self.net.LU)
1000
+
1001
+ @property
1002
+ def a(self) -> np.ndarray:
1003
+ """The decomposed matrix (``A``)."""
1004
+ return _numpy_matrix(self.net.A)
1005
+
1006
+ def solve(self, b: Any) -> np.ndarray:
1007
+ """Solve A x = b for a vector or a matrix of right-hand sides (``Solve``).
1008
+
1009
+ Parameters
1010
+ ----------
1011
+ b : Vector or Matrix or array_like
1012
+ A 1-D right-hand side or a 2-D array of them (one per column).
1013
+
1014
+ Returns
1015
+ -------
1016
+ numpy.ndarray
1017
+ The solution x, shaped like ``b``.
1018
+ """
1019
+ return _solve(self.net, b)
1020
+
1021
+ def inverse(self) -> np.ndarray:
1022
+ """Return A^-1 (``InverseA``).
1023
+
1024
+ Returns
1025
+ -------
1026
+ numpy.ndarray
1027
+ The inverse.
1028
+ """
1029
+ return _numpy_matrix(self.net.InverseA())
1030
+
1031
+ def determinant(self) -> float:
1032
+ """Return det(A) (``Determinant``).
1033
+
1034
+ Returns
1035
+ -------
1036
+ float
1037
+ The determinant.
1038
+ """
1039
+ return float(self.net.Determinant())
1040
+
1041
+
1042
+ class QRDecomposition:
1043
+ """The Householder QR decomposition A = Q R of an m-by-n matrix (``QRDecomposition``).
1044
+
1045
+ Parameters
1046
+ ----------
1047
+ matrix : Matrix or array_like
1048
+ The matrix.
1049
+
1050
+ Attributes
1051
+ ----------
1052
+ net : Numerics.Mathematics.LinearAlgebra.QRDecomposition
1053
+ The wrapped .NET decomposition.
1054
+ """
1055
+
1056
+ def __init__(self, matrix: Any) -> None:
1057
+ self.net = _linear_algebra().QRDecomposition(_numerics_matrix(matrix, unwrap=True))
1058
+
1059
+ @property
1060
+ def q(self) -> np.ndarray:
1061
+ """The orthogonal factor Q (``Q``)."""
1062
+ return _numpy_matrix(self.net.Q)
1063
+
1064
+ @property
1065
+ def r(self) -> np.ndarray:
1066
+ """The upper-triangular factor R (``RMatrix``)."""
1067
+ return _numpy_matrix(self.net.RMatrix)
1068
+
1069
+ def solve(self, b: Any) -> np.ndarray:
1070
+ """Solve A x = b (least squares when m > n) for a vector or a matrix (``Solve``).
1071
+
1072
+ Parameters
1073
+ ----------
1074
+ b : Vector or Matrix or array_like
1075
+ A 1-D right-hand side or a 2-D array of them.
1076
+
1077
+ Returns
1078
+ -------
1079
+ numpy.ndarray
1080
+ The solution x.
1081
+
1082
+ Raises
1083
+ ------
1084
+ ValueError
1085
+ If ``b`` does not match A or A is numerically rank-deficient (raised by Numerics).
1086
+ """
1087
+ return _solve(self.net, b)
1088
+
1089
+
1090
+ class SingularValueDecomposition:
1091
+ """The singular value decomposition A = U diag(W) V^T (``SingularValueDecomposition``).
1092
+
1093
+ A ``threshold`` argument below zero, the Numerics default, means the roundoff-based
1094
+ :attr:`threshold`; singular values below the threshold count as zero.
1095
+
1096
+ Parameters
1097
+ ----------
1098
+ matrix : Matrix or array_like
1099
+ The m-by-n matrix.
1100
+
1101
+ Attributes
1102
+ ----------
1103
+ net : Numerics.Mathematics.LinearAlgebra.SingularValueDecomposition
1104
+ The wrapped .NET decomposition.
1105
+ """
1106
+
1107
+ def __init__(self, matrix: Any) -> None:
1108
+ self.net = _linear_algebra().SingularValueDecomposition(_numerics_matrix(matrix, unwrap=True))
1109
+
1110
+ @property
1111
+ def a(self) -> np.ndarray:
1112
+ """The decomposed matrix (``A``)."""
1113
+ return _numpy_matrix(self.net.A)
1114
+
1115
+ @property
1116
+ def u(self) -> np.ndarray:
1117
+ """The m-by-n column-orthogonal factor U (``U``)."""
1118
+ return _numpy_matrix(self.net.U)
1119
+
1120
+ @property
1121
+ def v(self) -> np.ndarray:
1122
+ """The n-by-n orthogonal factor V, so A = U diag(w) V^T (``V``)."""
1123
+ return _numpy_matrix(self.net.V)
1124
+
1125
+ @property
1126
+ def w(self) -> np.ndarray:
1127
+ """The singular values, in decreasing order (``W``)."""
1128
+ return _numpy_vector(self.net.W)
1129
+
1130
+ @property
1131
+ def threshold(self) -> float:
1132
+ """The default zero threshold 0.5 sqrt(m + n + 1) w[0] eps (``Threshold``)."""
1133
+ return float(self.net.Threshold)
1134
+
1135
+ @property
1136
+ def inverse_condition(self) -> float:
1137
+ """The reciprocal condition number w[-1] / w[0] (``InverseCondition``)."""
1138
+ return float(self.net.InverseCondition)
1139
+
1140
+ def rank(self, threshold: float = -1.0) -> int:
1141
+ """Return the number of singular values above the threshold (``Rank``).
1142
+
1143
+ Parameters
1144
+ ----------
1145
+ threshold : float, optional
1146
+ The zero threshold; negative (default) uses :attr:`threshold`.
1147
+
1148
+ Returns
1149
+ -------
1150
+ int
1151
+ The rank.
1152
+ """
1153
+ return int(self.net.Rank(float(threshold)))
1154
+
1155
+ def nullity(self, threshold: float = -1.0) -> int:
1156
+ """Return the number of singular values at or below the threshold (``Nullity``).
1157
+
1158
+ Parameters
1159
+ ----------
1160
+ threshold : float, optional
1161
+ The zero threshold; negative (default) uses :attr:`threshold`.
1162
+
1163
+ Returns
1164
+ -------
1165
+ int
1166
+ The nullity.
1167
+ """
1168
+ return int(self.net.Nullity(float(threshold)))
1169
+
1170
+ def range(self, threshold: float = -1.0) -> np.ndarray:
1171
+ """Return an orthonormal basis of the range of A, one vector per column (``Range``).
1172
+
1173
+ Parameters
1174
+ ----------
1175
+ threshold : float, optional
1176
+ The zero threshold; negative (default) uses :attr:`threshold`.
1177
+
1178
+ Returns
1179
+ -------
1180
+ numpy.ndarray
1181
+ The m-by-rank basis.
1182
+ """
1183
+ return _numpy_matrix(self.net.Range(float(threshold)))
1184
+
1185
+ def nullspace(self, threshold: float = -1.0) -> np.ndarray:
1186
+ """Return an orthonormal basis of the null space of A, one vector per column (``Nullspace``).
1187
+
1188
+ Parameters
1189
+ ----------
1190
+ threshold : float, optional
1191
+ The zero threshold; negative (default) uses :attr:`threshold`.
1192
+
1193
+ Returns
1194
+ -------
1195
+ numpy.ndarray
1196
+ The n-by-nullity basis.
1197
+ """
1198
+ return _numpy_matrix(self.net.Nullspace(float(threshold)))
1199
+
1200
+ def solve(self, b: Any, threshold: float = -1.0) -> np.ndarray:
1201
+ """Return the pseudo-inverse solution of A x = b for a vector or a matrix (``Solve``).
1202
+
1203
+ The matrix overload of Numerics requires ``b`` to have as many rows as A has columns and
1204
+ reads its first m rows, so a matrix right-hand side is meaningful for a square A only.
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ b : Vector or Matrix or array_like
1209
+ A 1-D right-hand side of length m, or a 2-D array of them (square A).
1210
+ threshold : float, optional
1211
+ The zero threshold; negative (default) uses :attr:`threshold`.
1212
+
1213
+ Returns
1214
+ -------
1215
+ numpy.ndarray
1216
+ The solution x.
1217
+ """
1218
+ return _solve(self.net, b, float(threshold))
1219
+
1220
+ def log_determinant(self) -> float:
1221
+ """Return the sum of the logs of the singular values, ln abs(det A) (``LogDeterminant``).
1222
+
1223
+ Returns
1224
+ -------
1225
+ float
1226
+ The log absolute determinant.
1227
+ """
1228
+ return float(self.net.LogDeterminant())
1229
+
1230
+ def log_pseudo_determinant(self, threshold: float | None = None) -> float:
1231
+ """Return the sum of the logs of the nonzero singular values (``LogPseudoDeterminant``).
1232
+
1233
+ Parameters
1234
+ ----------
1235
+ threshold : float, optional
1236
+ None (the no-argument overload) drops only singular values exactly equal to zero; a
1237
+ value drops those below it, and a negative value uses :attr:`threshold`, matching
1238
+ :meth:`rank`.
1239
+
1240
+ Returns
1241
+ -------
1242
+ float
1243
+ The log pseudo-determinant.
1244
+ """
1245
+ if threshold is None:
1246
+ return float(self.net.LogPseudoDeterminant())
1247
+ return float(self.net.LogPseudoDeterminant(float(threshold)))
1248
+
1249
+
1250
+ class EigenValueDecomposition:
1251
+ """The eigen-decomposition A = V diag(lambda) V^T of a symmetric matrix (``EigenValueDecomposition``).
1252
+
1253
+ ``method="jacobi"`` (the default) uses cyclic Jacobi rotations and leaves the eigenvalues in
1254
+ diagonal order, not sorted. ``method="ql"`` uses Householder tridiagonalization and implicit QL
1255
+ iterations, returns the eigenvalues in ascending order as ``numpy.linalg.eigh`` does, and is
1256
+ many times faster for large matrices.
1257
+
1258
+ Parameters
1259
+ ----------
1260
+ matrix : Matrix or array_like
1261
+ The symmetric matrix.
1262
+ method : EigenSolverMethod or str, optional
1263
+ ``"jacobi"`` (default) or ``"ql"``.
1264
+
1265
+ Attributes
1266
+ ----------
1267
+ net : Numerics.Mathematics.LinearAlgebra.EigenValueDecomposition
1268
+ The wrapped .NET decomposition.
1269
+
1270
+ Raises
1271
+ ------
1272
+ ValueError
1273
+ If the matrix is not square, not symmetric or not finite (raised by Numerics), or
1274
+ ``method`` names no method.
1275
+ """
1276
+
1277
+ def __init__(self, matrix: Any, method: EigenSolverMethod | str = EigenSolverMethod.JACOBI) -> None:
1278
+ la = _linear_algebra()
1279
+ kind = EigenSolverMethod(method)
1280
+ a = _numerics_matrix(matrix, unwrap=True)
1281
+ if kind is EigenSolverMethod.JACOBI:
1282
+ self.net = la.EigenValueDecomposition(a)
1283
+ return
1284
+ self.net = la.EigenValueDecomposition(a, net_enum(la.EigenSolverMethod, kind))
1285
+
1286
+ @property
1287
+ def a(self) -> np.ndarray:
1288
+ """The decomposed matrix (``A``)."""
1289
+ return _numpy_matrix(self.net.A)
1290
+
1291
+ @property
1292
+ def eigenvalues(self) -> np.ndarray:
1293
+ """The eigenvalues (``EigenValues``)."""
1294
+ return _numpy_vector(self.net.EigenValues)
1295
+
1296
+ @property
1297
+ def eigenvectors(self) -> np.ndarray:
1298
+ """The orthonormal eigenvectors, one per column (``EigenVectors``)."""
1299
+ return _numpy_matrix(self.net.EigenVectors)
1300
+
1301
+ @property
1302
+ def is_converged(self) -> bool:
1303
+ """Whether every off-diagonal element fell to 1e-12 of the largest (``IsConverged``)."""
1304
+ return bool(self.net.IsConverged)
1305
+
1306
+ def effective_sample_size(self) -> float:
1307
+ """Return Dutilleul's effective sample size (sum lambda)**2 / sum lambda**2 (``EffectiveSampleSize``).
1308
+
1309
+ Returns
1310
+ -------
1311
+ float
1312
+ The effective sample size, or 0 for an all-zero spectrum.
1313
+ """
1314
+ return float(self.net.EffectiveSampleSize())
1315
+
1316
+
1317
+ def gauss_jordan_solve(a: Any, b: Any) -> tuple[np.ndarray, np.ndarray]:
1318
+ """Solve A X = B by Gauss-Jordan elimination with full pivoting (``GaussJordanElimination.Solve``).
1319
+
1320
+ Parameters
1321
+ ----------
1322
+ a : Matrix or array_like
1323
+ The square matrix A; a :class:`Matrix` is not modified (a copy is eliminated).
1324
+ b : Matrix or array_like
1325
+ The right-hand sides, one per column (2-D) or a single 1-D one.
1326
+
1327
+ Returns
1328
+ -------
1329
+ inverse : numpy.ndarray
1330
+ A^-1.
1331
+ solution : numpy.ndarray
1332
+ X, shaped like ``b``.
1333
+
1334
+ Raises
1335
+ ------
1336
+ ValueError
1337
+ If A is not square, B has a different number of rows, or A is not finite (raised by Numerics).
1338
+ """
1339
+ rhs = np.asarray(b, dtype=np.float64)
1340
+ column = rhs.ndim == 1
1341
+ net_a = _numerics_matrix(a, unwrap=True).Clone()
1342
+ net_b = _numerics_matrix(rhs.reshape(-1, 1) if column else rhs) if not isinstance(b, Matrix) else b.net.Clone()
1343
+ inverse, solution = _linear_algebra().GaussJordanElimination.Solve(net_a, net_b)
1344
+ out = _numpy_matrix(solution)
1345
+ return _numpy_matrix(inverse), out.ravel() if column else out
1346
+
1347
+
1348
+ def regularize(matrix: Any, eps: float = 1e-6, cap_multiplier: float = 50.0) -> np.ndarray:
1349
+ """Return a positive semi-definite version of a symmetric matrix (``MatrixRegularization.Regularize``).
1350
+
1351
+ The matrix is symmetrized and eigen-decomposed, its eigenvalues are floored at
1352
+ eps * trace / p (eps when the trace is not positive) and capped at cap_multiplier times
1353
+ their median, and it is rebuilt as Q diag(lambda) Q^T.
1354
+
1355
+ Parameters
1356
+ ----------
1357
+ matrix : array_like
1358
+ The (approximately) symmetric matrix.
1359
+ eps : float, optional
1360
+ The relative eigenvalue floor; default 1e-6.
1361
+ cap_multiplier : float, optional
1362
+ The eigenvalue cap as a multiple of the median; default 50.
1363
+
1364
+ Returns
1365
+ -------
1366
+ numpy.ndarray
1367
+ The regularized matrix.
1368
+ """
1369
+ values = np.asarray(matrix.to_array() if isinstance(matrix, Matrix) else matrix, dtype=np.float64)
1370
+ return to_numpy_matrix(_linear_algebra().MatrixRegularization.Regularize(to_net_matrix(values), float(eps),
1371
+ float(cap_multiplier)))
1372
+
1373
+
1374
+ def make_symmetric_positive_definite(matrix: Any) -> np.ndarray:
1375
+ """Return the nearest Cholesky-accepted symmetric matrix (``MatrixRegularization.MakeSymmetricPositiveDefinite``).
1376
+
1377
+ The symmetric part when its Cholesky factor exists, otherwise the symmetric part plus
1378
+ the smallest trace-scaled decade ridge whose factor exists.
1379
+
1380
+ Parameters
1381
+ ----------
1382
+ matrix : Matrix or array_like
1383
+ A finite square matrix.
1384
+
1385
+ Returns
1386
+ -------
1387
+ numpy.ndarray
1388
+ The regularized matrix.
1389
+
1390
+ Raises
1391
+ ------
1392
+ ValueError
1393
+ If the matrix is not square or holds non-finite entries (raised by Numerics).
1394
+ RuntimeError
1395
+ If no ridge on the ladder yields a factor (raised by Numerics).
1396
+ """
1397
+ regularized = _linear_algebra().MatrixRegularization.MakeSymmetricPositiveDefinite(
1398
+ _numerics_matrix(matrix, unwrap=True))
1399
+ return _numpy_matrix(regularized)
1400
+
1401
+
1402
+ __all__ = [
1403
+ "CholeskyDecomposition", "DecompositionMethod", "EigenSolverMethod", "EigenValueDecomposition", "LUDecomposition",
1404
+ "Matrix", "QRDecomposition", "SingularValueDecomposition", "Vector", "gauss_jordan_solve",
1405
+ "make_symmetric_positive_definite", "regularize",
1406
+ ]
1407
+
1408
+ translate_public(globals())