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,4043 @@
1
+ """Univariate distributions from ``Numerics.Distributions``.
2
+
3
+ Every class wraps one .NET distribution object (``.net``). Evaluation members accept a
4
+ scalar or any array: a scalar calls the scalar member, an array crosses once as a
5
+ ``System.Double[]`` into the ``UnivariateDistributionBase`` array overloads
6
+ (``PDF(IList<double>)``, ``CDF(IList<double>)``, ``InverseCDF(IList<double>)``), and
7
+ random generation uses ``GenerateRandomValues(n, seed)``, so no numerical work happens
8
+ in Python. Parameters follow the Numerics conventions (for example the generalized
9
+ extreme value shape is Hosking's kappa, and ``LogNormal`` is base 10 while ``LnNormal``
10
+ is natural-log with real-space mean and standard deviation).
11
+
12
+ Distributions compare by value: two wrappers are equal when they wrap the same .NET family with
13
+ bitwise-equal parameters (and the same ``Base`` where the family has one). A mutable distribution
14
+ is not hashable, because its value can change; :meth:`UnivariateDistribution.frozen` returns a
15
+ read-only :class:`FrozenDistribution` that is hashable by the same value. :data:`FAMILIES` maps each
16
+ Numerics type name to its typed class.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import types
22
+ from collections.abc import Callable, Mapping, Sequence
23
+ from typing import Any
24
+
25
+ import numpy as np
26
+
27
+ from pynumerics import enums
28
+ from pynumerics._util import (
29
+ _data,
30
+ _nd,
31
+ bind,
32
+ bind_list,
33
+ cached_overload,
34
+ evaluate,
35
+ list_overload,
36
+ loop,
37
+ namespace,
38
+ net_enum,
39
+ unwrap,
40
+ )
41
+ from pynumerics.errors import net_errors, translate, translate_class, translate_public, translated
42
+ from pynumerics.marshal import (
43
+ to_net_doubles,
44
+ to_net_ints,
45
+ to_net_matrix,
46
+ to_numpy_bools,
47
+ to_numpy_doubles,
48
+ to_numpy_matrix,
49
+ )
50
+
51
+ _PROTOTYPES: dict[str, Any] = {}
52
+
53
+
54
+ def _prototype(type_name: str) -> Any:
55
+ """Return a default-parameter .NET instance of a family, created once per process.
56
+
57
+ Used read-only, for the class-level ``parameter_names`` and ``validate_parameters``. A family
58
+ the factory cannot build without parameters (``Mixture`` needs its components) has none: the
59
+ factory's ``NotSupportedException`` is raised as ``NotImplementedError``.
60
+ """
61
+ net = _PROTOTYPES.get(type_name)
62
+ if net is None:
63
+ nd = _nd()
64
+ with net_errors():
65
+ net = _PROTOTYPES[type_name] = unwrap(nd.UnivariateDistributionFactory.CreateDistribution(
66
+ net_enum(nd.UnivariateDistributionType, type_name)))
67
+ return net
68
+
69
+
70
+ def _fixed_arity(net: Any) -> bool:
71
+ """Return whether the family's parameter vector describes it (``GetParameters`` has ``NumberOfParameters`` values).
72
+
73
+ False for the table families (``EmpiricalDistribution``, ``KernelDensity``), whose state is not
74
+ a parameter vector; their arity is not checked.
75
+ """
76
+ return len(net.GetParameters) == int(net.NumberOfParameters)
77
+
78
+
79
+ def _check_arity(net: Any, parameters: Any) -> np.ndarray:
80
+ """Return ``parameters`` as float64, or raise ValueError when their count is not the family's."""
81
+ values = np.atleast_1d(np.asarray(parameters, dtype=np.float64))
82
+ if values.ndim != 1:
83
+ raise ValueError(f"Expected a one-dimensional parameter vector; got shape {values.shape}.")
84
+ expected = int(net.NumberOfParameters)
85
+ if _fixed_arity(net) and values.size != expected:
86
+ names = ", ".join(str(name) for name in net.GetParameterPropertyNames)
87
+ raise ValueError(f"{net.Type} takes {expected} parameters ({names}); got {values.size}.")
88
+ return values
89
+
90
+
91
+ def _net_of(owner: Any) -> Any:
92
+ """Return the .NET object a family method runs on: the instance's, or the typed class's default instance.
93
+
94
+ Raises
95
+ ------
96
+ TypeError
97
+ If ``owner`` is the generic :class:`UnivariateDistribution` class.
98
+ """
99
+ if isinstance(owner, type):
100
+ type_name = _class_type_name(owner)
101
+ if type_name is None:
102
+ raise TypeError("This method needs a typed family class or an instance.")
103
+ return _prototype(type_name)
104
+ return owner.net
105
+
106
+
107
+ def _interface(net: Any, name: str) -> Any:
108
+ """Return ``net`` viewed through the ``Numerics.Distributions`` interface ``name``.
109
+
110
+ Raises
111
+ ------
112
+ NotImplementedError
113
+ If the family does not implement the interface.
114
+ """
115
+ import clr # noqa: PLC0415 - the runtime is running (net exists)
116
+
117
+ interface = getattr(_nd(), name)
118
+ if not clr.GetClrType(interface).IsInstanceOfType(net):
119
+ raise NotImplementedError(f"{net.DisplayName} does not implement {name}.")
120
+ return interface(net)
121
+
122
+
123
+ def _net_property(name: str, doc: str, kind: Callable[[Any], Any] = float, settable: bool = True) -> property:
124
+ """Return a property that reads (and, when ``settable``, writes) the .NET property ``name`` of ``self.net``.
125
+
126
+ The setter calls the owner's ``_ensure_mutable`` first when it has one (distributions do) and
127
+ passes the value through ``kind``; Numerics validates it as its own setter does.
128
+ """
129
+
130
+ def fget(self: Any) -> Any:
131
+ return kind(getattr(self.net, name))
132
+
133
+ if not settable:
134
+ return property(fget, doc=doc)
135
+
136
+ def fset(self: Any, value: Any) -> None:
137
+ ensure_mutable = getattr(self, "_ensure_mutable", None)
138
+ if ensure_mutable is not None:
139
+ ensure_mutable()
140
+ setattr(self.net, name, kind(value))
141
+
142
+ return property(fget, fset, doc=doc)
143
+
144
+
145
+ def _enum_property(name: str, net_type: Callable[[], Any], doc: str) -> property:
146
+ """Return a property that reads the .NET enum property ``name`` as its member name and sets it by name."""
147
+
148
+ def fget(self: Any) -> str:
149
+ return str(getattr(self.net, name))
150
+
151
+ def fset(self: Any, value: Any) -> None:
152
+ self._ensure_mutable()
153
+ setattr(self.net, name, net_enum(net_type(), value))
154
+
155
+ return property(fget, fset, doc=doc)
156
+
157
+
158
+ def _bins_net(bins: Any) -> Any:
159
+ """Return the .NET ``List<StratificationBin>`` of a :class:`pynumerics.sampling.StratificationBins`.
160
+
161
+ A sequence of them becomes a ``List<StratificationBin>[]`` (the many-range overloads).
162
+ """
163
+ if hasattr(bins, "net"):
164
+ return bins.net
165
+ import System # noqa: PLC0415 - the runtime is running
166
+ from System.Collections.Generic import List # noqa: PLC0415
167
+
168
+ sampling = namespace("Numerics.Sampling")
169
+ return System.Array[List[sampling.StratificationBin]]([b.net for b in bins])
170
+
171
+
172
+ def _members_array(distributions: Sequence[UnivariateDistribution]) -> Any:
173
+ """Return a ``UnivariateDistributionBase[]`` of the wrapped .NET objects."""
174
+ import System # noqa: PLC0415 - the runtime is running
175
+
176
+ return System.Array[_nd().UnivariateDistributionBase]([d.net for d in distributions])
177
+
178
+
179
+ def _parse_xml(xml: str) -> Any:
180
+ """Parse XML text into a ``System.Xml.Linq.XElement``."""
181
+ namespace("Numerics")
182
+ import clr # noqa: PLC0415
183
+
184
+ clr.AddReference("System.Xml.Linq")
185
+ from System.Xml.Linq import XElement # noqa: PLC0415
186
+
187
+ return XElement.Parse(str(xml))
188
+
189
+
190
+ _STATE: dict[str, tuple[bool, bool]] = {}
191
+
192
+
193
+ def _state_info(net: Any) -> tuple[bool, bool]:
194
+ """Return (has ``Base``, needs the XML) for the .NET type of ``net``, cached per type.
195
+
196
+ The XML joins the value key where the parameter vector is not the whole state: families that
197
+ write their own ``ToXElement`` (mixtures, tables, competing risks) or whose ``GetParameters`` is
198
+ not a full parameter vector.
199
+ """
200
+ net_type = net.GetType()
201
+ key = str(net_type.FullName)
202
+ info = _STATE.get(key)
203
+ if info is None:
204
+ declaring = net_type.GetMethod("ToXElement", _empty_types()).DeclaringType
205
+ own_xml = str(declaring.FullName) != "Numerics.Distributions.UnivariateDistributionBase"
206
+ info = _STATE[key] = (net_type.GetProperty("Base") is not None, own_xml or not _fixed_arity(net))
207
+ return info
208
+
209
+
210
+ def _empty_types() -> Any:
211
+ """Return an empty ``System.Type[]`` (the signature of a parameterless method lookup)."""
212
+ import System # noqa: PLC0415 - the runtime is running
213
+
214
+ return System.Type.EmptyTypes
215
+
216
+
217
+ class _FamilyAttribute:
218
+ """A read-only attribute with a class-level and an instance-level value.
219
+
220
+ On a typed class (``GeneralizedExtremeValue.type_name``) it gives the family's value without an
221
+ instance; on an instance it reads the wrapped .NET object, so the generic wrapper of any
222
+ family answers too.
223
+ """
224
+
225
+ def __init__(self, on_class: Callable[[type], Any], on_instance: Callable[[Any], Any], doc: str) -> None:
226
+ self._on_class = on_class
227
+ self._on_instance = on_instance
228
+ self.__doc__ = doc
229
+
230
+ def __get__(self, obj: Any, owner: type) -> Any:
231
+ if obj is None:
232
+ return self._on_class(owner)
233
+ return self._on_instance(obj)
234
+
235
+
236
+ class _FamilyMethod:
237
+ """A method callable on a typed class (on the family's default instance) or on an instance."""
238
+
239
+ def __init__(self, func: Callable[..., Any]) -> None:
240
+ self._func = translated(func)
241
+ self.__doc__ = func.__doc__
242
+ self.__wrapped__ = func
243
+
244
+ def __get__(self, obj: Any, owner: type) -> Any:
245
+ return types.MethodType(self._func, owner if obj is None else obj)
246
+
247
+
248
+ def _class_type_name(cls: type) -> str | None:
249
+ return getattr(cls, "_type_name", None)
250
+
251
+
252
+ def _class_parameter_names(cls: type) -> tuple[str, ...] | None:
253
+ type_name = _class_type_name(cls)
254
+ if type_name is None:
255
+ return None
256
+ try:
257
+ prototype = _prototype(type_name)
258
+ except NotImplementedError:
259
+ return None # the family has no default instance, so its names come from an instance only
260
+ return tuple(str(name) for name in prototype.GetParameterPropertyNames)
261
+
262
+
263
+ class UnivariateDistribution:
264
+ """A wrapped ``Numerics.Distributions.UnivariateDistributionBase``.
265
+
266
+ Parameters
267
+ ----------
268
+ net : Numerics.Distributions.UnivariateDistributionBase
269
+ The .NET distribution object; interface-typed values are unwrapped to their
270
+ concrete type.
271
+
272
+ Attributes
273
+ ----------
274
+ net : Numerics.Distributions.UnivariateDistributionBase
275
+ The wrapped .NET object, for passing back into Numerics members.
276
+ """
277
+
278
+ _type_name: str | None = None
279
+
280
+ def __init__(self, net: Any) -> None:
281
+ self.net = unwrap(net)
282
+
283
+ def __init_subclass__(cls, **kwargs: Any) -> None:
284
+ super().__init_subclass__(**kwargs)
285
+ translate_class(cls)
286
+
287
+ def __deepcopy__(self, memo: dict) -> UnivariateDistribution:
288
+ """Return a wrapper of the distribution's ``Clone``, so a deep copy owns its own .NET object."""
289
+ return wrap(self.net.Clone())
290
+
291
+ def _ensure_mutable(self) -> None:
292
+ """Do nothing; :class:`FrozenDistribution` raises here. Members that change the distribution call it first."""
293
+
294
+ def _value_key(self) -> tuple:
295
+ """Return the value compared by ``==``: .NET type, parameter bits, ``Base`` bits and, where needed, the XML."""
296
+ net = self.net
297
+ has_base, needs_xml = _state_info(net)
298
+ key: tuple = (str(net.GetType().FullName), to_numpy_doubles(net.GetParameters).tobytes())
299
+ if has_base:
300
+ key += (np.float64(float(net.Base)).tobytes(),)
301
+ if needs_xml:
302
+ key += (str(net.ToXElement().ToString()),)
303
+ return key
304
+
305
+ def __eq__(self, other: object) -> bool:
306
+ """Return whether ``other`` is a distribution of the same family with bitwise-equal parameters.
307
+
308
+ The family is the wrapped .NET type; parameters compare by their bits (so ``0.0`` and ``-0.0``
309
+ differ and a NaN equals the same NaN), as does ``Base`` where the family has one. Families whose
310
+ parameter vector is not their whole state (mixtures, empirical tables, kernel densities,
311
+ competing risks) also compare their ``ToXElement`` text. Frozen and mutable wrappers of equal
312
+ values are equal.
313
+ """
314
+ if not isinstance(other, UnivariateDistribution):
315
+ return NotImplemented
316
+ return self is other or self._value_key() == other._value_key()
317
+
318
+ __hash__ = None # type: ignore[assignment] # mutable values are unhashable; see frozen()
319
+
320
+ def frozen(self) -> FrozenDistribution:
321
+ """Return a read-only, hashable copy of this distribution.
322
+
323
+ The copy wraps its own ``Clone`` of the .NET object, so later changes to this distribution do
324
+ not reach it. It is an instance of :class:`FrozenDistribution` and of this distribution's
325
+ class, so the family's members stay available.
326
+
327
+ Returns
328
+ -------
329
+ FrozenDistribution
330
+ The frozen copy; ``set_parameters``, ``estimate`` and attribute assignment raise, and
331
+ ``copy.deepcopy`` returns it unchanged.
332
+ """
333
+ cls = _frozen_class(type(self))
334
+ instance = cls.__new__(cls)
335
+ instance.__dict__["net"] = unwrap(self.net.Clone())
336
+ return instance
337
+
338
+ def _scalar(self, name: str) -> Any:
339
+ """Return the ``double`` overload of a scalar member, bound once per instance (see ``_util.bind``)."""
340
+ cache = self.__dict__.setdefault("_bound", {})
341
+ member = cache.get(name)
342
+ if member is None:
343
+ member = cache[name] = bind(getattr(self.net, name))
344
+ return member
345
+
346
+ def _vector(self, name: str) -> Any:
347
+ """Return the ``IList<double>`` overload of an array member, bound once per instance (``_util.bind_list``)."""
348
+ cache = self.__dict__.setdefault("_bound_lists", {})
349
+ member = cache.get(name)
350
+ if member is None:
351
+ member = cache[name] = bind_list(getattr(self.net, name))
352
+ return member
353
+
354
+ def _evaluate(self, name: str, values: Any) -> Any:
355
+ """Evaluate a scalar member on a scalar or an array, through its array overload when Numerics has one.
356
+
357
+ A member without an ``IList<double>`` overload (``HF``) evaluates an array element by element
358
+ with the bound scalar overload. Both give the distribution's own scalar values, bit for bit.
359
+ """
360
+ cache = self.__dict__.setdefault("_optional_lists", {})
361
+ if name not in cache:
362
+ cache[name] = list_overload(getattr(self.net, name))
363
+ vector = cache[name]
364
+ if vector is None:
365
+ return loop(values, self._scalar(name))
366
+ return evaluate(values, self._scalar(name), vector)
367
+
368
+ def __repr__(self) -> str:
369
+ return f"{type(self).__name__}({', '.join(repr(float(p)) for p in self.parameters)})"
370
+
371
+ @property
372
+ def display_name(self) -> str:
373
+ """The Numerics display name."""
374
+ return str(self.net.DisplayName)
375
+
376
+ @property
377
+ def parameters(self) -> np.ndarray:
378
+ """The parameter values in Numerics order (``GetParameters``)."""
379
+ return to_numpy_doubles(self.net.GetParameters)
380
+
381
+ @property
382
+ def parameters_valid(self) -> bool:
383
+ """Whether the current parameters are valid (``ParametersValid``)."""
384
+ return bool(self.net.ParametersValid)
385
+
386
+ @property
387
+ def mean(self) -> float:
388
+ """The distribution mean."""
389
+ return float(self.net.Mean)
390
+
391
+ @property
392
+ def standard_deviation(self) -> float:
393
+ """The distribution standard deviation."""
394
+ return float(self.net.StandardDeviation)
395
+
396
+ @property
397
+ def skewness(self) -> float:
398
+ """The distribution skewness."""
399
+ return float(self.net.Skewness)
400
+
401
+ @property
402
+ def kurtosis(self) -> float:
403
+ """The distribution kurtosis."""
404
+ return float(self.net.Kurtosis)
405
+
406
+ @property
407
+ def minimum(self) -> float:
408
+ """The lower end of the support."""
409
+ return float(self.net.Minimum)
410
+
411
+ @property
412
+ def maximum(self) -> float:
413
+ """The upper end of the support."""
414
+ return float(self.net.Maximum)
415
+
416
+ def pdf(self, x: Any) -> Any:
417
+ """Evaluate the probability density (or mass) function.
418
+
419
+ Parameters
420
+ ----------
421
+ x : float or array_like
422
+ Values at which to evaluate.
423
+
424
+ Returns
425
+ -------
426
+ float or numpy.ndarray
427
+ Densities, shaped like ``x``.
428
+ """
429
+ return evaluate(x, self._scalar("PDF"), self._vector("PDF"))
430
+
431
+ def cdf(self, x: Any) -> Any:
432
+ """Evaluate the cumulative distribution function (non-exceedance probability).
433
+
434
+ Parameters
435
+ ----------
436
+ x : float or array_like
437
+ Values at which to evaluate.
438
+
439
+ Returns
440
+ -------
441
+ float or numpy.ndarray
442
+ Non-exceedance probabilities, shaped like ``x``.
443
+ """
444
+ return evaluate(x, self._scalar("CDF"), self._vector("CDF"))
445
+
446
+ def ccdf(self, x: Any) -> Any:
447
+ """Evaluate the complementary CDF (exceedance probability) through ``CCDF``.
448
+
449
+ An array crosses once into ``CCDF(IList<double>)``; every value is the distribution's own ``CCDF``.
450
+
451
+ Parameters
452
+ ----------
453
+ x : float or array_like
454
+ Values at which to evaluate.
455
+
456
+ Returns
457
+ -------
458
+ float or numpy.ndarray
459
+ Exceedance probabilities, shaped like ``x``.
460
+ """
461
+ return self._evaluate("CCDF", x)
462
+
463
+ def inverse_cdf(self, probability: Any) -> Any:
464
+ """Evaluate the quantile function at non-exceedance probabilities.
465
+
466
+ Parameters
467
+ ----------
468
+ probability : float or array_like
469
+ Non-exceedance probabilities in [0, 1].
470
+
471
+ Returns
472
+ -------
473
+ float or numpy.ndarray
474
+ Quantiles, shaped like ``probability``.
475
+ """
476
+ return evaluate(probability, self._scalar("InverseCDF"), self._vector("InverseCDF"))
477
+
478
+ def log_pdf(self, x: Any) -> Any:
479
+ """Evaluate the natural logarithm of the density through ``LogPDF``.
480
+
481
+ An array crosses once into ``LogPDF(IList<double>)``; every value is the distribution's own ``LogPDF``.
482
+
483
+ Parameters
484
+ ----------
485
+ x : float or array_like
486
+ Values at which to evaluate.
487
+
488
+ Returns
489
+ -------
490
+ float or numpy.ndarray
491
+ Log densities, shaped like ``x``.
492
+ """
493
+ return self._evaluate("LogPDF", x)
494
+
495
+ def log_cdf(self, x: Any) -> Any:
496
+ """Evaluate the natural logarithm of the CDF through ``LogCDF``.
497
+
498
+ An array crosses once into ``LogCDF(IList<double>)``.
499
+
500
+ Parameters
501
+ ----------
502
+ x : float or array_like
503
+ Values at which to evaluate.
504
+
505
+ Returns
506
+ -------
507
+ float or numpy.ndarray
508
+ Log non-exceedance probabilities, shaped like ``x``.
509
+ """
510
+ return self._evaluate("LogCDF", x)
511
+
512
+ def log_ccdf(self, x: Any) -> Any:
513
+ """Evaluate the natural logarithm of the complementary CDF through ``LogCCDF``.
514
+
515
+ An array crosses once into ``LogCCDF(IList<double>)``.
516
+
517
+ Parameters
518
+ ----------
519
+ x : float or array_like
520
+ Values at which to evaluate.
521
+
522
+ Returns
523
+ -------
524
+ float or numpy.ndarray
525
+ Log exceedance probabilities, shaped like ``x``.
526
+ """
527
+ return self._evaluate("LogCCDF", x)
528
+
529
+ type_name = _FamilyAttribute(
530
+ _class_type_name, lambda self: str(self.net.Type),
531
+ """The ``UnivariateDistributionType`` member name, for example ``"GeneralizedExtremeValue"``.
532
+
533
+ Available on the typed classes without an instance (``Gumbel.type_name``); None on the
534
+ generic :class:`UnivariateDistribution` class.
535
+ """)
536
+
537
+ parameter_names = _FamilyAttribute(
538
+ _class_parameter_names, lambda self: tuple(str(name) for name in self.net.GetParameterPropertyNames),
539
+ """The parameter property names in ``GetParameters`` order (``GetParameterPropertyNames``).
540
+
541
+ Available on the typed classes without an instance, from the family's default instance;
542
+ None on the generic :class:`UnivariateDistribution` class and on a family Numerics cannot
543
+ build without parameters (``Mixture``).
544
+ """)
545
+
546
+ @property
547
+ def number_of_parameters(self) -> int:
548
+ """The number of parameters (``NumberOfParameters``)."""
549
+ return int(self.net.NumberOfParameters)
550
+
551
+ def set_parameters(self, parameters: Any) -> None:
552
+ """Set the parameters in place through ``SetParameters(IList<double>)``.
553
+
554
+ Invalid values are accepted and recorded, as in Numerics: ``parameters_valid``
555
+ becomes False and the evaluation members raise.
556
+
557
+ Parameters
558
+ ----------
559
+ parameters : array_like
560
+ The parameters in Numerics order.
561
+
562
+ Raises
563
+ ------
564
+ TypeError
565
+ If the distribution is frozen.
566
+ """
567
+ self._ensure_mutable()
568
+ self.net.SetParameters(to_net_doubles(parameters))
569
+
570
+ @_FamilyMethod
571
+ def validate_parameters(self, parameters: Any) -> str | None:
572
+ """Return why a parameter vector is invalid for this family, or None when it is valid.
573
+
574
+ Callable on a typed class without an instance (``GeneralizedExtremeValue.validate_parameters(p)``,
575
+ checked on the family's default instance) or on an instance. The count of values is checked
576
+ first; then ``ValidateParameters(IList<double>, false)`` runs and the message of the exception
577
+ Numerics builds, without throwing it, is returned.
578
+
579
+ Parameters
580
+ ----------
581
+ parameters : array_like
582
+ A parameter vector in Numerics order.
583
+
584
+ Returns
585
+ -------
586
+ str or None
587
+ The Numerics message, or None for a valid vector.
588
+
589
+ Raises
590
+ ------
591
+ ValueError
592
+ If the vector does not have the family's number of parameters (families described by
593
+ a parameter vector only).
594
+ TypeError
595
+ If called on the generic :class:`UnivariateDistribution` class.
596
+ NotImplementedError
597
+ If called on the class of a family Numerics cannot build without parameters (``Mixture``).
598
+ """
599
+ if isinstance(self, type):
600
+ type_name = _class_type_name(self)
601
+ if type_name is None:
602
+ raise TypeError("validate_parameters needs a typed family class or an instance.")
603
+ net = _prototype(type_name)
604
+ else:
605
+ net = self.net
606
+ values = _check_arity(net, parameters)
607
+ error = net.ValidateParameters(to_net_doubles(values), False)
608
+ return None if error is None else str(error.Message)
609
+
610
+ @property
611
+ def has_parameter_covariance(self) -> bool:
612
+ """Whether the family implements ``IStandardError`` (an asymptotic parameter covariance)."""
613
+ nd = _nd()
614
+ import clr # noqa: PLC0415
615
+
616
+ return bool(clr.GetClrType(nd.IStandardError).IsInstanceOfType(self.net))
617
+
618
+ def parameter_covariance(self, sample_size: int,
619
+ method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD,
620
+ ) -> np.ndarray:
621
+ """Return the asymptotic parameter covariance through ``IStandardError.ParameterCovariance``.
622
+
623
+ Parameters
624
+ ----------
625
+ sample_size : int
626
+ The sample size n; the covariance scales as 1/n.
627
+ method : EstimationMethod or str, optional
628
+ The estimation method whose covariance is returned; default maximum likelihood.
629
+
630
+ Returns
631
+ -------
632
+ numpy.ndarray
633
+ Array of shape ``(number_of_parameters, number_of_parameters)``.
634
+
635
+ Raises
636
+ ------
637
+ NotImplementedError
638
+ If the distribution does not implement ``IStandardError``.
639
+ """
640
+ nd = _nd()
641
+ if not self.has_parameter_covariance:
642
+ raise NotImplementedError(f"{self.display_name} exposes no parameter covariance.")
643
+ covariance = nd.IStandardError(self.net).ParameterCovariance(int(sample_size),
644
+ net_enum(nd.ParameterEstimationMethod, method))
645
+ return to_numpy_matrix(covariance)
646
+
647
+ def parameter_set_quantiles(self, parameter_sets: Any, probability: float, *, validate: bool = False) -> np.ndarray:
648
+ """Return one quantile per parameter set, each from this family at that set.
649
+
650
+ A clone of this distribution receives every row through ``SetParameters`` and
651
+ returns ``InverseCDF(probability)``: the probe pattern a .NET caller uses for many
652
+ parameter sets, with the scalar overloads bound once. The rows cross one at a time
653
+ and are not validated; :meth:`parameter_set_quantile_table` passes the whole matrix
654
+ to ``ParameterSetQuantiles`` in one call.
655
+
656
+ Parameters
657
+ ----------
658
+ parameter_sets : array_like
659
+ Array of shape ``(count, number_of_parameters)``.
660
+ probability : float
661
+ The non-exceedance probability.
662
+ validate : bool, optional
663
+ When True, each row is checked with ``ValidateParameters(row, false)`` before it is applied,
664
+ and the first invalid row raises its own Numerics message with its index. Default False:
665
+ rows are not validated and an invalid row raises from ``InverseCDF`` without an index.
666
+
667
+ Returns
668
+ -------
669
+ numpy.ndarray
670
+ ``count`` quantiles.
671
+
672
+ Raises
673
+ ------
674
+ ValueError
675
+ If the rows do not have ``number_of_parameters`` columns, or ``validate`` is True and a
676
+ row is invalid (``"Parameter set <index>: <Numerics message>"``).
677
+ """
678
+ rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
679
+ arity = self.number_of_parameters
680
+ if rows.shape[1] != arity:
681
+ raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
682
+ if validate:
683
+ self._raise_first_invalid(rows, self.parameter_set_validity(rows))
684
+ probe = self.net.Clone()
685
+ quantile = bind(probe.InverseCDF)
686
+ p = float(probability)
687
+ out = np.empty(rows.shape[0], dtype=np.float64)
688
+ setter = _typed_member(probe, "SetParameters", arity) if arity > 1 else None
689
+ for index, row in enumerate(rows.tolist()):
690
+ if setter is not None:
691
+ setter(probe, *row)
692
+ else:
693
+ probe.SetParameters(to_net_doubles(row))
694
+ out[index] = quantile(p)
695
+ return out
696
+
697
+ def parameter_set_quantile_table(self, parameter_sets: Any, probabilities: Any, *,
698
+ validate: bool = False) -> tuple[np.ndarray, np.ndarray]:
699
+ """Return the quantiles of many probabilities under many parameter sets, in one call into Numerics.
700
+
701
+ Calls ``UnivariateDistributionBase.ParameterSetQuantiles(double[,], IList<double>, out bool[])``:
702
+ the parameter matrix crosses into .NET once, every row is validated with
703
+ ``ValidateParameters(row, false)``, a valid row is applied to one private clone with
704
+ ``SetParameters(row)`` and evaluated with ``InverseCDF`` at each probability, and the
705
+ quantile matrix and the validity flags cross back once. The values are bitwise those
706
+ of the per-row loop; the rows of invalid parameter sets are NaN.
707
+
708
+ Parameters
709
+ ----------
710
+ parameter_sets : array_like
711
+ Array of shape ``(count, number_of_parameters)``.
712
+ probabilities : array_like
713
+ The non-exceedance probabilities, one-dimensional.
714
+ validate : bool, optional
715
+ When True, an invalid row raises its own Numerics message with its index instead of
716
+ returning a NaN row. Default False.
717
+
718
+ Returns
719
+ -------
720
+ quantiles : numpy.ndarray
721
+ Array of shape ``(count, len(probabilities))``; NaN rows where the set is invalid.
722
+ valid : numpy.ndarray
723
+ Boolean array of length ``count``, True where ``ValidateParameters(row, false)`` is null.
724
+
725
+ Raises
726
+ ------
727
+ ValueError
728
+ If the rows do not have ``number_of_parameters`` columns, ``probabilities`` is not
729
+ one-dimensional, or ``validate`` is True and a row is invalid
730
+ (``"Parameter set <index>: <Numerics message>"``, for the first invalid row).
731
+ """
732
+ rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
733
+ arity = self.number_of_parameters
734
+ if rows.shape[1] != arity:
735
+ raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
736
+ p = np.asarray(probabilities, dtype=np.float64)
737
+ if p.ndim != 1:
738
+ raise ValueError(f"Expected one-dimensional probabilities; got shape {p.shape}.")
739
+ quantiles, valid = self.net.ParameterSetQuantiles(to_net_matrix(rows), to_net_doubles(p), None)
740
+ valid = to_numpy_bools(valid)
741
+ if validate:
742
+ self._raise_first_invalid(rows, valid)
743
+ return to_numpy_matrix(quantiles), valid
744
+
745
+ def _raise_first_invalid(self, rows: np.ndarray, valid: np.ndarray) -> None:
746
+ """Raise ValueError for the first row marked invalid, with its index and its own Numerics message."""
747
+ invalid = np.flatnonzero(~valid)
748
+ if invalid.size == 0:
749
+ return
750
+ index = int(invalid[0])
751
+ error = self.net.ValidateParameters(to_net_doubles(rows[index]), False)
752
+ message = "invalid parameters" if error is None else str(error.Message)
753
+ raise ValueError(f"Parameter set {index}: {message}") from (None if error is None else translate(error))
754
+
755
+ def parameter_set_validity(self, parameter_sets: Any) -> np.ndarray:
756
+ """Return which parameter sets this family accepts (``ValidateParameters(row, false)`` is null).
757
+
758
+ The matrix crosses once, into ``ParameterSetQuantiles`` with no probabilities, whose validity
759
+ flags are this test row by row.
760
+
761
+ Parameters
762
+ ----------
763
+ parameter_sets : array_like
764
+ Array of shape ``(count, number_of_parameters)``.
765
+
766
+ Returns
767
+ -------
768
+ numpy.ndarray
769
+ Boolean array of length ``count``, True where the row is valid.
770
+
771
+ Raises
772
+ ------
773
+ ValueError
774
+ If the rows do not have ``number_of_parameters`` columns.
775
+ """
776
+ rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
777
+ arity = self.number_of_parameters
778
+ if rows.shape[1] != arity:
779
+ raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
780
+ _, valid = self.net.ParameterSetQuantiles(to_net_matrix(rows), to_net_doubles(np.empty(0)), None)
781
+ return to_numpy_bools(valid)
782
+
783
+ def parameter_set_distributions(self, parameter_sets: Any, valid: Any = None) -> Any:
784
+ """Build one distribution of this family per parameter set, as a .NET array.
785
+
786
+ Each member is a ``Clone`` of this distribution with ``SetParameters(row)``, so fixed
787
+ settings that are not parameters (a ``LogNormal`` base) carry over. The result stays
788
+ on the .NET side for the members that take ``UnivariateDistributionBase[]``
789
+ (``UncertaintyAnalysisResults``, ``BootstrapAnalysis.Quantiles``); rows marked False
790
+ in ``valid`` are null entries, which those members treat as missing.
791
+
792
+ Parameters
793
+ ----------
794
+ parameter_sets : array_like
795
+ Array of shape ``(count, number_of_parameters)``.
796
+ valid : array_like of bool, optional
797
+ Which rows to build; None builds every row.
798
+
799
+ Returns
800
+ -------
801
+ System.Array[Numerics.Distributions.UnivariateDistributionBase]
802
+ ``count`` members, null where ``valid`` is False.
803
+
804
+ Raises
805
+ ------
806
+ ValueError
807
+ If the rows do not have ``number_of_parameters`` columns or ``valid`` has another length.
808
+ """
809
+ rows = np.atleast_2d(np.asarray(parameter_sets, dtype=np.float64))
810
+ arity = self.number_of_parameters
811
+ if rows.shape[1] != arity:
812
+ raise ValueError(f"Expected {arity} parameters per row; got {rows.shape[1]}.")
813
+ keep = np.ones(rows.shape[0], dtype=bool) if valid is None else np.asarray(valid, dtype=bool)
814
+ if keep.shape != (rows.shape[0],):
815
+ raise ValueError("valid must hold one flag per row.")
816
+ nd = _nd()
817
+ import System # noqa: PLC0415 - the runtime is running (self.net exists)
818
+
819
+ members = System.Array.CreateInstance(nd.UnivariateDistributionBase, rows.shape[0])
820
+ clone = self.net.Clone
821
+ setter = _typed_member(self.net, "SetParameters", arity) if arity > 1 else None
822
+ for index, row in enumerate(rows.tolist()):
823
+ if not keep[index]:
824
+ continue
825
+ member = clone()
826
+ if setter is not None:
827
+ setter(member, *row)
828
+ else:
829
+ member.SetParameters(to_net_doubles(row))
830
+ members[index] = member
831
+ return members
832
+
833
+ def to_xml(self) -> str:
834
+ """Return the distribution's ``ToXElement()`` as XML text.
835
+
836
+ Returns
837
+ -------
838
+ str
839
+ The element as .NET writes it (``XElement.ToString``).
840
+ """
841
+ return str(self.net.ToXElement().ToString())
842
+
843
+ def generate_random_values(self, sample_size: int, seed: int = -1) -> np.ndarray:
844
+ """Draw a Monte Carlo sample through ``GenerateRandomValues``.
845
+
846
+ Parameters
847
+ ----------
848
+ sample_size : int
849
+ Number of values.
850
+ seed : int, optional
851
+ Mersenne Twister seed; zero or negative seeds from the clock (not
852
+ reproducible).
853
+
854
+ Returns
855
+ -------
856
+ numpy.ndarray
857
+ The sample.
858
+ """
859
+ return to_numpy_doubles(self.net.GenerateRandomValues(int(sample_size), int(seed)))
860
+
861
+ def log_likelihood(self, sample: Any) -> float:
862
+ """Return the log-likelihood of a sample (``LogLikelihood(IList<double>)``).
863
+
864
+ Parameters
865
+ ----------
866
+ sample : array_like
867
+ Observations.
868
+
869
+ Returns
870
+ -------
871
+ float
872
+ The log-likelihood.
873
+ """
874
+ return float(self.net.LogLikelihood(to_net_doubles(sample)))
875
+
876
+ def estimate(self, sample: Any, method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD,
877
+ fixed_parameters: Any = None) -> None:
878
+ """Fit the parameters in place through ``IEstimation.Estimate``.
879
+
880
+ With ``fixed_parameters`` the fit holds those parameters at their values through
881
+ ``IFixedParameterEstimation.Estimate(sample, method, fixed)``, for example the generalized
882
+ Pareto scale and shape by maximum likelihood at a known location.
883
+
884
+ Parameters
885
+ ----------
886
+ sample : array_like
887
+ Observations.
888
+ method : EstimationMethod or str, optional
889
+ Estimation method; default maximum likelihood.
890
+ fixed_parameters : array_like, optional
891
+ One entry per parameter in Numerics order: a finite value holds that parameter fixed and
892
+ NaN marks a parameter to estimate. None (the default) estimates every parameter.
893
+
894
+ Raises
895
+ ------
896
+ TypeError
897
+ If the distribution does not implement ``IEstimation``, or ``fixed_parameters`` is given and
898
+ it does not implement ``IFixedParameterEstimation``.
899
+
900
+ Notes
901
+ -----
902
+ A frozen distribution raises ``TypeError``.
903
+ """
904
+ self._ensure_mutable()
905
+ nd = _nd()
906
+ import clr # noqa: PLC0415
907
+
908
+ if not clr.GetClrType(nd.IEstimation).IsInstanceOfType(self.net):
909
+ raise TypeError(f"{self.display_name} does not support parameter estimation.")
910
+ kind = net_enum(nd.ParameterEstimationMethod, method)
911
+ if fixed_parameters is None:
912
+ nd.IEstimation(self.net).Estimate(to_net_doubles(sample), kind)
913
+ return
914
+ fixed = nd.IFixedParameterEstimation
915
+ if not clr.GetClrType(fixed).IsInstanceOfType(self.net):
916
+ raise TypeError(f"{self.display_name} does not support estimation with fixed parameters.")
917
+ fixed(self.net).Estimate(to_net_doubles(sample), kind, to_net_doubles(fixed_parameters))
918
+
919
+ def clone(self) -> UnivariateDistribution:
920
+ """Return a deep copy through ``Clone``.
921
+
922
+ Returns
923
+ -------
924
+ UnivariateDistribution
925
+ A wrapper of the cloned .NET object, of the same Python class when possible.
926
+ """
927
+ return wrap(self.net.Clone())
928
+
929
+ @property
930
+ def median(self) -> float:
931
+ """The distribution median."""
932
+ return float(self.net.Median)
933
+
934
+ @property
935
+ def mode(self) -> float:
936
+ """The distribution mode."""
937
+ return float(self.net.Mode)
938
+
939
+ @property
940
+ def variance(self) -> float:
941
+ """The distribution variance (``Variance``, the square of ``StandardDeviation``)."""
942
+ return float(self.net.Variance)
943
+
944
+ @property
945
+ def coefficient_of_variation(self) -> float:
946
+ """The coefficient of variation, standard deviation over mean (``CoefficientOfVariation``)."""
947
+ return float(self.net.CoefficientOfVariation)
948
+
949
+ @property
950
+ def short_display_name(self) -> str:
951
+ """The Numerics short display name, for example ``"GEV"``."""
952
+ return str(self.net.ShortDisplayName)
953
+
954
+ @property
955
+ def display_label(self) -> str:
956
+ """The Numerics label: the short display name followed by the parameter values."""
957
+ return str(self.net.DisplayLabel)
958
+
959
+ @property
960
+ def parameter_display_names(self) -> tuple[str, ...]:
961
+ """The parameter names for display, for example ``"Location (ξ)"`` (``ParameterNames``)."""
962
+ return tuple(str(name) for name in self.net.ParameterNames)
963
+
964
+ @property
965
+ def parameter_names_short_form(self) -> tuple[str, ...]:
966
+ """The short parameter symbols, for example ``"ξ"`` (``ParameterNamesShortForm``)."""
967
+ return tuple(str(name) for name in self.net.ParameterNamesShortForm)
968
+
969
+ @property
970
+ def parameters_to_string(self) -> list[tuple[str, str]]:
971
+ """The (name, value) text rows Numerics shows for the parameters (``ParametersToString``)."""
972
+ table = self.net.ParametersToString
973
+ return [(str(table[i, 0]), str(table[i, 1])) for i in range(int(table.GetLength(0)))]
974
+
975
+ @property
976
+ def minimum_of_parameters(self) -> np.ndarray:
977
+ """The lower bound of each parameter, in Numerics order (``MinimumOfParameters``)."""
978
+ return to_numpy_doubles(self.net.MinimumOfParameters)
979
+
980
+ @property
981
+ def maximum_of_parameters(self) -> np.ndarray:
982
+ """The upper bound of each parameter, in Numerics order (``MaximumOfParameters``)."""
983
+ return to_numpy_doubles(self.net.MaximumOfParameters)
984
+
985
+ def hf(self, x: Any) -> Any:
986
+ """Evaluate the hazard function, the density over the survival probability (``HF``).
987
+
988
+ Parameters
989
+ ----------
990
+ x : float or array_like
991
+ Values at which to evaluate.
992
+
993
+ Returns
994
+ -------
995
+ float or numpy.ndarray
996
+ Hazard rates, shaped like ``x``; Numerics has no array overload, so an array is evaluated
997
+ element by element with the bound scalar member.
998
+ """
999
+ return self._evaluate("HF", x)
1000
+
1001
+ def inverse_ccdf(self, probability: Any) -> Any:
1002
+ """Evaluate the survival quantile: x with ``ccdf(x) = probability`` (``InverseCCDF``).
1003
+
1004
+ Families with a closed form in the exceedance probability (``GeneralizedPareto``) never form
1005
+ ``1 - probability``, which keeps the precision of small exceedance probabilities; the others
1006
+ return ``InverseCDF(1 - probability)``. An array crosses once into ``InverseCCDF(IList<double>)``.
1007
+
1008
+ Parameters
1009
+ ----------
1010
+ probability : float or array_like
1011
+ Exceedance probabilities in [0, 1].
1012
+
1013
+ Returns
1014
+ -------
1015
+ float or numpy.ndarray
1016
+ Quantiles, shaped like ``probability``.
1017
+ """
1018
+ return self._evaluate("InverseCCDF", probability)
1019
+
1020
+ def central_moments(self, tolerance: float = 1e-8, steps: int | None = None) -> np.ndarray:
1021
+ """Return the mean, standard deviation, skewness and kurtosis by numerical integration (``CentralMoments``).
1022
+
1023
+ With ``steps`` None the adaptive Gauss-Kronrod overload ``CentralMoments(double tolerance)``
1024
+ runs over ``[InverseCDF(1e-16), InverseCDF(1 - 1e-16)]``; with ``steps`` the fixed-step
1025
+ bin-expectation overload ``CentralMoments(int steps)`` runs instead and ``tolerance`` is unused.
1026
+
1027
+ Parameters
1028
+ ----------
1029
+ tolerance : float, optional
1030
+ Relative tolerance of the adaptive integration; default 1e-8.
1031
+ steps : int, optional
1032
+ Number of bins of the fixed-step overload (Numerics' default is 300).
1033
+
1034
+ Returns
1035
+ -------
1036
+ numpy.ndarray
1037
+ ``[mean, standard deviation, skewness, kurtosis]``; a moment whose integration fails is NaN.
1038
+ """
1039
+ import System # noqa: PLC0415 - the runtime is running
1040
+
1041
+ member = self.net.CentralMoments
1042
+ if steps is None:
1043
+ return to_numpy_doubles(member.Overloads[System.Double](float(tolerance)))
1044
+ return to_numpy_doubles(member.Overloads[System.Int32](int(steps)))
1045
+
1046
+ def conditional_moments(self, a: float, b: float) -> np.ndarray:
1047
+ """Return the first four moments of X restricted to [a, b] by 300-bin integration (``ConditionalMoments``).
1048
+
1049
+ Parameters
1050
+ ----------
1051
+ a : float
1052
+ Lower limit.
1053
+ b : float
1054
+ Upper limit.
1055
+
1056
+ Returns
1057
+ -------
1058
+ numpy.ndarray
1059
+ ``[E[X | a < X < b], m2, m3, m4]``, where ``mk`` is the conditional expectation of
1060
+ ``(X - mean)**k`` about the unconditional distribution mean, as Numerics computes them;
1061
+ NaN when ``a >= b`` or the interval has no probability. They are not the central moments
1062
+ of the truncated distribution: its variance is ``m2 - (m1 - mean)**2``.
1063
+ """
1064
+ return to_numpy_doubles(self.net.ConditionalMoments(float(a), float(b)))
1065
+
1066
+ def conditional_mean(self, a: float, b: float, tolerance: float = 1e-8) -> float:
1067
+ """Return E[X | a < X < b] by adaptive Gauss-Kronrod integration (``ConditionalMean``).
1068
+
1069
+ Parameters
1070
+ ----------
1071
+ a : float
1072
+ Lower limit.
1073
+ b : float
1074
+ Upper limit.
1075
+ tolerance : float, optional
1076
+ Relative integration tolerance; default 1e-8.
1077
+
1078
+ Returns
1079
+ -------
1080
+ float
1081
+ The integral of ``x f(x)`` over [a, b] divided by ``F(b) - F(a)``; NaN if the integration fails.
1082
+ """
1083
+ return float(self.net.ConditionalMean(float(a), float(b), float(tolerance)))
1084
+
1085
+ def conditional_variance(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
1086
+ """Return E[(X - mean)^2 | a < X < b] by adaptive integration (``ConditionalVariance``).
1087
+
1088
+ Parameters
1089
+ ----------
1090
+ a : float
1091
+ Lower limit.
1092
+ b : float
1093
+ Upper limit.
1094
+ mean : float, optional
1095
+ The centre of the moment; default 0 (pass :meth:`conditional_mean` for the central moment).
1096
+ tolerance : float, optional
1097
+ Relative integration tolerance; default 1e-8.
1098
+
1099
+ Returns
1100
+ -------
1101
+ float
1102
+ The conditional second moment about ``mean``.
1103
+ """
1104
+ return float(self.net.ConditionalVariance(float(a), float(b), float(mean), float(tolerance)))
1105
+
1106
+ def conditional_skewness(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
1107
+ """Return E[(X - mean)^3 | a < X < b] by adaptive integration (``ConditionalSkewness``).
1108
+
1109
+ Parameters
1110
+ ----------
1111
+ a : float
1112
+ Lower limit.
1113
+ b : float
1114
+ Upper limit.
1115
+ mean : float, optional
1116
+ The centre of the moment; default 0.
1117
+ tolerance : float, optional
1118
+ Relative integration tolerance; default 1e-8.
1119
+
1120
+ Returns
1121
+ -------
1122
+ float
1123
+ The conditional third moment about ``mean`` (not standardized).
1124
+ """
1125
+ return float(self.net.ConditionalSkewness(float(a), float(b), float(mean), float(tolerance)))
1126
+
1127
+ def conditional_kurtosis(self, a: float, b: float, mean: float = 0.0, tolerance: float = 1e-8) -> float:
1128
+ """Return E[(X - mean)^4 | a < X < b] by adaptive integration (``ConditionalKurtosis``).
1129
+
1130
+ Parameters
1131
+ ----------
1132
+ a : float
1133
+ Lower limit.
1134
+ b : float
1135
+ Upper limit.
1136
+ mean : float, optional
1137
+ The centre of the moment; default 0.
1138
+ tolerance : float, optional
1139
+ Relative integration tolerance; default 1e-8.
1140
+
1141
+ Returns
1142
+ -------
1143
+ float
1144
+ The conditional fourth moment about ``mean`` (not standardized).
1145
+ """
1146
+ return float(self.net.ConditionalKurtosis(float(a), float(b), float(mean), float(tolerance)))
1147
+
1148
+ def conditional_expected_value(self, alpha: float, tolerance: float = 1e-8) -> float:
1149
+ """Return the mean of the upper tail beyond the ``alpha`` quantile (``ConditionalExpectedValue``).
1150
+
1151
+ Parameters
1152
+ ----------
1153
+ alpha : float
1154
+ The non-exceedance level; the tail is ``[InverseCDF(alpha), InverseCDF(1 - 1e-16)]``.
1155
+ tolerance : float, optional
1156
+ Relative integration tolerance; default 1e-8.
1157
+
1158
+ Returns
1159
+ -------
1160
+ float
1161
+ The integral of ``x f(x)`` over the tail divided by ``1 - alpha`` (the conditional value at risk).
1162
+ """
1163
+ return float(self.net.ConditionalExpectedValue(float(alpha), float(tolerance)))
1164
+
1165
+ def create_pdf_graph(self, bins: Any = None) -> np.ndarray:
1166
+ """Return a (x, density) table for plotting (``CreatePDFGraph``).
1167
+
1168
+ Parameters
1169
+ ----------
1170
+ bins : pynumerics.sampling.StratificationBins or sequence of them, optional
1171
+ X-value bins whose midpoints are the table points; None, the default, lets Numerics
1172
+ choose the bounds.
1173
+
1174
+ Returns
1175
+ -------
1176
+ numpy.ndarray
1177
+ Array of shape ``(n, 2)``: x and the density.
1178
+ """
1179
+ member = self.net.CreatePDFGraph
1180
+ return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
1181
+
1182
+ def create_cdf_graph(self, bins: Any = None) -> np.ndarray:
1183
+ """Return a (x, non-exceedance probability) table for plotting (``CreateCDFGraph``).
1184
+
1185
+ Parameters
1186
+ ----------
1187
+ bins : pynumerics.sampling.StratificationBins or sequence of them, optional
1188
+ X-value bins; None, the default, lets Numerics choose the bounds.
1189
+
1190
+ Returns
1191
+ -------
1192
+ numpy.ndarray
1193
+ Array of shape ``(n, 2)``: x and the CDF.
1194
+ """
1195
+ member = self.net.CreateCDFGraph
1196
+ return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
1197
+
1198
+ def create_inverse_cdf_graph(self, bins: Any = None) -> np.ndarray:
1199
+ """Return a (probability, quantile) table for plotting (``CreateInverseCDFGraph``).
1200
+
1201
+ Parameters
1202
+ ----------
1203
+ bins : pynumerics.sampling.StratificationBins or sequence of them, optional
1204
+ Probability bins; None, the default, lets Numerics choose the probabilities.
1205
+
1206
+ Returns
1207
+ -------
1208
+ numpy.ndarray
1209
+ Array of shape ``(n, 2)``: the probability and its quantile.
1210
+ """
1211
+ member = self.net.CreateInverseCDFGraph
1212
+ return to_numpy_matrix(member() if bins is None else member(_bins_net(bins)))
1213
+
1214
+ def log_likelihood_left_censored(self, threshold: float, number_below: int) -> float:
1215
+ """Return the log-likelihood of ``number_below`` values known only to lie below ``threshold``.
1216
+
1217
+ Parameters
1218
+ ----------
1219
+ threshold : float
1220
+ The censoring threshold.
1221
+ number_below : int
1222
+ The count of censored values.
1223
+
1224
+ Returns
1225
+ -------
1226
+ float
1227
+ ``number_below * log F(threshold)`` as Numerics computes it (``LogLikelihood_LeftCensored``).
1228
+ """
1229
+ return float(self.net.LogLikelihood_LeftCensored(float(threshold), int(number_below)))
1230
+
1231
+ def log_likelihood_right_censored(self, threshold: float, number_above: int) -> float:
1232
+ """Return the log-likelihood of ``number_above`` values known only to exceed ``threshold``.
1233
+
1234
+ Parameters
1235
+ ----------
1236
+ threshold : float
1237
+ The censoring threshold.
1238
+ number_above : int
1239
+ The count of censored values.
1240
+
1241
+ Returns
1242
+ -------
1243
+ float
1244
+ ``number_above * log(1 - F(threshold))`` as Numerics computes it (``LogLikelihood_RightCensored``).
1245
+ """
1246
+ return float(self.net.LogLikelihood_RightCensored(float(threshold), int(number_above)))
1247
+
1248
+ def log_likelihood_intervals(self, lower: float, upper: float) -> float:
1249
+ """Return the log-likelihood of one value known to lie in [lower, upper] (``LogLikelihood_Intervals``).
1250
+
1251
+ Parameters
1252
+ ----------
1253
+ lower : float
1254
+ Lower limit of the interval.
1255
+ upper : float
1256
+ Upper limit of the interval.
1257
+
1258
+ Returns
1259
+ -------
1260
+ float
1261
+ ``log(F(upper) - F(lower))`` as Numerics computes it.
1262
+ """
1263
+ return float(self.net.LogLikelihood_Intervals(float(lower), float(upper)))
1264
+
1265
+ def compare_to(self, other: UnivariateDistribution | None) -> int:
1266
+ """Order two distributions by their means (``CompareTo``).
1267
+
1268
+ Parameters
1269
+ ----------
1270
+ other : UnivariateDistribution or None
1271
+ The distribution to compare with; None orders below every distribution.
1272
+
1273
+ Returns
1274
+ -------
1275
+ int
1276
+ Negative, zero or positive as this mean is below, equal to or above the other's.
1277
+ """
1278
+ return int(self.net.CompareTo(None if other is None else other.net))
1279
+
1280
+ def bootstrap(self, method: enums.EstimationMethod | str, sample_size: int,
1281
+ seed: int = -1) -> UnivariateDistribution:
1282
+ """Draw a sample from this distribution and refit the family to it (``IBootstrappable.Bootstrap``).
1283
+
1284
+ Parameters
1285
+ ----------
1286
+ method : EstimationMethod or str
1287
+ The estimation method of the refit.
1288
+ sample_size : int
1289
+ The size of the drawn sample.
1290
+ seed : int, optional
1291
+ Mersenne Twister seed; zero or negative seeds from the clock (not reproducible).
1292
+
1293
+ Returns
1294
+ -------
1295
+ UnivariateDistribution
1296
+ The refitted distribution (a new object; this one is unchanged).
1297
+
1298
+ Raises
1299
+ ------
1300
+ NotImplementedError
1301
+ If the family does not implement ``IBootstrappable``.
1302
+ """
1303
+ nd = _nd()
1304
+ replicate = _interface(self.net, "IBootstrappable").Bootstrap(
1305
+ net_enum(nd.ParameterEstimationMethod, method), int(sample_size), int(seed))
1306
+ return wrap(replicate)
1307
+
1308
+ def mle(self, sample: Any) -> np.ndarray:
1309
+ """Return the maximum likelihood parameters of a sample, without changing this distribution.
1310
+
1311
+ Parameters
1312
+ ----------
1313
+ sample : array_like
1314
+ Observations.
1315
+
1316
+ Returns
1317
+ -------
1318
+ numpy.ndarray
1319
+ The parameters in Numerics order (``IMaximumLikelihoodEstimation.MLE``).
1320
+
1321
+ Raises
1322
+ ------
1323
+ NotImplementedError
1324
+ If the family does not implement ``IMaximumLikelihoodEstimation``.
1325
+ """
1326
+ return to_numpy_doubles(_interface(self.net, "IMaximumLikelihoodEstimation").MLE(to_net_doubles(sample)))
1327
+
1328
+ def parameter_constraints(self, sample: Any) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
1329
+ """Return the starting values and bounds the maximum likelihood search uses for a sample.
1330
+
1331
+ Parameters
1332
+ ----------
1333
+ sample : array_like
1334
+ Observations.
1335
+
1336
+ Returns
1337
+ -------
1338
+ initial : numpy.ndarray
1339
+ The starting parameters (``IMaximumLikelihoodEstimation.GetParameterConstraints``).
1340
+ lower : numpy.ndarray
1341
+ The lower bound of each parameter.
1342
+ upper : numpy.ndarray
1343
+ The upper bound of each parameter.
1344
+
1345
+ Raises
1346
+ ------
1347
+ NotImplementedError
1348
+ If the family does not implement ``IMaximumLikelihoodEstimation``.
1349
+ """
1350
+ result = _interface(self.net, "IMaximumLikelihoodEstimation").GetParameterConstraints(to_net_doubles(sample))
1351
+ return to_numpy_doubles(result.Item1), to_numpy_doubles(result.Item2), to_numpy_doubles(result.Item3)
1352
+
1353
+ @_FamilyMethod
1354
+ def parameters_from_moments(self, moments: Any) -> np.ndarray:
1355
+ """Return the parameters matching product moments (``IMomentEstimation.ParametersFromMoments``).
1356
+
1357
+ Callable on a typed class without an instance (on the family's default instance) or on an
1358
+ instance; families with a setting that is not a parameter (a ``LogNormal`` base) use the
1359
+ instance's setting.
1360
+
1361
+ Parameters
1362
+ ----------
1363
+ moments : array_like
1364
+ ``[mean, standard deviation, skewness, kurtosis]`` (the leading entries the family needs).
1365
+
1366
+ Returns
1367
+ -------
1368
+ numpy.ndarray
1369
+ The parameters in Numerics order.
1370
+
1371
+ Raises
1372
+ ------
1373
+ NotImplementedError
1374
+ If the family does not implement ``IMomentEstimation``.
1375
+ """
1376
+ net = _interface(_net_of(self), "IMomentEstimation")
1377
+ return to_numpy_doubles(net.ParametersFromMoments(to_net_doubles(moments)))
1378
+
1379
+ @_FamilyMethod
1380
+ def moments_from_parameters(self, parameters: Any) -> np.ndarray:
1381
+ """Return the product moments of a parameter vector (``IMomentEstimation.MomentsFromParameters``).
1382
+
1383
+ Callable on a typed class without an instance or on an instance.
1384
+
1385
+ Parameters
1386
+ ----------
1387
+ parameters : array_like
1388
+ The parameters in Numerics order.
1389
+
1390
+ Returns
1391
+ -------
1392
+ numpy.ndarray
1393
+ ``[mean, standard deviation, skewness, kurtosis]``.
1394
+
1395
+ Raises
1396
+ ------
1397
+ NotImplementedError
1398
+ If the family does not implement ``IMomentEstimation``.
1399
+ """
1400
+ net = _interface(_net_of(self), "IMomentEstimation")
1401
+ return to_numpy_doubles(net.MomentsFromParameters(to_net_doubles(parameters)))
1402
+
1403
+ @_FamilyMethod
1404
+ def parameters_from_linear_moments(self, moments: Any) -> np.ndarray:
1405
+ """Return the parameters matching L-moments (``ILinearMomentEstimation.ParametersFromLinearMoments``).
1406
+
1407
+ Callable on a typed class without an instance
1408
+ (``GeneralizedExtremeValue.parameters_from_linear_moments(l)``) or on an instance.
1409
+
1410
+ Parameters
1411
+ ----------
1412
+ moments : array_like
1413
+ ``[λ1, λ2, τ3, τ4]``: the first two L-moments and the L-skewness and L-kurtosis ratios,
1414
+ as :func:`pynumerics.statistics.linear_moments` returns them.
1415
+
1416
+ Returns
1417
+ -------
1418
+ numpy.ndarray
1419
+ The parameters in Numerics order.
1420
+
1421
+ Raises
1422
+ ------
1423
+ NotImplementedError
1424
+ If the family does not implement ``ILinearMomentEstimation``.
1425
+ """
1426
+ net = _interface(_net_of(self), "ILinearMomentEstimation")
1427
+ return to_numpy_doubles(net.ParametersFromLinearMoments(to_net_doubles(moments)))
1428
+
1429
+ @_FamilyMethod
1430
+ def linear_moments_from_parameters(self, parameters: Any) -> np.ndarray:
1431
+ """Return the L-moments of a parameter vector (``ILinearMomentEstimation.LinearMomentsFromParameters``).
1432
+
1433
+ Callable on a typed class without an instance or on an instance.
1434
+
1435
+ Parameters
1436
+ ----------
1437
+ parameters : array_like
1438
+ The parameters in Numerics order.
1439
+
1440
+ Returns
1441
+ -------
1442
+ numpy.ndarray
1443
+ ``[λ1, λ2, τ3, τ4]``.
1444
+
1445
+ Raises
1446
+ ------
1447
+ NotImplementedError
1448
+ If the family does not implement ``ILinearMomentEstimation``.
1449
+ """
1450
+ net = _interface(_net_of(self), "ILinearMomentEstimation")
1451
+ return to_numpy_doubles(net.LinearMomentsFromParameters(to_net_doubles(parameters)))
1452
+
1453
+ def quantile_variance(self, probability: float, sample_size: int,
1454
+ method: enums.EstimationMethod | str = enums.EstimationMethod.MAXIMUM_LIKELIHOOD) -> float:
1455
+ """Return the asymptotic variance of the quantile estimate (``IStandardError.QuantileVariance``).
1456
+
1457
+ Parameters
1458
+ ----------
1459
+ probability : float
1460
+ The non-exceedance probability.
1461
+ sample_size : int
1462
+ The sample size n.
1463
+ method : EstimationMethod or str, optional
1464
+ The estimation method; default maximum likelihood.
1465
+
1466
+ Returns
1467
+ -------
1468
+ float
1469
+ The variance of the estimated ``inverse_cdf(probability)``.
1470
+
1471
+ Raises
1472
+ ------
1473
+ NotImplementedError
1474
+ If the family does not implement ``IStandardError``.
1475
+ """
1476
+ net = _interface(self.net, "IStandardError")
1477
+ return float(net.QuantileVariance(float(probability), int(sample_size),
1478
+ net_enum(_nd().ParameterEstimationMethod, method)))
1479
+
1480
+ def quantile_gradient(self, probability: float) -> np.ndarray:
1481
+ """Return the gradient of the quantile with respect to the parameters (``IStandardError.QuantileGradient``).
1482
+
1483
+ Parameters
1484
+ ----------
1485
+ probability : float
1486
+ The non-exceedance probability.
1487
+
1488
+ Returns
1489
+ -------
1490
+ numpy.ndarray
1491
+ The partial derivatives of ``inverse_cdf(probability)``, in parameter order.
1492
+
1493
+ Raises
1494
+ ------
1495
+ NotImplementedError
1496
+ If the family does not implement ``IStandardError``.
1497
+ """
1498
+ return to_numpy_doubles(_interface(self.net, "IStandardError").QuantileGradient(float(probability)))
1499
+
1500
+ def quantile_jacobian(self, probabilities: Any) -> tuple[np.ndarray, float]:
1501
+ """Return the Jacobian of quantiles with respect to the parameters (``IStandardError.QuantileJacobian``).
1502
+
1503
+ Parameters
1504
+ ----------
1505
+ probabilities : array_like
1506
+ Non-exceedance probabilities, one per parameter.
1507
+
1508
+ Returns
1509
+ -------
1510
+ jacobian : numpy.ndarray
1511
+ Square matrix; row i is the gradient of the quantile at ``probabilities[i]``.
1512
+ determinant : float
1513
+ Its determinant.
1514
+
1515
+ Raises
1516
+ ------
1517
+ NotImplementedError
1518
+ If the family does not implement ``IStandardError``.
1519
+ """
1520
+ jacobian, determinant = _interface(self.net, "IStandardError").QuantileJacobian(
1521
+ to_net_doubles(probabilities), 0.0)
1522
+ return to_numpy_matrix(jacobian), float(determinant)
1523
+
1524
+ def log_abs_quantile_jacobian(self, probabilities: Any) -> float:
1525
+ """Return log abs(det J) of the quantile Jacobian (``StandardErrorExtensions.LogAbsQuantileJacobian``).
1526
+
1527
+ Parameters
1528
+ ----------
1529
+ probabilities : array_like
1530
+ Non-exceedance probabilities, one per parameter.
1531
+
1532
+ Returns
1533
+ -------
1534
+ float
1535
+ The natural logarithm of the absolute Jacobian determinant.
1536
+
1537
+ Raises
1538
+ ------
1539
+ NotImplementedError
1540
+ If the family does not implement ``IStandardError``.
1541
+ """
1542
+ net = _interface(self.net, "IStandardError")
1543
+ return float(_nd().StandardErrorExtensions.LogAbsQuantileJacobian(net, to_net_doubles(probabilities)))
1544
+
1545
+
1546
+ def _typed_member(net: Any, name: str, arity: int) -> Any:
1547
+ """Return the unbound ``name(double x arity)`` overload of ``net``'s concrete type, or None.
1548
+
1549
+ Called as ``member(instance, *values)``. Binding the overload once on the type, rather than
1550
+ resolving the method group on every call, is what makes the per-row loops of
1551
+ ``parameter_set_*`` cheap; families without such an overload use the ``IList<double>`` one.
1552
+ """
1553
+ import System # noqa: PLC0415 - the runtime is running (net exists)
1554
+
1555
+ types = (System.Double,) * arity
1556
+ try:
1557
+ return getattr(type(net), name).Overloads[types if len(types) > 1 else types[0]]
1558
+ except (AttributeError, KeyError, TypeError):
1559
+ return None
1560
+
1561
+
1562
+ class FrozenDistribution(UnivariateDistribution):
1563
+ """A read-only, hashable distribution, made by :meth:`UnivariateDistribution.frozen`.
1564
+
1565
+ Each frozen object is an instance of ``FrozenDistribution`` and of the family's class (for
1566
+ example ``FrozenGeneralizedExtremeValue`` derives from both), and wraps its own ``Clone`` of the
1567
+ .NET object. ``set_parameters``, ``estimate`` and every attribute assignment (including the
1568
+ settable properties) raise; ``copy.copy`` and ``copy.deepcopy`` return the object itself;
1569
+ :meth:`clone` returns a mutable copy. It hashes by the value ``==`` compares. Changing the
1570
+ ``.net`` object through .NET directly bypasses the protection and must not be done.
1571
+
1572
+ Parameters
1573
+ ----------
1574
+ net : Numerics.Distributions.UnivariateDistributionBase
1575
+ Unused; frozen distributions are made with :meth:`UnivariateDistribution.frozen`.
1576
+ """
1577
+
1578
+ def __setattr__(self, name: str, value: Any) -> None:
1579
+ raise AttributeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
1580
+
1581
+ def __delattr__(self, name: str) -> None:
1582
+ raise AttributeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
1583
+
1584
+ def _ensure_mutable(self) -> None:
1585
+ raise TypeError(f"{type(self).__name__} is frozen; use clone() for a mutable copy.")
1586
+
1587
+ def __hash__(self) -> int:
1588
+ key = self.__dict__.get("_hash")
1589
+ if key is None:
1590
+ key = self.__dict__["_hash"] = hash(self._value_key())
1591
+ return key
1592
+
1593
+ def __copy__(self) -> FrozenDistribution:
1594
+ return self
1595
+
1596
+ def __deepcopy__(self, memo: dict) -> FrozenDistribution:
1597
+ return self
1598
+
1599
+ def frozen(self) -> FrozenDistribution:
1600
+ """Return this distribution, which is already frozen.
1601
+
1602
+ Returns
1603
+ -------
1604
+ FrozenDistribution
1605
+ ``self``.
1606
+ """
1607
+ return self
1608
+
1609
+
1610
+ _FROZEN_CLASSES: dict[type, type] = {}
1611
+
1612
+
1613
+ def _frozen_class(cls: type) -> type:
1614
+ """Return the frozen class of a wrapper class, created once: ``Frozen<Name>(FrozenDistribution, cls)``."""
1615
+ if issubclass(cls, FrozenDistribution):
1616
+ return cls
1617
+ frozen = _FROZEN_CLASSES.get(cls)
1618
+ if frozen is None:
1619
+ if cls is UnivariateDistribution:
1620
+ frozen = FrozenDistribution
1621
+ else:
1622
+ frozen = types.new_class(f"Frozen{cls.__name__}", (FrozenDistribution, cls), exec_body=lambda ns: ns.update(
1623
+ {"__module__": cls.__module__, "__doc__": f"Frozen {cls.__name__}; see FrozenDistribution."}))
1624
+ _FROZEN_CLASSES[cls] = frozen
1625
+ return frozen
1626
+
1627
+
1628
+ _CLASS_BY_NET_NAME: dict[str, type[UnivariateDistribution]] = {}
1629
+ _FAMILIES: dict[str, type[UnivariateDistribution]] = {}
1630
+
1631
+ FAMILIES: Mapping[str, type[UnivariateDistribution]] = types.MappingProxyType(_FAMILIES)
1632
+ """The typed distribution classes, keyed by their Numerics type name (``UnivariateDistributionType``).
1633
+
1634
+ Read-only. ``FAMILIES[name].type_name == name`` for every entry, so a persisted type name finds its
1635
+ class without an instance; families without a typed class are created by :func:`create_distribution`.
1636
+ """
1637
+
1638
+
1639
+ def wrap(net: Any) -> UnivariateDistribution:
1640
+ """Wrap a .NET univariate distribution in the matching Python class.
1641
+
1642
+ Parameters
1643
+ ----------
1644
+ net : Numerics.Distributions.UnivariateDistributionBase or IUnivariateDistribution
1645
+ The .NET object.
1646
+
1647
+ Returns
1648
+ -------
1649
+ UnivariateDistribution
1650
+ The specific wrapper class when one exists, otherwise the generic wrapper.
1651
+ """
1652
+ concrete = unwrap(net)
1653
+ cls = _CLASS_BY_NET_NAME.get(str(concrete.GetType().Name), UnivariateDistribution)
1654
+ instance = cls.__new__(cls)
1655
+ UnivariateDistribution.__init__(instance, concrete)
1656
+ return instance
1657
+
1658
+
1659
+ def _register(net_name: str, type_name: str | None = None):
1660
+ """Register a wrapper class for a .NET class name (for :func:`wrap`) and its type name (for :data:`FAMILIES`).
1661
+
1662
+ ``type_name`` is the ``UnivariateDistributionType`` member, which is the .NET class name unless
1663
+ given (``BetaDistribution`` is ``Beta``).
1664
+ """
1665
+
1666
+ def decorator(cls: type[UnivariateDistribution]) -> type[UnivariateDistribution]:
1667
+ name = type_name or net_name
1668
+ cls._type_name = name
1669
+ _CLASS_BY_NET_NAME[net_name] = cls
1670
+ _FAMILIES[name] = cls
1671
+ return cls
1672
+
1673
+ return decorator
1674
+
1675
+
1676
+ _LOCATION = "The location parameter ξ (``Xi``); settable."
1677
+ _SCALE = "The scale parameter α > 0 (``Alpha``); settable."
1678
+ _SHAPE = "The shape parameter κ, Hosking sign (``Kappa``); settable."
1679
+
1680
+
1681
+ class _KappaMoments:
1682
+ """Members of the three-parameter Hosking families with a skew-to-shape solution (GEV, GPA, GLO)."""
1683
+
1684
+ def solve_for_kappa(self, skew: float) -> float:
1685
+ """Return the shape κ whose product-moment skewness is ``skew`` (``SolveForKappa``).
1686
+
1687
+ Parameters
1688
+ ----------
1689
+ skew : float
1690
+ The skewness coefficient.
1691
+
1692
+ Returns
1693
+ -------
1694
+ float
1695
+ The shape κ, Hosking sign.
1696
+ """
1697
+ return float(self.net.SolveForKappa(float(skew)))
1698
+
1699
+ def direct_method_of_moments(self, moments: Any) -> np.ndarray:
1700
+ """Return the parameters of the direct method of moments (``DirectMethodOfMoments``).
1701
+
1702
+ Parameters
1703
+ ----------
1704
+ moments : array_like
1705
+ ``[mean, standard deviation, skewness]`` (further entries are ignored).
1706
+
1707
+ Returns
1708
+ -------
1709
+ numpy.ndarray
1710
+ ``[location, scale, shape]``.
1711
+ """
1712
+ return to_numpy_doubles(self.net.DirectMethodOfMoments(to_net_doubles(moments)))
1713
+
1714
+
1715
+ class _LogMoments:
1716
+ """Indirect (log-space) moment fits of the log-transformed families (LnNormal, LogNormal, LogPearsonTypeIII)."""
1717
+
1718
+ def indirect_method_of_moments(self, sample: Any) -> np.ndarray:
1719
+ """Return the product moments of the log-transformed sample (``IndirectMethodOfMoments``).
1720
+
1721
+ The indirect method of moments (U.S. Water Resources Council, 1967) fits the log-space
1722
+ parameters to these moments.
1723
+
1724
+ Parameters
1725
+ ----------
1726
+ sample : array_like
1727
+ Positive observations.
1728
+
1729
+ Returns
1730
+ -------
1731
+ numpy.ndarray
1732
+ ``[mean, standard deviation, skewness, kurtosis]`` of log_base(sample), in this
1733
+ distribution's base.
1734
+ """
1735
+ return to_numpy_doubles(self.net.IndirectMethodOfMoments(to_net_doubles(sample)))
1736
+
1737
+ def indirect_method_of_linear_moments(self, sample: Any) -> np.ndarray:
1738
+ """Return the L-moments of the log-transformed sample (``IndirectMethodOfLinearMoments``).
1739
+
1740
+ Parameters
1741
+ ----------
1742
+ sample : array_like
1743
+ Positive observations.
1744
+
1745
+ Returns
1746
+ -------
1747
+ numpy.ndarray
1748
+ ``[λ1, λ2, τ3, τ4]`` of log_base(sample), in this distribution's base.
1749
+ """
1750
+ return to_numpy_doubles(self.net.IndirectMethodOfLinearMoments(to_net_doubles(sample)))
1751
+
1752
+
1753
+ class _WilsonHilferty:
1754
+ """The Wilson-Hilferty quantile approximation of the gamma-type families."""
1755
+
1756
+ def wilson_hilferty_inverse_cdf(self, probability: float) -> float:
1757
+ """Return the Wilson-Hilferty approximation of the quantile (``WilsonHilfertyInverseCDF``).
1758
+
1759
+ Parameters
1760
+ ----------
1761
+ probability : float
1762
+ The non-exceedance probability.
1763
+
1764
+ Returns
1765
+ -------
1766
+ float
1767
+ The approximate quantile; :meth:`UnivariateDistribution.inverse_cdf` gives the exact one.
1768
+ """
1769
+ return float(self.net.WilsonHilfertyInverseCDF(float(probability)))
1770
+
1771
+
1772
+ class _PearsonForm(_WilsonHilferty):
1773
+ """The three-parameter gamma form (ξ, β, α) of the Pearson type III families."""
1774
+
1775
+ support_bound = _net_property(
1776
+ "Xi", "The location ξ of the gamma form: the lower support bound for positive skew, the upper for "
1777
+ "negative (``Xi``; in log space for LogPearsonTypeIII).", settable=False)
1778
+ scale = _net_property("Beta", "The scale β of the gamma form, negative for negative skew (``Beta``).",
1779
+ settable=False)
1780
+ shape = _net_property("Alpha", "The shape α of the gamma form, 4 / skew² (``Alpha``).", settable=False)
1781
+
1782
+ def quantile_gradient_for_moments(self, probability: float) -> np.ndarray:
1783
+ """Return the quantile gradient in (mean, standard deviation, skew) (``QuantileGradientForMoments``).
1784
+
1785
+ Parameters
1786
+ ----------
1787
+ probability : float
1788
+ The non-exceedance probability.
1789
+
1790
+ Returns
1791
+ -------
1792
+ numpy.ndarray
1793
+ The three partial derivatives.
1794
+ """
1795
+ return to_numpy_doubles(self.net.QuantileGradientForMoments(float(probability)))
1796
+
1797
+
1798
+ class _MonteCarloIntervals:
1799
+ """Monte Carlo quantile confidence intervals of the normal families."""
1800
+
1801
+ def monte_carlo_confidence_intervals(self, sample_size: int, realizations: int, probabilities: Any,
1802
+ percentiles: Any) -> np.ndarray:
1803
+ """Return Monte Carlo confidence limits of quantiles (``MonteCarloConfidenceIntervals``).
1804
+
1805
+ Parameters
1806
+ ----------
1807
+ sample_size : int
1808
+ The sample size of each realization.
1809
+ realizations : int
1810
+ The number of simulated samples.
1811
+ probabilities : array_like
1812
+ Non-exceedance probabilities of the quantiles.
1813
+ percentiles : array_like
1814
+ Confidence percentiles, for example ``[0.05, 0.95]``.
1815
+
1816
+ Returns
1817
+ -------
1818
+ numpy.ndarray
1819
+ Array of shape ``(len(probabilities), len(percentiles))``.
1820
+ """
1821
+ return to_numpy_matrix(self.net.MonteCarloConfidenceIntervals(int(sample_size), int(realizations),
1822
+ to_net_doubles(probabilities),
1823
+ to_net_doubles(percentiles)))
1824
+
1825
+
1826
+ for _mixin in (_KappaMoments, _LogMoments, _WilsonHilferty, _PearsonForm, _MonteCarloIntervals):
1827
+ translate_class(_mixin)
1828
+
1829
+
1830
+ @_register("GeneralizedExtremeValue")
1831
+ class GeneralizedExtremeValue(_KappaMoments, UnivariateDistribution):
1832
+ """Generalized extreme value distribution (Hosking parameterization).
1833
+
1834
+ Parameters
1835
+ ----------
1836
+ location : float, optional
1837
+ Location ξ; default 100.
1838
+ scale : float, optional
1839
+ Scale α > 0; default 10.
1840
+ shape : float, optional
1841
+ Shape κ (Hosking sign: κ < 0 is heavy-tailed); default 0.
1842
+ """
1843
+
1844
+ def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
1845
+ super().__init__(_nd().GeneralizedExtremeValue(float(location), float(scale), float(shape)))
1846
+
1847
+ location = _net_property("Xi", _LOCATION)
1848
+ scale = _net_property("Alpha", _SCALE)
1849
+ shape = _net_property("Kappa", _SHAPE)
1850
+
1851
+ @classmethod
1852
+ def from_poisson_generalized_pareto(cls, rate: float, location: float, scale: float,
1853
+ shape: float) -> GeneralizedExtremeValue:
1854
+ """Return the annual-maximum GEV of a Poisson count of generalized Pareto exceedances.
1855
+
1856
+ Calls ``GeneralizedExtremeValue.FromPoissonGeneralizedPareto``: with N ~ Poisson(``rate``)
1857
+ exceedances a year of GPD(``location``, ``scale``, ``shape``), the maximum is GEV with
1858
+ ``scale * rate**(-shape)`` and the same shape (Hosking sign), the location formed through
1859
+ ``expm1`` so it stays accurate near ``shape = 0``.
1860
+
1861
+ Parameters
1862
+ ----------
1863
+ rate : float
1864
+ The mean number of exceedances per year (> 0, finite).
1865
+ location : float
1866
+ The GPD location (the threshold).
1867
+ scale : float
1868
+ The GPD scale (> 0).
1869
+ shape : float
1870
+ The GPD shape κ (Hosking sign).
1871
+
1872
+ Returns
1873
+ -------
1874
+ GeneralizedExtremeValue
1875
+ The annual-maximum distribution.
1876
+
1877
+ Raises
1878
+ ------
1879
+ ValueError
1880
+ If ``rate`` or ``scale`` is not positive and finite.
1881
+ """
1882
+ member = _nd().GeneralizedExtremeValue.FromPoissonGeneralizedPareto
1883
+ instance = cls.__new__(cls)
1884
+ UnivariateDistribution.__init__(instance, member(float(rate), float(location), float(scale), float(shape)))
1885
+ return instance
1886
+
1887
+ def expected_information_matrix(self, sample_size: int) -> np.ndarray:
1888
+ """Return the expected (Fisher) information matrix of a sample (``ExpectedInformationMatrix``).
1889
+
1890
+ Parameters
1891
+ ----------
1892
+ sample_size : int
1893
+ The sample size n.
1894
+
1895
+ Returns
1896
+ -------
1897
+ numpy.ndarray
1898
+ The 3 x 3 information matrix in parameter order.
1899
+ """
1900
+ return to_numpy_matrix(self.net.ExpectedInformationMatrix(int(sample_size)).ToArray())
1901
+
1902
+
1903
+ @_register("GeneralizedPareto")
1904
+ class GeneralizedPareto(_KappaMoments, UnivariateDistribution):
1905
+ """Generalized Pareto distribution (Hosking parameterization).
1906
+
1907
+ Parameters
1908
+ ----------
1909
+ location : float, optional
1910
+ Location ξ; default 100.
1911
+ scale : float, optional
1912
+ Scale α > 0; default 10.
1913
+ shape : float, optional
1914
+ Shape κ (Hosking sign); default 0.
1915
+ """
1916
+
1917
+ def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
1918
+ super().__init__(_nd().GeneralizedPareto(float(location), float(scale), float(shape)))
1919
+
1920
+ location = _net_property("Xi", _LOCATION)
1921
+ scale = _net_property("Alpha", _SCALE)
1922
+ shape = _net_property("Kappa", _SHAPE)
1923
+ peaks_per_block = _net_property("Lambda", "The average number of peaks per block (``Lambda``); settable.")
1924
+
1925
+ @classmethod
1926
+ def from_poisson_generalized_extreme_value(cls, rate: float, location: float, scale: float,
1927
+ shape: float) -> GeneralizedPareto:
1928
+ """Return the exceedance GPD whose Poisson annual count gives a stated GEV annual maximum.
1929
+
1930
+ Calls ``GeneralizedPareto.FromPoissonGeneralizedExtremeValue``, the inverse of
1931
+ :meth:`GeneralizedExtremeValue.from_poisson_generalized_pareto`: for an annual-maximum
1932
+ GEV(``location``, ``scale``, ``shape``) and N ~ Poisson(``rate``) exceedances a year, the
1933
+ exceedances are GPD with scale ``scale * rate**shape`` and the same shape (Hosking sign), the
1934
+ location ``location - scale * expm1(shape * log(rate)) / shape`` formed through ``expm1`` so it
1935
+ stays accurate near ``shape = 0`` (``location - scale * log(rate)`` at ``shape = 0``).
1936
+
1937
+ Parameters
1938
+ ----------
1939
+ rate : float
1940
+ The mean number of exceedances per year (> 0, finite).
1941
+ location : float
1942
+ The GEV location.
1943
+ scale : float
1944
+ The GEV scale (> 0).
1945
+ shape : float
1946
+ The GEV shape κ (Hosking sign).
1947
+
1948
+ Returns
1949
+ -------
1950
+ GeneralizedPareto
1951
+ The exceedance distribution; its location is the threshold.
1952
+
1953
+ Raises
1954
+ ------
1955
+ ValueError
1956
+ If ``rate`` or ``scale`` is not positive and finite, ``location`` or ``shape`` is not finite,
1957
+ or the mapped parameters overflow.
1958
+ """
1959
+ member = _nd().GeneralizedPareto.FromPoissonGeneralizedExtremeValue
1960
+ instance = cls.__new__(cls)
1961
+ UnivariateDistribution.__init__(instance, member(float(rate), float(location), float(scale), float(shape)))
1962
+ return instance
1963
+
1964
+ @classmethod
1965
+ def fit_known_location(cls, sample: Any, location: float) -> GeneralizedPareto:
1966
+ """Fit the scale and shape by maximum likelihood at a known location (``MLE(sample, location)``).
1967
+
1968
+ Parameters
1969
+ ----------
1970
+ sample : array_like
1971
+ Observations, each at or above ``location`` (for example the peaks over a threshold).
1972
+ location : float
1973
+ The known location ξ (for example the threshold).
1974
+
1975
+ Returns
1976
+ -------
1977
+ GeneralizedPareto
1978
+ The fitted distribution, with location ``location``.
1979
+ """
1980
+ parameters = to_numpy_doubles(_nd().GeneralizedPareto().MLE(to_net_doubles(sample), float(location)))
1981
+ return cls(float(parameters[0]), float(parameters[1]), float(parameters[2]))
1982
+
1983
+ def mle(self, sample: Any, location: float | None = None) -> np.ndarray:
1984
+ """Return the maximum likelihood parameters of a sample, without changing this distribution.
1985
+
1986
+ Parameters
1987
+ ----------
1988
+ sample : array_like
1989
+ Observations.
1990
+ location : float, optional
1991
+ A known location; when given only the scale and shape are fitted (``MLE(sample, location)``).
1992
+
1993
+ Returns
1994
+ -------
1995
+ numpy.ndarray
1996
+ ``[location, scale, shape]``.
1997
+ """
1998
+ if location is None:
1999
+ return super().mle(sample)
2000
+ return to_numpy_doubles(self.net.MLE(to_net_doubles(sample), float(location)))
2001
+
2002
+ def modified_method_of_moments(self, sample: Any) -> np.ndarray:
2003
+ """Return the parameters of the modified method of moments (``ModifiedMethodOfMoments``).
2004
+
2005
+ Parameters
2006
+ ----------
2007
+ sample : array_like
2008
+ Observations.
2009
+
2010
+ Returns
2011
+ -------
2012
+ numpy.ndarray
2013
+ ``[location, scale, shape]``.
2014
+ """
2015
+ return to_numpy_doubles(self.net.ModifiedMethodOfMoments(to_net_doubles(sample)))
2016
+
2017
+
2018
+ @_register("Gumbel")
2019
+ class Gumbel(UnivariateDistribution):
2020
+ """Gumbel (extreme value type I) distribution.
2021
+
2022
+ Parameters
2023
+ ----------
2024
+ location : float, optional
2025
+ Location ξ; default 100.
2026
+ scale : float, optional
2027
+ Scale α > 0; default 10.
2028
+ """
2029
+
2030
+ def __init__(self, location: float = 100.0, scale: float = 10.0) -> None:
2031
+ super().__init__(_nd().Gumbel(float(location), float(scale)))
2032
+
2033
+ location = _net_property("Xi", _LOCATION)
2034
+ scale = _net_property("Alpha", _SCALE)
2035
+
2036
+ def set_parameters_from_mle(self, sample: Any) -> None:
2037
+ """Fit the parameters in place by maximum likelihood (``SetParametersFromMLE``).
2038
+
2039
+ Parameters
2040
+ ----------
2041
+ sample : array_like
2042
+ Observations.
2043
+
2044
+ Raises
2045
+ ------
2046
+ TypeError
2047
+ If the distribution is frozen.
2048
+ """
2049
+ self._ensure_mutable()
2050
+ self.net.SetParametersFromMLE(to_net_doubles(sample))
2051
+
2052
+
2053
+ @_register("Normal")
2054
+ class Normal(_MonteCarloIntervals, UnivariateDistribution):
2055
+ """Normal distribution.
2056
+
2057
+ Parameters
2058
+ ----------
2059
+ mean : float, optional
2060
+ Mean μ; default 0.
2061
+ standard_deviation : float, optional
2062
+ Standard deviation σ > 0; default 1.
2063
+ """
2064
+
2065
+ def __init__(self, mean: float = 0.0, standard_deviation: float = 1.0) -> None:
2066
+ super().__init__(_nd().Normal(float(mean), float(standard_deviation)))
2067
+
2068
+ mean = _net_property("Mu", "The mean μ (``Mu``); settable.")
2069
+ standard_deviation = _net_property("Sigma", "The standard deviation σ > 0 (``Sigma``); settable.")
2070
+
2071
+ def normal_confidence_intervals(self, sample_size: int, probabilities: Any, percentiles: Any) -> np.ndarray:
2072
+ """Return normal-approximation confidence limits of quantiles (``NormalConfidenceIntervals``).
2073
+
2074
+ Parameters
2075
+ ----------
2076
+ sample_size : int
2077
+ The sample size.
2078
+ probabilities : array_like
2079
+ Non-exceedance probabilities of the quantiles.
2080
+ percentiles : array_like
2081
+ Confidence percentiles, for example ``[0.05, 0.95]``.
2082
+
2083
+ Returns
2084
+ -------
2085
+ numpy.ndarray
2086
+ Array of shape ``(len(probabilities), len(percentiles))``.
2087
+ """
2088
+ return to_numpy_matrix(self.net.NormalConfidenceIntervals(int(sample_size), to_net_doubles(probabilities),
2089
+ to_net_doubles(percentiles)))
2090
+
2091
+ def noncentral_t_confidence_intervals(self, sample_size: int, probabilities: Any,
2092
+ percentiles: Any) -> np.ndarray:
2093
+ """Return exact noncentral-t confidence limits of quantiles (``NoncentralTConfidenceIntervals``).
2094
+
2095
+ Parameters
2096
+ ----------
2097
+ sample_size : int
2098
+ The sample size.
2099
+ probabilities : array_like
2100
+ Non-exceedance probabilities of the quantiles.
2101
+ percentiles : array_like
2102
+ Confidence percentiles, for example ``[0.05, 0.95]``.
2103
+
2104
+ Returns
2105
+ -------
2106
+ numpy.ndarray
2107
+ Array of shape ``(len(probabilities), len(percentiles))``.
2108
+ """
2109
+ return to_numpy_matrix(self.net.NoncentralTConfidenceIntervals(
2110
+ int(sample_size), to_net_doubles(probabilities), to_net_doubles(percentiles)))
2111
+
2112
+ def expected_probability(self, sample_size: int, probability: float) -> float:
2113
+ """Return the expected non-exceedance probability of the estimated quantile (``ExpectedProbability``).
2114
+
2115
+ Parameters
2116
+ ----------
2117
+ sample_size : int
2118
+ The sample size.
2119
+ probability : float
2120
+ The nominal non-exceedance probability.
2121
+
2122
+ Returns
2123
+ -------
2124
+ float
2125
+ The expected probability, which accounts for parameter uncertainty.
2126
+ """
2127
+ return float(self.net.ExpectedProbability(int(sample_size), float(probability)))
2128
+
2129
+
2130
+ @_register("LogNormal")
2131
+ class LogNormal(_LogMoments, _MonteCarloIntervals, UnivariateDistribution):
2132
+ """Log-normal distribution with log-space parameters (``Numerics.Distributions.LogNormal``).
2133
+
2134
+ Parameters
2135
+ ----------
2136
+ mean_of_log : float, optional
2137
+ Mean of log_base(X); default 3.
2138
+ standard_deviation_of_log : float, optional
2139
+ Standard deviation of log_base(X); default 0.5.
2140
+ base : float, optional
2141
+ The logarithm base, finite and greater than one; default 10.
2142
+ """
2143
+
2144
+ def __init__(self, mean_of_log: float = 3.0, standard_deviation_of_log: float = 0.5, base: float = 10.0) -> None:
2145
+ net = _nd().LogNormal(float(mean_of_log), float(standard_deviation_of_log))
2146
+ net.Base = float(base)
2147
+ super().__init__(net)
2148
+
2149
+ mean_of_log = _net_property("Mu", "The mean of log_base(X) (``Mu``); settable.")
2150
+ standard_deviation_of_log = _net_property("Sigma", "The standard deviation of log_base(X) (``Sigma``); settable.")
2151
+ base = _net_property("Base", "The logarithm base, finite and greater than one (``Base``); settable.")
2152
+
2153
+ def direct_method_of_moments(self, mean: float, standard_deviation: float) -> tuple[float, float]:
2154
+ """Return (mean_of_log, standard_deviation_of_log) for real-space moments (``DirectMethodOfMoments``).
2155
+
2156
+ Parameters
2157
+ ----------
2158
+ mean : float
2159
+ The mean of X.
2160
+ standard_deviation : float
2161
+ The standard deviation of X.
2162
+
2163
+ Returns
2164
+ -------
2165
+ tuple of float
2166
+ The log-space parameters in this distribution's base.
2167
+ """
2168
+ values = self.net.DirectMethodOfMoments(float(mean), float(standard_deviation))
2169
+ return float(values[0]), float(values[1])
2170
+
2171
+
2172
+ @_register("LnNormal")
2173
+ class LnNormal(_LogMoments, UnivariateDistribution):
2174
+ """Natural-log normal distribution with real-space moments (``LnNormal``).
2175
+
2176
+ Parameters
2177
+ ----------
2178
+ mean : float, optional
2179
+ Real-space mean of X; default 10.
2180
+ standard_deviation : float, optional
2181
+ Real-space standard deviation of X; default 10.
2182
+ """
2183
+
2184
+ def __init__(self, mean: float = 10.0, standard_deviation: float = 10.0) -> None:
2185
+ super().__init__(_nd().LnNormal(float(mean), float(standard_deviation)))
2186
+
2187
+ mean_of_log = _net_property("Mu", "The mean μ of ln X (``Mu``); settable.")
2188
+ standard_deviation_of_log = _net_property("Sigma", "The standard deviation σ of ln X (``Sigma``); settable.")
2189
+
2190
+ @staticmethod
2191
+ def direct_method_of_moments(mean: float, standard_deviation: float) -> tuple[float, float]:
2192
+ """Return (mu, sigma) of ln X for real-space moments (``LnNormal.DirectMethodOfMoments``).
2193
+
2194
+ Parameters
2195
+ ----------
2196
+ mean : float
2197
+ The mean of X.
2198
+ standard_deviation : float
2199
+ The standard deviation of X.
2200
+
2201
+ Returns
2202
+ -------
2203
+ tuple of float
2204
+ ``(mu, sigma)`` as Numerics computes them, before the ``Sigma`` setter's clamp.
2205
+ """
2206
+ values = _nd().LnNormal.DirectMethodOfMoments(float(mean), float(standard_deviation))
2207
+ return float(values[0]), float(values[1])
2208
+
2209
+ @classmethod
2210
+ def from_log_parameters(cls, mu: float, sigma: float) -> LnNormal:
2211
+ """Create the distribution from mu and sigma of ln X (the ``Mu`` and ``Sigma`` setters).
2212
+
2213
+ Parameters
2214
+ ----------
2215
+ mu : float
2216
+ The mean of ln X.
2217
+ sigma : float
2218
+ The standard deviation of ln X.
2219
+
2220
+ Returns
2221
+ -------
2222
+ LnNormal
2223
+ The distribution; Numerics derives the real-space moments from mu and sigma.
2224
+ """
2225
+ net = _nd().LnNormal()
2226
+ net.Mu = float(mu)
2227
+ net.Sigma = float(sigma)
2228
+ instance = cls.__new__(cls)
2229
+ UnivariateDistribution.__init__(instance, net)
2230
+ return instance
2231
+
2232
+ def indirect_method_of_moments(self, sample: Any) -> np.ndarray:
2233
+ """Return the product moments of ln(sample) (``LnNormal.IndirectMethodOfMoments``).
2234
+
2235
+ Parameters
2236
+ ----------
2237
+ sample : array_like
2238
+ Positive observations.
2239
+
2240
+ Returns
2241
+ -------
2242
+ numpy.ndarray
2243
+ ``[mean, standard deviation, skewness, kurtosis]`` of the natural logarithms.
2244
+ """
2245
+ return to_numpy_doubles(_nd().LnNormal.IndirectMethodOfMoments(to_net_doubles(sample)))
2246
+
2247
+
2248
+ @_register("PearsonTypeIII")
2249
+ class PearsonTypeIII(_PearsonForm, UnivariateDistribution):
2250
+ """Pearson type III distribution.
2251
+
2252
+ Parameters
2253
+ ----------
2254
+ mean : float, optional
2255
+ Mean μ; default 100.
2256
+ standard_deviation : float, optional
2257
+ Standard deviation σ > 0; default 10.
2258
+ skew : float, optional
2259
+ Skewness γ; default 0.
2260
+ """
2261
+
2262
+ def __init__(self, mean: float = 100.0, standard_deviation: float = 10.0, skew: float = 0.0) -> None:
2263
+ super().__init__(_nd().PearsonTypeIII(float(mean), float(standard_deviation), float(skew)))
2264
+
2265
+ mean = _net_property("Mu", "The mean μ (``Mu``); settable.")
2266
+ standard_deviation = _net_property("Sigma", "The standard deviation σ > 0 (``Sigma``); settable.")
2267
+ skew = _net_property("Gamma", "The skewness γ (``Gamma``); settable.")
2268
+
2269
+
2270
+ @_register("LogPearsonTypeIII")
2271
+ class LogPearsonTypeIII(_LogMoments, _PearsonForm, UnivariateDistribution):
2272
+ """Log-Pearson type III distribution: Pearson type III in log_base(X).
2273
+
2274
+ Parameters
2275
+ ----------
2276
+ mean_of_log : float, optional
2277
+ Mean of log_base(X); default 3.
2278
+ standard_deviation_of_log : float, optional
2279
+ Standard deviation of log_base(X); default 0.5.
2280
+ skew_of_log : float, optional
2281
+ Skewness of log_base(X); default 0.
2282
+ base : float, optional
2283
+ The logarithm base, finite and greater than one; default 10.
2284
+ """
2285
+
2286
+ def __init__(self, mean_of_log: float = 3.0, standard_deviation_of_log: float = 0.5, skew_of_log: float = 0.0,
2287
+ base: float = 10.0) -> None:
2288
+ net = _nd().LogPearsonTypeIII(float(mean_of_log), float(standard_deviation_of_log), float(skew_of_log))
2289
+ net.Base = float(base)
2290
+ super().__init__(net)
2291
+
2292
+ mean_of_log = _net_property("Mu", "The mean of log_base(X) (``Mu``); settable.")
2293
+ standard_deviation_of_log = _net_property("Sigma", "The standard deviation of log_base(X) (``Sigma``); settable.")
2294
+ skew_of_log = _net_property("Gamma", "The skewness of log_base(X) (``Gamma``); settable.")
2295
+ base = _net_property("Base", "The logarithm base, finite and greater than one (``Base``); settable.")
2296
+
2297
+ def log_space(self) -> PearsonTypeIII:
2298
+ """Return the Pearson type III distribution of log_base(X).
2299
+
2300
+ Returns
2301
+ -------
2302
+ PearsonTypeIII
2303
+ ``PearsonTypeIII(mean_of_log, standard_deviation_of_log, skew_of_log)``.
2304
+ """
2305
+ return PearsonTypeIII(self.mean_of_log, self.standard_deviation_of_log, self.skew_of_log)
2306
+
2307
+
2308
+ @_register("KappaFour")
2309
+ class KappaFour(UnivariateDistribution):
2310
+ """Four-parameter kappa distribution (Hosking).
2311
+
2312
+ Parameters
2313
+ ----------
2314
+ location : float, optional
2315
+ Location ξ; default 100.
2316
+ scale : float, optional
2317
+ Scale α > 0; default 10.
2318
+ shape : float, optional
2319
+ Shape κ; default 0.
2320
+ shape2 : float, optional
2321
+ Second shape h; default 0.
2322
+ """
2323
+
2324
+ def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0, shape2: float = 0.0) -> None:
2325
+ super().__init__(_nd().KappaFour(float(location), float(scale), float(shape), float(shape2)))
2326
+
2327
+ location = _net_property("Xi", _LOCATION)
2328
+ scale = _net_property("Alpha", _SCALE)
2329
+ shape = _net_property("Kappa", _SHAPE)
2330
+ shape2 = _net_property("Hondo", "The second shape parameter h (``Hondo``); settable.")
2331
+
2332
+
2333
+ @_register("Uniform")
2334
+ class Uniform(UnivariateDistribution):
2335
+ """Continuous uniform distribution on [minimum, maximum] (``Uniform``).
2336
+
2337
+ Parameters
2338
+ ----------
2339
+ minimum : float, optional
2340
+ Lower end; default 0.
2341
+ maximum : float, optional
2342
+ Upper end; default 1.
2343
+ """
2344
+
2345
+ def __init__(self, minimum: float = 0.0, maximum: float = 1.0) -> None:
2346
+ super().__init__(_nd().Uniform(float(minimum), float(maximum)))
2347
+
2348
+ minimum = _net_property("Min", "The lower end of the support (``Min``); settable.")
2349
+ maximum = _net_property("Max", "The upper end of the support (``Max``); settable.")
2350
+
2351
+
2352
+ @_register("BetaDistribution", "Beta")
2353
+ class Beta(UnivariateDistribution):
2354
+ """Beta distribution on [0, 1] (``BetaDistribution``).
2355
+
2356
+ Parameters
2357
+ ----------
2358
+ alpha : float, optional
2359
+ First shape α > 0; default 2.
2360
+ beta : float, optional
2361
+ Second shape β > 0; default 2.
2362
+ """
2363
+
2364
+ def __init__(self, alpha: float = 2.0, beta: float = 2.0) -> None:
2365
+ super().__init__(_nd().BetaDistribution(float(alpha), float(beta)))
2366
+
2367
+ alpha = _net_property("Alpha", "The first shape α > 0 (``Alpha``); settable.")
2368
+ beta = _net_property("Beta", "The second shape β > 0 (``Beta``); settable.")
2369
+
2370
+
2371
+ @_register("Poisson")
2372
+ class Poisson(UnivariateDistribution):
2373
+ """Poisson distribution (discrete; ``pdf`` is the probability mass).
2374
+
2375
+ Parameters
2376
+ ----------
2377
+ rate : float, optional
2378
+ Rate λ > 0; default 1.
2379
+ """
2380
+
2381
+ def __init__(self, rate: float = 1.0) -> None:
2382
+ super().__init__(_nd().Poisson(float(rate)))
2383
+
2384
+ rate = _net_property("Lambda", "The rate λ > 0 (``Lambda``); settable.")
2385
+
2386
+
2387
+ @_register("Bernoulli")
2388
+ class Bernoulli(UnivariateDistribution):
2389
+ """Bernoulli distribution on {0, 1} (discrete; ``pdf`` is the probability mass).
2390
+
2391
+ Parameters
2392
+ ----------
2393
+ probability : float, optional
2394
+ Probability of a one, in [0, 1]; default 0.5.
2395
+ """
2396
+
2397
+ def __init__(self, probability: float = 0.5) -> None:
2398
+ super().__init__(_nd().Bernoulli(float(probability)))
2399
+
2400
+ probability = _net_property("Probability", "The probability p of a one (``Probability``); settable.")
2401
+ complement = _net_property("Complement", "The probability 1 - p of a zero (``Complement``).", settable=False)
2402
+
2403
+
2404
+ @_register("Binomial")
2405
+ class Binomial(UnivariateDistribution):
2406
+ """Binomial distribution: successes in ``number_of_trials`` trials (discrete).
2407
+
2408
+ Parameters
2409
+ ----------
2410
+ probability : float, optional
2411
+ Probability of success per trial, in [0, 1]; default 0.5.
2412
+ number_of_trials : int, optional
2413
+ The number of trials n >= 0; default 10.
2414
+ """
2415
+
2416
+ def __init__(self, probability: float = 0.5, number_of_trials: int = 10) -> None:
2417
+ super().__init__(_nd().Binomial(float(probability), int(number_of_trials)))
2418
+
2419
+ probability = _net_property("ProbabilityOfSuccess", "The probability of success p (``ProbabilityOfSuccess``); "
2420
+ "settable.")
2421
+ number_of_trials = _net_property("NumberOfTrials", "The number of trials n (``NumberOfTrials``); settable.",
2422
+ kind=int)
2423
+ complement = _net_property("Complement", "The probability of failure 1 - p (``Complement``).", settable=False)
2424
+
2425
+
2426
+ @_register("Cauchy")
2427
+ class Cauchy(UnivariateDistribution):
2428
+ """Cauchy distribution.
2429
+
2430
+ Parameters
2431
+ ----------
2432
+ location : float, optional
2433
+ Location x0; default 0.
2434
+ scale : float, optional
2435
+ Scale γ > 0; default 1.
2436
+ """
2437
+
2438
+ def __init__(self, location: float = 0.0, scale: float = 1.0) -> None:
2439
+ super().__init__(_nd().Cauchy(float(location), float(scale)))
2440
+
2441
+ location = _net_property("X0", "The location x0 (``X0``); settable.")
2442
+ scale = _net_property("Gamma", "The scale γ > 0 (``Gamma``); settable.")
2443
+
2444
+
2445
+ @_register("ChiSquared")
2446
+ class ChiSquared(UnivariateDistribution):
2447
+ """Chi-squared distribution.
2448
+
2449
+ Parameters
2450
+ ----------
2451
+ degrees_of_freedom : int, optional
2452
+ Degrees of freedom ν >= 1; default 10.
2453
+ """
2454
+
2455
+ def __init__(self, degrees_of_freedom: int = 10) -> None:
2456
+ super().__init__(_nd().ChiSquared(int(degrees_of_freedom)))
2457
+
2458
+ degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν (``DegreesOfFreedom``); "
2459
+ "settable.", kind=int)
2460
+
2461
+
2462
+ @_register("Deterministic")
2463
+ class Deterministic(UnivariateDistribution):
2464
+ """A point mass at one value (``Deterministic``).
2465
+
2466
+ Parameters
2467
+ ----------
2468
+ value : float, optional
2469
+ The value; default 0.5.
2470
+ """
2471
+
2472
+ def __init__(self, value: float = 0.5) -> None:
2473
+ super().__init__(_nd().Deterministic(float(value)))
2474
+
2475
+ value = _net_property("Value", "The value of the point mass (``Value``); settable.")
2476
+
2477
+
2478
+ @_register("Exponential")
2479
+ class Exponential(UnivariateDistribution):
2480
+ """Shifted exponential distribution: ``F(x) = 1 - exp(-(x - ξ) / α)`` for x >= ξ.
2481
+
2482
+ Parameters
2483
+ ----------
2484
+ location : float, optional
2485
+ Location ξ; default 100.
2486
+ scale : float, optional
2487
+ Scale α > 0 (the mean excess); default 10.
2488
+ """
2489
+
2490
+ def __init__(self, location: float = 100.0, scale: float = 10.0) -> None:
2491
+ super().__init__(_nd().Exponential(float(location), float(scale)))
2492
+
2493
+ location = _net_property("Xi", _LOCATION)
2494
+ scale = _net_property("Alpha", _SCALE)
2495
+
2496
+
2497
+ @_register("GammaDistribution")
2498
+ class GammaDistribution(_WilsonHilferty, UnivariateDistribution):
2499
+ """Gamma distribution with scale θ and shape κ (``GammaDistribution``).
2500
+
2501
+ Parameters
2502
+ ----------
2503
+ scale : float, optional
2504
+ Scale θ > 0; default 10.
2505
+ shape : float, optional
2506
+ Shape κ > 0; default 2.
2507
+ """
2508
+
2509
+ def __init__(self, scale: float = 10.0, shape: float = 2.0) -> None:
2510
+ super().__init__(_nd().GammaDistribution(float(scale), float(shape)))
2511
+
2512
+ scale = _net_property("Theta", "The scale θ > 0 (``Theta``); settable.")
2513
+ shape = _net_property("Kappa", "The shape κ > 0 (``Kappa``); settable.")
2514
+ rate = _net_property("Rate", "The rate 1 / θ (``Rate``).", settable=False)
2515
+
2516
+ def estimate_mle_newton_raphson(self, sample: Any) -> None:
2517
+ """Fit the parameters in place by maximum likelihood with Newton-Raphson iterations (``MLE_NR``).
2518
+
2519
+ Parameters
2520
+ ----------
2521
+ sample : array_like
2522
+ Positive observations.
2523
+
2524
+ Raises
2525
+ ------
2526
+ TypeError
2527
+ If the distribution is frozen.
2528
+ """
2529
+ self._ensure_mutable()
2530
+ self.net.MLE_NR(to_net_doubles(sample))
2531
+
2532
+ def estimate_mle_bobee(self, sample: Any) -> None:
2533
+ """Fit the parameters in place by the maximum likelihood approximation of Bobee (``MLE_Bobee``).
2534
+
2535
+ Parameters
2536
+ ----------
2537
+ sample : array_like
2538
+ Positive observations.
2539
+
2540
+ Raises
2541
+ ------
2542
+ TypeError
2543
+ If the distribution is frozen.
2544
+ """
2545
+ self._ensure_mutable()
2546
+ self.net.MLE_Bobee(to_net_doubles(sample))
2547
+
2548
+ @staticmethod
2549
+ def frequency_factor_kp(skewness: float, probability: float) -> float:
2550
+ """Return the approximate Pearson type III frequency factor K(γ, p) (``GammaDistribution.FrequencyFactorKp``).
2551
+
2552
+ The Cornish-Fisher expansion for abs(γ) <= 2 and the modified Wilson-Hilferty transformation
2553
+ (Kirby, 1972) beyond; it approximates, and is not, the exact standardized quantile.
2554
+
2555
+ Parameters
2556
+ ----------
2557
+ skewness : float
2558
+ The skewness γ.
2559
+ probability : float
2560
+ The non-exceedance probability.
2561
+
2562
+ Returns
2563
+ -------
2564
+ float
2565
+ The standardized quantile, so the quantile is ``mean + K * standard_deviation``.
2566
+ """
2567
+ return float(_nd().GammaDistribution.FrequencyFactorKp(float(skewness), float(probability)))
2568
+
2569
+ @staticmethod
2570
+ def partial_kp(skewness: float, probability: float) -> float:
2571
+ """Return the derivative of the frequency factor with respect to the skewness (``PartialKp``).
2572
+
2573
+ Parameters
2574
+ ----------
2575
+ skewness : float
2576
+ The skewness γ.
2577
+ probability : float
2578
+ The non-exceedance probability.
2579
+
2580
+ Returns
2581
+ -------
2582
+ float
2583
+ The derivative dK/dγ at ``(skewness, probability)``.
2584
+ """
2585
+ return float(_nd().GammaDistribution.PartialKp(float(skewness), float(probability)))
2586
+
2587
+
2588
+ @_register("GeneralizedBeta")
2589
+ class GeneralizedBeta(UnivariateDistribution):
2590
+ """Beta distribution rescaled to [minimum, maximum] (``GeneralizedBeta``).
2591
+
2592
+ Parameters
2593
+ ----------
2594
+ alpha : float, optional
2595
+ First shape α > 0; default 2.
2596
+ beta : float, optional
2597
+ Second shape β > 0; default 2.
2598
+ minimum : float, optional
2599
+ Lower end; default 0.
2600
+ maximum : float, optional
2601
+ Upper end; default 1.
2602
+ """
2603
+
2604
+ def __init__(self, alpha: float = 2.0, beta: float = 2.0, minimum: float = 0.0, maximum: float = 1.0) -> None:
2605
+ super().__init__(_nd().GeneralizedBeta(float(alpha), float(beta), float(minimum), float(maximum)))
2606
+
2607
+ alpha = _net_property("Alpha", "The first shape α > 0 (``Alpha``); settable.")
2608
+ beta = _net_property("Beta", "The second shape β > 0 (``Beta``); settable.")
2609
+ minimum = _net_property("Min", "The lower end of the support (``Min``); settable.")
2610
+ maximum = _net_property("Max", "The upper end of the support (``Max``); settable.")
2611
+
2612
+ @classmethod
2613
+ def pert(cls, minimum: float, mode: float, maximum: float, scale: float | None = None) -> GeneralizedBeta:
2614
+ """Return the PERT beta distribution through a mode (``GeneralizedBeta.PERT``).
2615
+
2616
+ Parameters
2617
+ ----------
2618
+ minimum : float
2619
+ Lower end.
2620
+ mode : float
2621
+ The most likely value.
2622
+ maximum : float
2623
+ Upper end.
2624
+ scale : float, optional
2625
+ The PERT weight λ of the mode; None, the default, is Numerics' ``PERT(min, mode, max)``
2626
+ (λ = 4).
2627
+
2628
+ Returns
2629
+ -------
2630
+ GeneralizedBeta
2631
+ ``α = 1 + λ (mode - min) / (max - min)``, ``β = 1 + λ (max - mode) / (max - min)``.
2632
+ """
2633
+ pert = _nd().GeneralizedBeta.PERT
2634
+ net = (pert(float(minimum), float(mode), float(maximum)) if scale is None
2635
+ else pert(float(minimum), float(mode), float(maximum), float(scale)))
2636
+ instance = cls.__new__(cls)
2637
+ UnivariateDistribution.__init__(instance, net)
2638
+ return instance
2639
+
2640
+ def set_parameters_from_moments(self, mean: float, standard_deviation: float, minimum: float,
2641
+ maximum: float) -> None:
2642
+ """Set the shapes in place from a mean and standard deviation on [minimum, maximum].
2643
+
2644
+ Parameters
2645
+ ----------
2646
+ mean : float
2647
+ The mean.
2648
+ standard_deviation : float
2649
+ The standard deviation.
2650
+ minimum : float
2651
+ Lower end.
2652
+ maximum : float
2653
+ Upper end.
2654
+
2655
+ Raises
2656
+ ------
2657
+ TypeError
2658
+ If the distribution is frozen.
2659
+
2660
+ Notes
2661
+ -----
2662
+ Calls ``SetParametersFromMoments``.
2663
+ """
2664
+ self._ensure_mutable()
2665
+ self.net.SetParametersFromMoments(float(mean), float(standard_deviation), float(minimum), float(maximum))
2666
+
2667
+
2668
+ @_register("GeneralizedLogistic")
2669
+ class GeneralizedLogistic(_KappaMoments, UnivariateDistribution):
2670
+ """Generalized logistic distribution (Hosking parameterization).
2671
+
2672
+ ``F(x) = 1 / (1 + exp(-y))`` with ``y = -ln(1 - κ (x - ξ) / α) / κ`` (``y = (x - ξ) / α`` at κ = 0).
2673
+
2674
+ Parameters
2675
+ ----------
2676
+ location : float, optional
2677
+ Location ξ; default 100.
2678
+ scale : float, optional
2679
+ Scale α > 0; default 10.
2680
+ shape : float, optional
2681
+ Shape κ (Hosking sign); default 0.
2682
+ """
2683
+
2684
+ def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
2685
+ super().__init__(_nd().GeneralizedLogistic(float(location), float(scale), float(shape)))
2686
+
2687
+ location = _net_property("Xi", _LOCATION)
2688
+ scale = _net_property("Alpha", _SCALE)
2689
+ shape = _net_property("Kappa", _SHAPE)
2690
+
2691
+ @_FamilyMethod
2692
+ def moments_from_parameters(self, parameters: Any) -> np.ndarray:
2693
+ """Return the product moments of a parameter vector (``MomentsFromParameters``).
2694
+
2695
+ Callable on the class without an instance or on an instance.
2696
+
2697
+ Parameters
2698
+ ----------
2699
+ parameters : array_like
2700
+ ``[location, scale, shape]``.
2701
+
2702
+ Returns
2703
+ -------
2704
+ numpy.ndarray
2705
+ ``[mean, standard deviation, skewness, kurtosis]``.
2706
+ """
2707
+ return to_numpy_doubles(_net_of(self).MomentsFromParameters(to_net_doubles(parameters)))
2708
+
2709
+
2710
+ @_register("GeneralizedNormal")
2711
+ class GeneralizedNormal(UnivariateDistribution):
2712
+ """Generalized normal distribution (Hosking parameterization).
2713
+
2714
+ ``F(x) = Φ(y)`` with ``y = -ln(1 - κ (x - ξ) / α) / κ`` (``y = (x - ξ) / α`` at κ = 0).
2715
+
2716
+ Parameters
2717
+ ----------
2718
+ location : float, optional
2719
+ Location ξ; default 100.
2720
+ scale : float, optional
2721
+ Scale α > 0; default 10.
2722
+ shape : float, optional
2723
+ Shape κ (Hosking sign); default 0.
2724
+ """
2725
+
2726
+ def __init__(self, location: float = 100.0, scale: float = 10.0, shape: float = 0.0) -> None:
2727
+ super().__init__(_nd().GeneralizedNormal(float(location), float(scale), float(shape)))
2728
+
2729
+ location = _net_property("Xi", _LOCATION)
2730
+ scale = _net_property("Alpha", _SCALE)
2731
+ shape = _net_property("Kappa", _SHAPE)
2732
+
2733
+
2734
+ @_register("Geometric")
2735
+ class Geometric(UnivariateDistribution):
2736
+ """Geometric distribution: failures before the first success (discrete, support 0, 1, 2, ...).
2737
+
2738
+ Parameters
2739
+ ----------
2740
+ probability : float, optional
2741
+ Probability of success per trial, in (0, 1]; default 0.5.
2742
+ """
2743
+
2744
+ def __init__(self, probability: float = 0.5) -> None:
2745
+ super().__init__(_nd().Geometric(float(probability)))
2746
+
2747
+ probability = _net_property("ProbabilityOfSuccess", "The probability of success p (``ProbabilityOfSuccess``); "
2748
+ "settable.")
2749
+
2750
+
2751
+ @_register("InverseChiSquared")
2752
+ class InverseChiSquared(UnivariateDistribution):
2753
+ """Scaled inverse chi-squared distribution (``InverseChiSquared``).
2754
+
2755
+ The inverse gamma distribution with shape ν/2 and scale ν τ²/2; the mean is ν τ²/(ν - 2) for ν > 2.
2756
+
2757
+ Parameters
2758
+ ----------
2759
+ degrees_of_freedom : int, optional
2760
+ Degrees of freedom ν >= 1; default 10.
2761
+ scale : float, optional
2762
+ The scale τ² > 0 (Numerics ``Sigma``); default 1.
2763
+ """
2764
+
2765
+ def __init__(self, degrees_of_freedom: int = 10, scale: float = 1.0) -> None:
2766
+ super().__init__(_nd().InverseChiSquared(int(degrees_of_freedom), float(scale)))
2767
+
2768
+ degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν (``DegreesOfFreedom``); "
2769
+ "settable.", kind=int)
2770
+ scale = _net_property("Sigma", "The scale τ² > 0 (``Sigma``); settable.")
2771
+ precision = _net_property("Precision", "The value 1 / τ⁴, the reciprocal of the squared scale (``Precision``).",
2772
+ settable=False)
2773
+
2774
+
2775
+ @_register("InverseGamma")
2776
+ class InverseGamma(UnivariateDistribution):
2777
+ """Inverse gamma distribution with scale β and shape α (``InverseGamma``).
2778
+
2779
+ Parameters
2780
+ ----------
2781
+ scale : float, optional
2782
+ Scale β > 0; default 0.5.
2783
+ shape : float, optional
2784
+ Shape α > 0; default 2.
2785
+ """
2786
+
2787
+ def __init__(self, scale: float = 0.5, shape: float = 2.0) -> None:
2788
+ super().__init__(_nd().InverseGamma(float(scale), float(shape)))
2789
+
2790
+ scale = _net_property("Beta", "The scale β > 0 (``Beta``); settable.")
2791
+ shape = _net_property("Alpha", "The shape α > 0 (``Alpha``); settable.")
2792
+
2793
+
2794
+ @_register("Logistic")
2795
+ class Logistic(UnivariateDistribution):
2796
+ """Logistic distribution.
2797
+
2798
+ Parameters
2799
+ ----------
2800
+ location : float, optional
2801
+ Location ξ; default 0.
2802
+ scale : float, optional
2803
+ Scale α > 0; default 0.1.
2804
+ """
2805
+
2806
+ def __init__(self, location: float = 0.0, scale: float = 0.1) -> None:
2807
+ super().__init__(_nd().Logistic(float(location), float(scale)))
2808
+
2809
+ location = _net_property("Xi", _LOCATION)
2810
+ scale = _net_property("Alpha", _SCALE)
2811
+
2812
+
2813
+ @_register("NoncentralT")
2814
+ class NoncentralT(UnivariateDistribution):
2815
+ """Noncentral Student's t distribution.
2816
+
2817
+ Parameters
2818
+ ----------
2819
+ degrees_of_freedom : float, optional
2820
+ Degrees of freedom ν > 0; default 10.
2821
+ noncentrality : float, optional
2822
+ Noncentrality μ; default 0.
2823
+ """
2824
+
2825
+ def __init__(self, degrees_of_freedom: float = 10.0, noncentrality: float = 0.0) -> None:
2826
+ super().__init__(_nd().NoncentralT(float(degrees_of_freedom), float(noncentrality)))
2827
+
2828
+ degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν > 0 (``DegreesOfFreedom``); "
2829
+ "settable.")
2830
+ noncentrality = _net_property("Noncentrality", "The noncentrality μ (``Noncentrality``); settable.")
2831
+
2832
+
2833
+ @_register("Pareto")
2834
+ class Pareto(UnivariateDistribution):
2835
+ """Pareto (type I) distribution: ``F(x) = 1 - (xm / x)^α`` for x >= xm.
2836
+
2837
+ Parameters
2838
+ ----------
2839
+ scale : float, optional
2840
+ Scale xm > 0, the lower end of the support; default 1.
2841
+ shape : float, optional
2842
+ Shape α > 0; default 10.
2843
+ """
2844
+
2845
+ def __init__(self, scale: float = 1.0, shape: float = 10.0) -> None:
2846
+ super().__init__(_nd().Pareto(float(scale), float(shape)))
2847
+
2848
+ scale = _net_property("Xm", "The scale xm > 0 (``Xm``); settable.")
2849
+ shape = _net_property("Alpha", "The shape α > 0 (``Alpha``); settable.")
2850
+
2851
+
2852
+ @_register("Pert")
2853
+ class Pert(UnivariateDistribution):
2854
+ """PERT distribution: a beta on [minimum, maximum] weighted towards the most likely value.
2855
+
2856
+ Parameters
2857
+ ----------
2858
+ minimum : float, optional
2859
+ Lower end a; default 0.
2860
+ most_likely : float, optional
2861
+ The mode c; default 0.5.
2862
+ maximum : float, optional
2863
+ Upper end b; default 1.
2864
+ """
2865
+
2866
+ def __init__(self, minimum: float = 0.0, most_likely: float = 0.5, maximum: float = 1.0) -> None:
2867
+ super().__init__(_nd().Pert(float(minimum), float(most_likely), float(maximum)))
2868
+
2869
+ minimum = _net_property("Min", "The lower end a (``Min``); settable.")
2870
+ most_likely = _net_property("MostLikely", "The most likely value c (``MostLikely``); settable.")
2871
+ maximum = _net_property("Max", "The upper end b (``Max``); settable.")
2872
+
2873
+ @property
2874
+ def beta(self) -> GeneralizedBeta:
2875
+ """The underlying beta distribution on [a, b], wrapped (``Beta``)."""
2876
+ return wrap(self.net.Beta)
2877
+
2878
+
2879
+ class _PertPercentiles:
2880
+ """The percentile parameters shared by the percentile PERT families."""
2881
+
2882
+ percentile_5th = _net_property("Percentile5th", "The 5th percentile (``Percentile5th``); settable.")
2883
+ percentile_50th = _net_property("Percentile50th", "The 50th percentile (``Percentile50th``); settable.")
2884
+ percentile_95th = _net_property("Percentile95th", "The 95th percentile (``Percentile95th``); settable.")
2885
+
2886
+ def solve_parameters(self) -> None:
2887
+ """Solve the PERT parameters that reproduce the three percentiles (``SolveParameters``).
2888
+
2889
+ Raises
2890
+ ------
2891
+ TypeError
2892
+ If the distribution is frozen.
2893
+ """
2894
+ self._ensure_mutable()
2895
+ self.net.SolveParameters()
2896
+
2897
+ def to_pert(self) -> Pert:
2898
+ """Return the solved PERT distribution (``ToPert``).
2899
+
2900
+ Returns
2901
+ -------
2902
+ Pert
2903
+ The PERT distribution whose percentiles match.
2904
+ """
2905
+ return wrap(self.net.ToPert())
2906
+
2907
+
2908
+ translate_class(_PertPercentiles)
2909
+
2910
+
2911
+ @_register("PertPercentile")
2912
+ class PertPercentile(_PertPercentiles, UnivariateDistribution):
2913
+ """PERT distribution given by its 5th, 50th and 95th percentiles (``PertPercentile``).
2914
+
2915
+ Parameters
2916
+ ----------
2917
+ percentile_5th : float, optional
2918
+ The 5th percentile; default 0.05.
2919
+ percentile_50th : float, optional
2920
+ The median; default 0.5.
2921
+ percentile_95th : float, optional
2922
+ The 95th percentile; default 0.95.
2923
+ """
2924
+
2925
+ def __init__(self, percentile_5th: float = 0.05, percentile_50th: float = 0.5,
2926
+ percentile_95th: float = 0.95) -> None:
2927
+ super().__init__(_nd().PertPercentile(float(percentile_5th), float(percentile_50th), float(percentile_95th)))
2928
+
2929
+ min_allowable_value = _net_property("MinAllowableValue", "The smallest value the solved support may take "
2930
+ "(``MinAllowableValue``); settable.")
2931
+ max_allowable_value = _net_property("MaxAllowableValue", "The largest value the solved support may take "
2932
+ "(``MaxAllowableValue``); settable.")
2933
+
2934
+
2935
+ @_register("PertPercentileZ")
2936
+ class PertPercentileZ(_PertPercentiles, UnivariateDistribution):
2937
+ """PERT distribution given by three percentiles, solved in a transformed space (``PertPercentileZ``).
2938
+
2939
+ Parameters
2940
+ ----------
2941
+ percentile_5th : float, optional
2942
+ The 5th percentile, in (0, 1); default 0.05.
2943
+ percentile_50th : float, optional
2944
+ The median, in (0, 1); default 0.5.
2945
+ percentile_95th : float, optional
2946
+ The 95th percentile, in (0, 1); default 0.95.
2947
+ """
2948
+
2949
+ def __init__(self, percentile_5th: float = 0.05, percentile_50th: float = 0.5,
2950
+ percentile_95th: float = 0.95) -> None:
2951
+ super().__init__(_nd().PertPercentileZ(float(percentile_5th), float(percentile_50th),
2952
+ float(percentile_95th)))
2953
+
2954
+
2955
+ @_register("Rayleigh")
2956
+ class Rayleigh(UnivariateDistribution):
2957
+ """Rayleigh distribution.
2958
+
2959
+ Parameters
2960
+ ----------
2961
+ scale : float, optional
2962
+ Scale σ > 0; default 10.
2963
+ """
2964
+
2965
+ def __init__(self, scale: float = 10.0) -> None:
2966
+ super().__init__(_nd().Rayleigh(float(scale)))
2967
+
2968
+ scale = _net_property("Sigma", "The scale σ > 0 (``Sigma``); settable.")
2969
+
2970
+
2971
+ @_register("StudentT")
2972
+ class StudentT(UnivariateDistribution):
2973
+ """Location-scale Student's t distribution.
2974
+
2975
+ Parameters
2976
+ ----------
2977
+ location : float, optional
2978
+ Location μ; default 0.
2979
+ scale : float, optional
2980
+ Scale σ > 0; default 1.
2981
+ degrees_of_freedom : float, optional
2982
+ Degrees of freedom ν > 0; default 10.
2983
+ """
2984
+
2985
+ def __init__(self, location: float = 0.0, scale: float = 1.0, degrees_of_freedom: float = 10.0) -> None:
2986
+ super().__init__(_nd().StudentT(float(location), float(scale), float(degrees_of_freedom)))
2987
+
2988
+ location = _net_property("Mu", "The location μ (``Mu``); settable.")
2989
+ scale = _net_property("Sigma", "The scale σ > 0 (``Sigma``); settable.")
2990
+ degrees_of_freedom = _net_property("DegreesOfFreedom", "The degrees of freedom ν > 0 (``DegreesOfFreedom``); "
2991
+ "settable.")
2992
+
2993
+
2994
+ @_register("Triangular")
2995
+ class Triangular(UnivariateDistribution):
2996
+ """Triangular distribution on [minimum, maximum] with mode ``most_likely``.
2997
+
2998
+ Parameters
2999
+ ----------
3000
+ minimum : float, optional
3001
+ Lower end a; default 0.
3002
+ most_likely : float, optional
3003
+ The mode c; default 0.5.
3004
+ maximum : float, optional
3005
+ Upper end b; default 1.
3006
+ """
3007
+
3008
+ def __init__(self, minimum: float = 0.0, most_likely: float = 0.5, maximum: float = 1.0) -> None:
3009
+ super().__init__(_nd().Triangular(float(minimum), float(most_likely), float(maximum)))
3010
+
3011
+ minimum = _net_property("Min", "The lower end a (``Min``); settable.")
3012
+ most_likely = _net_property("MostLikely", "The mode c (``MostLikely``); settable.")
3013
+ maximum = _net_property("Max", "The upper end b (``Max``); settable.")
3014
+
3015
+
3016
+ class TruncatedDistribution(UnivariateDistribution):
3017
+ """Any univariate distribution truncated to [lower_bound, upper_bound] (``TruncatedDistribution``).
3018
+
3019
+ The density is ``f(x) / (F(upper) - F(lower))`` on the interval. ``type_name`` on an instance is
3020
+ the base distribution's (Numerics reports the base type), so this class is not in :data:`FAMILIES`.
3021
+
3022
+ Parameters
3023
+ ----------
3024
+ distribution : UnivariateDistribution
3025
+ The base distribution; Numerics keeps its own copy.
3026
+ lower_bound : float
3027
+ Lower truncation point.
3028
+ upper_bound : float
3029
+ Upper truncation point, above ``lower_bound``.
3030
+ """
3031
+
3032
+ def __init__(self, distribution: UnivariateDistribution, lower_bound: float, upper_bound: float) -> None:
3033
+ super().__init__(_nd().TruncatedDistribution(distribution.net, float(lower_bound), float(upper_bound)))
3034
+
3035
+ lower_bound = _net_property("Min", "The lower truncation point (``Min``).", settable=False)
3036
+ upper_bound = _net_property("Max", "The upper truncation point (``Max``).", settable=False)
3037
+
3038
+ @property
3039
+ def base_distribution(self) -> UnivariateDistribution:
3040
+ """The base distribution, wrapped (``BaseDistribution``)."""
3041
+ return wrap(self.net.BaseDistribution)
3042
+
3043
+ def _value_key(self) -> tuple:
3044
+ """Return the truncated value key extended with the base distribution's family and state."""
3045
+ return super()._value_key() + self.base_distribution._value_key()
3046
+
3047
+
3048
+ _CLASS_BY_NET_NAME["TruncatedDistribution"] = TruncatedDistribution
3049
+
3050
+
3051
+ @_register("TruncatedNormal")
3052
+ class TruncatedNormal(UnivariateDistribution):
3053
+ """Normal distribution truncated to [minimum, maximum] (``TruncatedNormal``).
3054
+
3055
+ ``mu`` and ``sigma`` are the parameters of the parent normal; the moment properties
3056
+ (``mean``, ``standard_deviation``) are those of the truncated distribution.
3057
+
3058
+ Parameters
3059
+ ----------
3060
+ mu : float, optional
3061
+ Mean of the parent normal; default 0.5.
3062
+ sigma : float, optional
3063
+ Standard deviation of the parent normal, > 0; default 0.2.
3064
+ minimum : float, optional
3065
+ Lower truncation point; default 0.
3066
+ maximum : float, optional
3067
+ Upper truncation point; default 1.
3068
+ """
3069
+
3070
+ def __init__(self, mu: float = 0.5, sigma: float = 0.2, minimum: float = 0.0, maximum: float = 1.0) -> None:
3071
+ super().__init__(_nd().TruncatedNormal(float(mu), float(sigma), float(minimum), float(maximum)))
3072
+
3073
+ mu = _net_property("Mu", "The mean of the parent normal (``Mu``); settable.")
3074
+ sigma = _net_property("Sigma", "The standard deviation of the parent normal (``Sigma``); settable.")
3075
+ minimum = _net_property("Min", "The lower truncation point (``Min``); settable.")
3076
+ maximum = _net_property("Max", "The upper truncation point (``Max``); settable.")
3077
+
3078
+
3079
+ @_register("UniformDiscrete")
3080
+ class UniformDiscrete(UnivariateDistribution):
3081
+ """Discrete uniform distribution on the integers minimum, ..., maximum.
3082
+
3083
+ Parameters
3084
+ ----------
3085
+ minimum : float, optional
3086
+ Lowest value; default 0.
3087
+ maximum : float, optional
3088
+ Highest value; default 1.
3089
+ """
3090
+
3091
+ def __init__(self, minimum: float = 0.0, maximum: float = 1.0) -> None:
3092
+ super().__init__(_nd().UniformDiscrete(float(minimum), float(maximum)))
3093
+
3094
+ minimum = _net_property("Min", "The lowest value (``Min``); settable.")
3095
+ maximum = _net_property("Max", "The highest value (``Max``); settable.")
3096
+ count = _net_property("N", "The number of values, maximum - minimum + 1 (``N``).", settable=False)
3097
+
3098
+
3099
+ @_register("VonMises")
3100
+ class VonMises(UnivariateDistribution):
3101
+ """Von Mises (circular normal) distribution on [-π, π]; the CDF accumulates from -π.
3102
+
3103
+ Parameters
3104
+ ----------
3105
+ mu : float, optional
3106
+ Mean direction μ in [-π, π]; default 0.
3107
+ kappa : float, optional
3108
+ Concentration κ >= 0; default 1.
3109
+ """
3110
+
3111
+ def __init__(self, mu: float = 0.0, kappa: float = 1.0) -> None:
3112
+ super().__init__(_nd().VonMises(float(mu), float(kappa)))
3113
+
3114
+ mu = _net_property("Mu", "The mean direction μ (``Mu``); settable.")
3115
+ kappa = _net_property("Kappa", "The concentration κ (``Kappa``); settable.")
3116
+
3117
+
3118
+ @_register("Weibull")
3119
+ class Weibull(UnivariateDistribution):
3120
+ """Two-parameter Weibull distribution: ``F(x) = 1 - exp(-(x / λ)^κ)``.
3121
+
3122
+ Parameters
3123
+ ----------
3124
+ scale : float, optional
3125
+ Scale λ > 0; default 10.
3126
+ shape : float, optional
3127
+ Shape κ > 0; default 2.
3128
+ """
3129
+
3130
+ def __init__(self, scale: float = 10.0, shape: float = 2.0) -> None:
3131
+ super().__init__(_nd().Weibull(float(scale), float(shape)))
3132
+
3133
+ scale = _net_property("Lambda", "The scale λ > 0 (``Lambda``); settable.")
3134
+ shape = _net_property("Kappa", "The shape κ > 0 (``Kappa``); settable.")
3135
+
3136
+ def solve_mle(self, sample: Any) -> np.ndarray:
3137
+ """Return the maximum likelihood ``[scale, shape]`` from the profile-likelihood root (``SolveMLE``).
3138
+
3139
+ Parameters
3140
+ ----------
3141
+ sample : array_like
3142
+ Positive observations.
3143
+
3144
+ Returns
3145
+ -------
3146
+ numpy.ndarray
3147
+ ``[scale, shape]``; this distribution is unchanged.
3148
+ """
3149
+ return to_numpy_doubles(self.net.SolveMLE(to_net_doubles(sample)))
3150
+
3151
+
3152
+ def _dependency_type() -> Any:
3153
+ """Return the ``Probability.DependencyType`` enum type."""
3154
+ return namespace("Numerics.Data.Statistics").Probability.DependencyType
3155
+
3156
+
3157
+ @_register("CompetingRisks")
3158
+ class CompetingRisks(UnivariateDistribution):
3159
+ """The minimum (or maximum) of several random variables, the competing-risks failure time.
3160
+
3161
+ With independent components the CDF of the minimum is ``1 - prod(1 - F_i(x))``; ``dependency``
3162
+ and ``correlation_matrix`` select a dependent combination. The CDF is tabulated on a grid and
3163
+ interpolated (``x_transform``, ``probability_transform``).
3164
+
3165
+ Parameters
3166
+ ----------
3167
+ distributions : sequence of UnivariateDistribution
3168
+ The component distributions.
3169
+ """
3170
+
3171
+ def __init__(self, distributions: Sequence[UnivariateDistribution]) -> None:
3172
+ super().__init__(_nd().CompetingRisks(_members_array(distributions)))
3173
+
3174
+ def __repr__(self) -> str:
3175
+ return f"CompetingRisks({self.distributions!r})"
3176
+
3177
+ @property
3178
+ def distributions(self) -> list[UnivariateDistribution]:
3179
+ """The component distributions, wrapped (``Distributions``)."""
3180
+ return [wrap(component) for component in self.net.Distributions]
3181
+
3182
+ prng_seed = _net_property("PRNGSeed", "The seed of the random draws of dependent combinations (``PRNGSeed``); "
3183
+ "settable.", kind=int)
3184
+ minimum_of_random_variables = _net_property(
3185
+ "MinimumOfRandomVariables", "True for the minimum of the components (default), False for the maximum "
3186
+ "(``MinimumOfRandomVariables``); settable.", kind=bool)
3187
+ x_transform = _enum_property("XTransform", lambda: _data().Transform,
3188
+ "The X interpolation transform of the CDF table; settable.")
3189
+ probability_transform = _enum_property("ProbabilityTransform", lambda: _data().Transform,
3190
+ "The probability interpolation transform of the CDF table; settable.")
3191
+ dependency = _enum_property("Dependency", _dependency_type,
3192
+ "The dependency between components (``Probability.DependencyType`` name); settable.")
3193
+
3194
+ @property
3195
+ def correlation_matrix(self) -> np.ndarray | None:
3196
+ """The correlation matrix of the components for a correlated dependency; settable."""
3197
+ matrix = self.net.CorrelationMatrix
3198
+ return None if matrix is None else to_numpy_matrix(matrix)
3199
+
3200
+ @correlation_matrix.setter
3201
+ def correlation_matrix(self, value: Any) -> None:
3202
+ self._ensure_mutable()
3203
+ self.net.CorrelationMatrix = None if value is None else to_net_matrix(value)
3204
+
3205
+ def set_parameters(self, parameters: Any) -> None:
3206
+ """Replace the components (``SetParameters(UnivariateDistributionBase[])``) or set a parameter vector.
3207
+
3208
+ Parameters
3209
+ ----------
3210
+ parameters : sequence of UnivariateDistribution or array_like
3211
+ New component distributions, or the parameter vector in Numerics order.
3212
+
3213
+ Raises
3214
+ ------
3215
+ TypeError
3216
+ If the distribution is frozen.
3217
+ """
3218
+ self._ensure_mutable()
3219
+ items = list(parameters) if not isinstance(parameters, np.ndarray) else []
3220
+ if items and all(isinstance(item, UnivariateDistribution) for item in items):
3221
+ self.net.SetParameters(_members_array(items))
3222
+ else:
3223
+ super().set_parameters(parameters)
3224
+
3225
+ def create_empirical_cdf(self) -> None:
3226
+ """Build the tabulated CDF now rather than on first evaluation (``CreateEmpiricalCDF``)."""
3227
+ self.net.CreateEmpiricalCDF()
3228
+
3229
+ def cumulative_incidence_functions(self, bins: Any = None) -> list[Empirical]:
3230
+ """Return the cumulative incidence function of each component (``CumulativeIncidenceFunctions``).
3231
+
3232
+ Parameters
3233
+ ----------
3234
+ bins : pynumerics.sampling.StratificationBins, optional
3235
+ The X bins of the tables; None, the default, lets Numerics choose them.
3236
+
3237
+ Returns
3238
+ -------
3239
+ list of Empirical
3240
+ One table per component: the probability that it is the first to occur by x.
3241
+ """
3242
+ functions = self.net.CumulativeIncidenceFunctions(None if bins is None else _bins_net(bins))
3243
+ return [wrap(function) for function in functions]
3244
+
3245
+ def generate_random_values_with_dependency(self, sample_size: int, seed: int = -1) -> np.ndarray:
3246
+ """Draw the minimum (or maximum) of dependent component draws (``GenerateRandomValuesWithDependency``).
3247
+
3248
+ Parameters
3249
+ ----------
3250
+ sample_size : int
3251
+ Number of values.
3252
+ seed : int, optional
3253
+ Mersenne Twister seed; zero or negative seeds from the clock.
3254
+
3255
+ Returns
3256
+ -------
3257
+ numpy.ndarray
3258
+ The sample.
3259
+ """
3260
+ return to_numpy_doubles(self.net.GenerateRandomValuesWithDependency(int(sample_size), int(seed)))
3261
+
3262
+
3263
+ @_register("Mixture")
3264
+ class Mixture(UnivariateDistribution):
3265
+ """Finite mixture of univariate distributions.
3266
+
3267
+ Parameters
3268
+ ----------
3269
+ weights : array_like
3270
+ Component weights.
3271
+ distributions : sequence of UnivariateDistribution
3272
+ Components, one per weight.
3273
+ """
3274
+
3275
+ def __init__(self, weights: Any, distributions: Sequence[UnivariateDistribution]) -> None:
3276
+ super().__init__(_nd().Mixture(to_net_doubles(weights), _members_array(distributions)))
3277
+
3278
+ @property
3279
+ def weights(self) -> np.ndarray:
3280
+ """The component weights (``Weights``)."""
3281
+ return to_numpy_doubles(self.net.Weights)
3282
+
3283
+ @property
3284
+ def components(self) -> list[UnivariateDistribution]:
3285
+ """The component distributions, wrapped (``Distributions``)."""
3286
+ return [wrap(component) for component in self.net.Distributions]
3287
+
3288
+ is_zero_inflated = _net_property("IsZeroInflated", "Whether a point mass at zero with weight ``zero_weight`` "
3289
+ "joins the components (``IsZeroInflated``); settable.",
3290
+ kind=bool)
3291
+ zero_weight = _net_property("ZeroWeight", "The weight of the point mass at zero (``ZeroWeight``); settable.")
3292
+ max_iterations = _net_property("MaxIterations", "The iteration limit of the maximum likelihood fit "
3293
+ "(``MaxIterations``); settable.", kind=int)
3294
+ tolerance = _net_property("Tolerance", "The convergence tolerance of the maximum likelihood fit (``Tolerance``); "
3295
+ "settable.")
3296
+ iterations = _net_property("Iterations", "The iterations the last fit used (``Iterations``).", kind=int,
3297
+ settable=False)
3298
+ x_transform = _enum_property("XTransform", lambda: _data().Transform,
3299
+ "The X interpolation transform of the inverse-CDF table; settable.")
3300
+ probability_transform = _enum_property("ProbabilityTransform", lambda: _data().Transform,
3301
+ "The probability interpolation transform of the inverse-CDF table; "
3302
+ "settable.")
3303
+
3304
+ def create_empirical_cdf(self) -> None:
3305
+ """Build the tabulated CDF used by ``inverse_cdf`` now rather than on first use (``CreateEmpiricalCDF``)."""
3306
+ self.net.CreateEmpiricalCDF()
3307
+
3308
+
3309
+ @_register("EmpiricalDistribution", "Empirical")
3310
+ class Empirical(UnivariateDistribution):
3311
+ """Empirical distribution defined by value and probability tables.
3312
+
3313
+ With the default orders the probabilities are non-exceedance probabilities
3314
+ (strictly ascending). With ``probability_order="descending"`` the table is a
3315
+ survival function: the probabilities are exceedance probabilities S(x) (strictly
3316
+ descending), ``cdf`` returns 1 − S(x) and ``inverse_cdf(q)`` reads the exceedance
3317
+ axis at 1 − q. Interpolation is in (X, Φ⁻¹(P)) space by default
3318
+ (``probability_transform="normal_z"``) and holds the endpoints.
3319
+
3320
+ Parameters
3321
+ ----------
3322
+ values : array_like
3323
+ Ascending X values (ties allowed).
3324
+ probabilities : array_like
3325
+ Probability values in [0, 1], strictly monotone in ``probability_order``.
3326
+ x_order : SortOrder or str, optional
3327
+ Sort order of ``values``; must be ascending (the default).
3328
+ probability_order : SortOrder or str, optional
3329
+ ``"ascending"`` (non-exceedance, default) or ``"descending"`` (survival).
3330
+
3331
+ Notes
3332
+ -----
3333
+ Uses ``EmpiricalDistribution(IList, IList, SortOrder, SortOrder)``.
3334
+ """
3335
+
3336
+ def __init__(self, values: Any, probabilities: Any, x_order: enums.SortOrder | str = enums.SortOrder.ASCENDING,
3337
+ probability_order: enums.SortOrder | str = enums.SortOrder.ASCENDING) -> None:
3338
+ nd = _nd()
3339
+ data = _data()
3340
+ super().__init__(nd.EmpiricalDistribution(to_net_doubles(values), to_net_doubles(probabilities),
3341
+ net_enum(data.SortOrder, x_order),
3342
+ net_enum(data.SortOrder, probability_order)))
3343
+
3344
+ @classmethod
3345
+ def survival(cls, values: Any, exceedance: Any) -> Empirical:
3346
+ """Build a survival-orientation table from exceedance probabilities.
3347
+
3348
+ Parameters
3349
+ ----------
3350
+ values : array_like
3351
+ Ascending X values.
3352
+ exceedance : array_like
3353
+ Strictly descending exceedance probabilities S(x).
3354
+
3355
+ Returns
3356
+ -------
3357
+ Empirical
3358
+ The distribution with ``probability_order`` descending.
3359
+ """
3360
+ return cls(values, exceedance, enums.SortOrder.ASCENDING, enums.SortOrder.DESCENDING)
3361
+
3362
+ @classmethod
3363
+ def from_sample(cls, sample: Any,
3364
+ plotting_position: enums.PlottingPosition | str = enums.PlottingPosition.WEIBULL) -> Empirical:
3365
+ """Build the distribution of a sample with plotting-position probabilities.
3366
+
3367
+ Parameters
3368
+ ----------
3369
+ sample : array_like
3370
+ Observations; sorted by Numerics.
3371
+ plotting_position : PlottingPosition or str, optional
3372
+ Plotting-position formula; default Weibull.
3373
+
3374
+ Returns
3375
+ -------
3376
+ Empirical
3377
+ ``EmpiricalDistribution(IList, PlottingPostionType)``.
3378
+ """
3379
+ nd = _nd()
3380
+ stats = namespace("Numerics.Data.Statistics")
3381
+ kind = net_enum(stats.PlottingPositions.PlottingPostionType, plotting_position)
3382
+ instance = cls.__new__(cls)
3383
+ UnivariateDistribution.__init__(instance, nd.EmpiricalDistribution(to_net_doubles(sample), kind))
3384
+ return instance
3385
+
3386
+ @classmethod
3387
+ def from_paired_data(cls, paired_data: Any) -> Empirical:
3388
+ """Build the distribution from ordered paired data, keeping its orders and strictness.
3389
+
3390
+ Parameters
3391
+ ----------
3392
+ paired_data : pynumerics.data.OrderedPairedData or Numerics.Data.OrderedPairedData
3393
+ X ascending; Y the probabilities.
3394
+
3395
+ Returns
3396
+ -------
3397
+ Empirical
3398
+ ``EmpiricalDistribution(OrderedPairedData)``.
3399
+ """
3400
+ net = getattr(paired_data, "net", paired_data)
3401
+ instance = cls.__new__(cls)
3402
+ UnivariateDistribution.__init__(instance, _nd().EmpiricalDistribution(net))
3403
+ return instance
3404
+
3405
+ @property
3406
+ def x_values(self) -> np.ndarray:
3407
+ """The X table."""
3408
+ return to_numpy_doubles(self.net.XValues)
3409
+
3410
+ @property
3411
+ def probability_values(self) -> np.ndarray:
3412
+ """The probability table as stored (exceedance for a survival table)."""
3413
+ return to_numpy_doubles(self.net.ProbabilityValues)
3414
+
3415
+ @property
3416
+ def probability_order(self) -> str:
3417
+ """The probability sort order: ``"Ascending"``, ``"Descending"`` or ``"None"``."""
3418
+ return str(self.net.ProbabilityOrder)
3419
+
3420
+ @property
3421
+ def x_transform(self) -> str:
3422
+ """The X interpolation transform; settable with a :class:`~pynumerics.enums.Transform` or name."""
3423
+ return str(self.net.XTransform)
3424
+
3425
+ @x_transform.setter
3426
+ def x_transform(self, value: enums.Transform | str) -> None:
3427
+ self.net.XTransform = net_enum(_data().Transform, value)
3428
+
3429
+ @property
3430
+ def probability_transform(self) -> str:
3431
+ """The probability interpolation transform (default ``NormalZ``); settable."""
3432
+ return str(self.net.ProbabilityTransform)
3433
+
3434
+ @probability_transform.setter
3435
+ def probability_transform(self, value: enums.Transform | str) -> None:
3436
+ self.net.ProbabilityTransform = net_enum(_data().Transform, value)
3437
+
3438
+ @property
3439
+ def extrapolation(self) -> str:
3440
+ """The X-space extrapolation sides (default ``None``: endpoint hold); settable."""
3441
+ return str(self.net.Extrapolation)
3442
+
3443
+ @extrapolation.setter
3444
+ def extrapolation(self, value: enums.Extrapolation | str) -> None:
3445
+ self.net.Extrapolation = net_enum(_data().ExtrapolationSides, value)
3446
+
3447
+ @property
3448
+ def x_value_order(self) -> str:
3449
+ """The X sort order: ``"Ascending"`` (``XValueOrder``)."""
3450
+ return str(self.net.XValueOrder)
3451
+
3452
+ @property
3453
+ def strict_x_values(self) -> bool:
3454
+ """Whether the X values are strictly monotone, with no ties (``StrictXValues``)."""
3455
+ return bool(self.net.StrictXValues)
3456
+
3457
+ @property
3458
+ def strict_probabilities(self) -> bool:
3459
+ """Whether the probabilities are strictly monotone (``StrictProbabilities``)."""
3460
+ return bool(self.net.StrictProbabilities)
3461
+
3462
+ @classmethod
3463
+ def convolve(cls, distributions: Sequence[Empirical], number_of_points: int = 1024,
3464
+ log_spaced_output: bool = False) -> Empirical:
3465
+ """Return the distribution of the sum of independent empirical variables (``Convolve``).
3466
+
3467
+ Parameters
3468
+ ----------
3469
+ distributions : sequence of Empirical
3470
+ Two or more distributions; ``log_spaced_output`` needs exactly two.
3471
+ number_of_points : int, optional
3472
+ Points of the result table; default 1024.
3473
+ log_spaced_output : bool, optional
3474
+ Space the output X values logarithmically (the two-distribution overload); default False.
3475
+
3476
+ Returns
3477
+ -------
3478
+ Empirical
3479
+ The table of the sum.
3480
+ """
3481
+ empirical = _nd().EmpiricalDistribution
3482
+ items = list(distributions)
3483
+ if log_spaced_output:
3484
+ if len(items) != 2:
3485
+ raise ValueError("log_spaced_output needs exactly two distributions.")
3486
+ result = empirical.Convolve(items[0].net, items[1].net, int(number_of_points), True)
3487
+ elif len(items) == 2:
3488
+ result = empirical.Convolve(items[0].net, items[1].net, int(number_of_points))
3489
+ else:
3490
+ from System.Collections.Generic import List # noqa: PLC0415 - the runtime is running
3491
+
3492
+ members = List[empirical]()
3493
+ for item in items:
3494
+ members.Add(item.net)
3495
+ result = empirical.Convolve(members, int(number_of_points))
3496
+ return wrap(result)
3497
+
3498
+ @staticmethod
3499
+ def convolve_discrete(values1: Any, masses1: Any, values2: Any, masses2: Any,
3500
+ lattice_points: int) -> tuple[np.ndarray, np.ndarray]:
3501
+ """Convolve two discrete distributions on a common lattice (``ConvolveDiscrete``).
3502
+
3503
+ Parameters
3504
+ ----------
3505
+ values1, masses1 : array_like
3506
+ Support points and probability masses of the first variable.
3507
+ values2, masses2 : array_like
3508
+ Support points and probability masses of the second variable.
3509
+ lattice_points : int
3510
+ Number of lattice points of the result.
3511
+
3512
+ Returns
3513
+ -------
3514
+ values : numpy.ndarray
3515
+ The lattice of the sum.
3516
+ masses : numpy.ndarray
3517
+ The probability masses on the lattice.
3518
+ """
3519
+ values, masses = _nd().EmpiricalDistribution.ConvolveDiscrete(
3520
+ to_net_doubles(values1), to_net_doubles(masses1), to_net_doubles(values2), to_net_doubles(masses2),
3521
+ int(lattice_points), None, None)
3522
+ return to_numpy_doubles(values), to_numpy_doubles(masses)
3523
+
3524
+
3525
+ @_register("KernelDensity")
3526
+ class KernelDensity(UnivariateDistribution):
3527
+ """Kernel density estimate of a sample (``Numerics.Distributions.KernelDensity``).
3528
+
3529
+ The density is ``f(x) = sum_i w_i K((x - x_i) / h) / (h sum_i w_i)`` (unit weights when none
3530
+ are given). The CDF and inverse CDF interpolate a 1000-bin cumulative table over the support,
3531
+ which is the sample range when ``bounded_by_data`` is True (the default) and the range widened
3532
+ by ``3 h`` on each side otherwise.
3533
+
3534
+ Parameters
3535
+ ----------
3536
+ sample : array_like
3537
+ Finite sample values, in any order.
3538
+ kernel : KernelType or str, optional
3539
+ The kernel; default Gaussian.
3540
+ bandwidth : float, optional
3541
+ The bandwidth ``h`` (positive). None, the default, applies the Numerics rule of thumb
3542
+ ``(4 / (3 n))^(1/5) s``: ``s`` is the sample standard deviation (divisor ``n - 1``), or
3543
+ with weights the weighted standard deviation with divisor ``sum_i w_i``.
3544
+ weights : array_like, optional
3545
+ Non-negative weights, one per value, with a positive total.
3546
+
3547
+ Notes
3548
+ -----
3549
+ Uses ``KernelDensity(IList, KernelType[, double])``, or
3550
+ ``KernelDensity(IList, IList, KernelType, double?)`` with weights.
3551
+ """
3552
+
3553
+ def __init__(self, sample: Any, kernel: enums.KernelType | str = enums.KernelType.GAUSSIAN,
3554
+ bandwidth: float | None = None, weights: Any = None) -> None:
3555
+ kd = _nd().KernelDensity
3556
+ kind, values = net_enum(kd.KernelType, kernel), to_net_doubles(sample)
3557
+ if weights is not None:
3558
+ net = kd(values, to_net_doubles(weights), kind, None if bandwidth is None else float(bandwidth))
3559
+ elif bandwidth is None:
3560
+ net = kd(values, kind)
3561
+ else:
3562
+ net = kd(values, kind, float(bandwidth))
3563
+ super().__init__(net)
3564
+
3565
+ def __repr__(self) -> str:
3566
+ return f"KernelDensity(kernel={self.kernel!r}, bandwidth={self.bandwidth!r}, sample_size={self.sample_size})"
3567
+
3568
+ @property
3569
+ def sample_data(self) -> np.ndarray:
3570
+ """The sample values, in input order."""
3571
+ return to_numpy_doubles(self.net.SampleData)
3572
+
3573
+ @property
3574
+ def sample_size(self) -> int:
3575
+ """The number of sample values."""
3576
+ return int(self.net.SampleSize)
3577
+
3578
+ @property
3579
+ def kernel(self) -> str:
3580
+ """The kernel name (``KernelDistribution``); settable with a :class:`~pynumerics.enums.KernelType` or name."""
3581
+ return str(self.net.KernelDistribution)
3582
+
3583
+ @kernel.setter
3584
+ def kernel(self, value: enums.KernelType | str) -> None:
3585
+ self.net.KernelDistribution = net_enum(_nd().KernelDensity.KernelType, value)
3586
+
3587
+ @property
3588
+ def bandwidth(self) -> float:
3589
+ """The bandwidth ``h``; settable with a positive finite value."""
3590
+ return float(self.net.Bandwidth)
3591
+
3592
+ @bandwidth.setter
3593
+ def bandwidth(self, value: float) -> None:
3594
+ self.net.Bandwidth = float(value)
3595
+
3596
+ @property
3597
+ def bounded_by_data(self) -> bool:
3598
+ """Whether the support is the sample range (default) rather than the range widened by ``3 h``; settable."""
3599
+ return bool(self.net.BoundedByData)
3600
+
3601
+ @bounded_by_data.setter
3602
+ def bounded_by_data(self, value: bool) -> None:
3603
+ self.net.BoundedByData = bool(value)
3604
+
3605
+ @property
3606
+ def x_transform(self) -> str:
3607
+ """The X interpolation transform of the CDF table (default ``None``); settable."""
3608
+ return str(self.net.XTransform)
3609
+
3610
+ @x_transform.setter
3611
+ def x_transform(self, value: enums.Transform | str) -> None:
3612
+ self.net.XTransform = net_enum(_data().Transform, value)
3613
+
3614
+ @property
3615
+ def probability_transform(self) -> str:
3616
+ """The probability interpolation transform of the CDF table (default ``NormalZ``); settable."""
3617
+ return str(self.net.ProbabilityTransform)
3618
+
3619
+ @probability_transform.setter
3620
+ def probability_transform(self, value: enums.Transform | str) -> None:
3621
+ self.net.ProbabilityTransform = net_enum(_data().Transform, value)
3622
+
3623
+ def bandwidth_rule(self, sample: Any, weights: Any = None) -> float:
3624
+ """Return the rule-of-thumb bandwidth of a sample (``BandwidthRule``).
3625
+
3626
+ Parameters
3627
+ ----------
3628
+ sample : array_like
3629
+ Finite sample values.
3630
+ weights : array_like, optional
3631
+ Weights, one per value; the weighted standard deviation is used.
3632
+
3633
+ Returns
3634
+ -------
3635
+ float
3636
+ ``(4 / (3 n))^(1/5)`` times the standard deviation (as for the ``bandwidth`` default),
3637
+ kept finite and positive for a constant sample.
3638
+ """
3639
+ values = to_net_doubles(sample)
3640
+ if weights is None:
3641
+ return float(self.net.BandwidthRule(values))
3642
+ return float(self.net.BandwidthRule(values, to_net_doubles(weights)))
3643
+
3644
+ def set_sample_data(self, sample: Any, weights: Any = None) -> None:
3645
+ """Replace the sample (and weights) in place, keeping the kernel and bandwidth (``SetSampleData``).
3646
+
3647
+ Parameters
3648
+ ----------
3649
+ sample : array_like
3650
+ Finite sample values.
3651
+ weights : array_like, optional
3652
+ Non-negative weights, one per value; None, the default, gives unit weights.
3653
+
3654
+ Raises
3655
+ ------
3656
+ TypeError
3657
+ If the distribution is frozen.
3658
+ """
3659
+ self._ensure_mutable()
3660
+ if weights is None:
3661
+ self.net.SetSampleData(to_net_doubles(sample))
3662
+ else:
3663
+ self.net.SetSampleData(to_net_doubles(sample), to_net_doubles(weights))
3664
+
3665
+ degenerate_relative_bandwidth = _FamilyAttribute(
3666
+ lambda cls: float(_nd().KernelDensity.DegenerateRelativeBandwidth),
3667
+ lambda self: float(_nd().KernelDensity.DegenerateRelativeBandwidth),
3668
+ """The bandwidth of a constant sample relative to its magnitude (``DegenerateRelativeBandwidth``).""")
3669
+
3670
+ degenerate_absolute_bandwidth = _FamilyAttribute(
3671
+ lambda cls: float(_nd().KernelDensity.DegenerateAbsoluteBandwidth),
3672
+ lambda self: float(_nd().KernelDensity.DegenerateAbsoluteBandwidth),
3673
+ """The bandwidth of a constant sample at zero (``DegenerateAbsoluteBandwidth``).""")
3674
+
3675
+
3676
+ def create_distribution(type_name: str, parameters: Any = None) -> UnivariateDistribution:
3677
+ """Create a distribution by type name through ``UnivariateDistributionFactory``.
3678
+
3679
+ ``CreateDistribution(UnivariateDistributionType)`` builds the family at its default
3680
+ parameters; ``parameters``, when given, are then set through ``SetParameters``.
3681
+ Invalid parameters are recorded, not refused (``parameters_valid`` is False).
3682
+
3683
+ Parameters
3684
+ ----------
3685
+ type_name : str
3686
+ A ``UnivariateDistributionType`` member name, for example ``"Gumbel"``; matched
3687
+ case-insensitively with underscores ignored.
3688
+ parameters : array_like, optional
3689
+ The parameters in Numerics order.
3690
+
3691
+ Returns
3692
+ -------
3693
+ UnivariateDistribution
3694
+ The wrapped distribution (its specific wrapper class when one exists).
3695
+
3696
+ Raises
3697
+ ------
3698
+ ValueError
3699
+ If ``type_name`` is not a member of ``UnivariateDistributionType``, or ``parameters`` does
3700
+ not hold the family's number of parameters (the message names the expected count).
3701
+ """
3702
+ nd = _nd()
3703
+ net = unwrap(nd.UnivariateDistributionFactory.CreateDistribution(
3704
+ net_enum(nd.UnivariateDistributionType, type_name)))
3705
+ if parameters is not None:
3706
+ net.SetParameters(to_net_doubles(_check_arity(net, parameters)))
3707
+ return wrap(net)
3708
+
3709
+
3710
+ def standard_z(probability: Any) -> Any:
3711
+ """Return standard normal quantiles through ``Normal.StandardZ``.
3712
+
3713
+ Parameters
3714
+ ----------
3715
+ probability : float or array_like
3716
+ Non-exceedance probabilities; 0 and 1 map to ±``double.Epsilon`` probabilities
3717
+ as in Numerics.
3718
+
3719
+ Returns
3720
+ -------
3721
+ float or numpy.ndarray
3722
+ Standard normal deviates, shaped like ``probability``.
3723
+ """
3724
+ n = _nd().Normal
3725
+ return evaluate(probability, _static("StandardZ", n), _static_list("StandardZ", n))
3726
+
3727
+
3728
+ def standard_cdf(z: Any) -> Any:
3729
+ """Return the standard normal CDF through ``Normal.StandardCDF``.
3730
+
3731
+ Parameters
3732
+ ----------
3733
+ z : float or array_like
3734
+ Standard normal deviates.
3735
+
3736
+ Returns
3737
+ -------
3738
+ float or numpy.ndarray
3739
+ Non-exceedance probabilities, shaped like ``z``.
3740
+ """
3741
+ n = _nd().Normal
3742
+ return evaluate(z, _static("StandardCDF", n), _static_list("StandardCDF", n))
3743
+
3744
+
3745
+ _STATIC_BOUND: dict[str, Any] = {}
3746
+
3747
+
3748
+ def _static(name: str, owner: Any) -> Any:
3749
+ """Return a static member's ``double`` overload, bound once per process (see ``_util.bind``)."""
3750
+ member = _STATIC_BOUND.get(name)
3751
+ if member is None:
3752
+ member = _STATIC_BOUND[name] = bind(getattr(owner, name))
3753
+ return member
3754
+
3755
+
3756
+ _STATIC_LIST_BOUND: dict[str, Any] = {}
3757
+
3758
+
3759
+ def _static_list(name: str, owner: Any) -> Any:
3760
+ """Return a static member's ``IList<double>`` overload, bound once per process (see ``_util.bind_list``)."""
3761
+ member = _STATIC_LIST_BOUND.get(name)
3762
+ if member is None:
3763
+ member = _STATIC_LIST_BOUND[name] = bind_list(getattr(owner, name))
3764
+ return member
3765
+
3766
+
3767
+ def standard_pdf(z: Any) -> Any:
3768
+ """Return the standard normal density through ``Normal.StandardPDF``.
3769
+
3770
+ Parameters
3771
+ ----------
3772
+ z : float or array_like
3773
+ Standard normal deviates.
3774
+
3775
+ Returns
3776
+ -------
3777
+ float or numpy.ndarray
3778
+ Densities, shaped like ``z``.
3779
+ """
3780
+ n = _nd().Normal
3781
+ return evaluate(z, _static("StandardPDF", n), _static_list("StandardPDF", n))
3782
+
3783
+
3784
+ def try_create_distribution(type_name: str) -> UnivariateDistribution | None:
3785
+ """Create a family at its default parameters, or return None when Numerics cannot (``TryCreateDistribution``).
3786
+
3787
+ Parameters
3788
+ ----------
3789
+ type_name : str
3790
+ A ``UnivariateDistributionType`` member name, matched case-insensitively with underscores ignored.
3791
+
3792
+ Returns
3793
+ -------
3794
+ UnivariateDistribution or None
3795
+ The wrapped distribution; None for the families that need components (``Mixture``,
3796
+ ``CompetingRisks``) and ``UserDefined``.
3797
+
3798
+ Raises
3799
+ ------
3800
+ ValueError
3801
+ If ``type_name`` is not a member of ``UnivariateDistributionType``.
3802
+ """
3803
+ nd = _nd()
3804
+ created, net = nd.UnivariateDistributionFactory.TryCreateDistribution(
3805
+ net_enum(nd.UnivariateDistributionType, type_name), None)
3806
+ return wrap(net) if created else None
3807
+
3808
+
3809
+ def from_xml(xml: str) -> UnivariateDistribution:
3810
+ """Rebuild a distribution from the XML :meth:`UnivariateDistribution.to_xml` writes.
3811
+
3812
+ Calls ``UnivariateDistributionFactory.CreateDistribution(XElement)``, which dispatches to the
3813
+ family's own ``FromXElement`` for mixtures, competing risks, percentile PERT, empirical and
3814
+ kernel density tables.
3815
+
3816
+ Parameters
3817
+ ----------
3818
+ xml : str
3819
+ The ``Distribution`` element as text.
3820
+
3821
+ Returns
3822
+ -------
3823
+ UnivariateDistribution
3824
+ The wrapped distribution.
3825
+
3826
+ Raises
3827
+ ------
3828
+ ValueError
3829
+ If the type or a parameter is missing or invalid.
3830
+ """
3831
+ return wrap(_nd().UnivariateDistributionFactory.CreateDistribution(_parse_xml(xml)))
3832
+
3833
+
3834
+ def standard_log_pdf(z: Any) -> Any:
3835
+ """Return the log of the standard normal density through ``Normal.StandardLogPDF``.
3836
+
3837
+ Parameters
3838
+ ----------
3839
+ z : float or array_like
3840
+ Standard normal deviates.
3841
+
3842
+ Returns
3843
+ -------
3844
+ float or numpy.ndarray
3845
+ Log densities, shaped like ``z``; Numerics has no array overload, so an array is evaluated
3846
+ element by element with the bound scalar member.
3847
+ """
3848
+ return loop(z, _static("StandardLogPDF", _nd().Normal))
3849
+
3850
+
3851
+ def _elementwise(*arrays: Any) -> tuple[tuple[int, ...], list[np.ndarray]]:
3852
+ """Broadcast the per-element arguments of a static array member and flatten them."""
3853
+ broadcast = np.broadcast_arrays(*(np.asarray(a) for a in arrays))
3854
+ shape = broadcast[0].shape
3855
+ return shape, [np.ascontiguousarray(b).ravel() for b in broadcast]
3856
+
3857
+
3858
+ def _shaped(values: Any, shape: tuple[int, ...]) -> Any:
3859
+ """Return a .NET result array as a float for a scalar shape, otherwise as an array of ``shape``."""
3860
+ out = to_numpy_doubles(values)
3861
+ return float(out[0]) if shape == () else out.reshape(shape)
3862
+
3863
+
3864
+ def _poisson_static(name: str, rate: Any, values: Any) -> Any:
3865
+ """Call the per-element static ``Poisson.<name>(double[], double[])`` on broadcast arguments."""
3866
+ shape, (rates, points) = _elementwise(np.asarray(rate, dtype=np.float64), np.asarray(values, dtype=np.float64))
3867
+
3868
+ def factory() -> Any:
3869
+ poisson = _nd().Poisson # starts the runtime before System is imported
3870
+ import System # noqa: PLC0415 - the runtime is running
3871
+
3872
+ doubles = System.Array[System.Double]
3873
+ return getattr(poisson, name).Overloads[doubles, doubles]
3874
+
3875
+ member = cached_overload(f"Poisson.{name}(double[], double[])", factory)
3876
+ return _shaped(member(to_net_doubles(rates), to_net_doubles(points)), shape)
3877
+
3878
+
3879
+ def poisson_inverse_cdf(rate: Any, probability: Any) -> Any:
3880
+ """Return Poisson quantiles with one rate per probability (``Poisson.InverseCDF(double[], double[])``).
3881
+
3882
+ Parameters
3883
+ ----------
3884
+ rate : float or array_like
3885
+ Rates λ > 0, broadcast against ``probability``.
3886
+ probability : float or array_like
3887
+ Non-exceedance probabilities in [0, 1].
3888
+
3889
+ Returns
3890
+ -------
3891
+ float or numpy.ndarray
3892
+ The smallest integer k with P(X <= k) >= p for each element, shaped like the broadcast
3893
+ arguments; probability 0 gives 0 and 1 gives infinity. Each value is bitwise equal to
3894
+ ``Poisson(rate).inverse_cdf(probability)``.
3895
+
3896
+ Raises
3897
+ ------
3898
+ ValueError
3899
+ If a rate is not positive and finite, a probability is outside [0, 1], or the arguments
3900
+ do not broadcast.
3901
+ """
3902
+ return _poisson_static("InverseCDF", rate, probability)
3903
+
3904
+
3905
+ def poisson_log_pmf(rate: Any, k: Any) -> Any:
3906
+ """Return Poisson log probability masses with one rate per count (``Poisson.LogPDF(double[], double[])``).
3907
+
3908
+ Parameters
3909
+ ----------
3910
+ rate : float or array_like
3911
+ Rates λ > 0, broadcast against ``k``.
3912
+ k : float or array_like
3913
+ Counts; a value that is not a nonnegative integer has log mass ``-inf``.
3914
+
3915
+ Returns
3916
+ -------
3917
+ float or numpy.ndarray
3918
+ The log masses, shaped like the broadcast arguments, each bitwise equal to
3919
+ ``Poisson(rate).log_pdf(k)``.
3920
+
3921
+ Raises
3922
+ ------
3923
+ ValueError
3924
+ If a rate is not positive and finite, or the arguments do not broadcast.
3925
+ """
3926
+ return _poisson_static("LogPDF", rate, k)
3927
+
3928
+
3929
+ def poisson_cdf(rate: Any, k: Any) -> Any:
3930
+ """Return Poisson cumulative probabilities with one rate per count (``Poisson.CDF(double[], double[])``).
3931
+
3932
+ Parameters
3933
+ ----------
3934
+ rate : float or array_like
3935
+ Rates λ > 0, broadcast against ``k``.
3936
+ k : float or array_like
3937
+ Counts, floored when not integers.
3938
+
3939
+ Returns
3940
+ -------
3941
+ float or numpy.ndarray
3942
+ P(X <= k), shaped like the broadcast arguments, each bitwise equal to ``Poisson(rate).cdf(k)``.
3943
+
3944
+ Raises
3945
+ ------
3946
+ ValueError
3947
+ If a rate is not positive and finite, or the arguments do not broadcast.
3948
+ """
3949
+ return _poisson_static("CDF", rate, k)
3950
+
3951
+
3952
+ def _binomial_static(name: str, probability: Any, number_of_trials: Any, k: Any) -> Any:
3953
+ """Call the per-element static ``Binomial.<name>(double[], int[], double[])`` on broadcast arguments."""
3954
+ trials = np.asarray(number_of_trials)
3955
+ if trials.dtype.kind not in "iuf" or (trials.dtype.kind == "f" and not np.array_equal(trials, np.trunc(trials))):
3956
+ raise ValueError("The number of trials must be an integer.")
3957
+ if trials.size and (trials.min() < np.iinfo(np.int32).min or trials.max() > np.iinfo(np.int32).max):
3958
+ raise ValueError("The number of trials must fit in a 32-bit integer.")
3959
+ shape, (p, n, points) = _elementwise(np.asarray(probability, dtype=np.float64), trials,
3960
+ np.asarray(k, dtype=np.float64))
3961
+
3962
+ def factory() -> Any:
3963
+ binomial = _nd().Binomial # starts the runtime before System is imported
3964
+ import System # noqa: PLC0415 - the runtime is running
3965
+
3966
+ doubles = System.Array[System.Double]
3967
+ return getattr(binomial, name).Overloads[doubles, System.Array[System.Int32], doubles]
3968
+
3969
+ member = cached_overload(f"Binomial.{name}(double[], int[], double[])", factory)
3970
+ return _shaped(member(to_net_doubles(p), to_net_ints(n), to_net_doubles(points)), shape)
3971
+
3972
+
3973
+ def binomial_log_pmf(probability: Any, number_of_trials: Any, k: Any) -> Any:
3974
+ """Return binomial log probability masses with one parameter set per count (``Binomial.LogPDF``).
3975
+
3976
+ Calls ``Binomial.LogPDF(double[], int[], double[])``.
3977
+
3978
+ Parameters
3979
+ ----------
3980
+ probability : float or array_like
3981
+ Success probabilities in [0, 1], broadcast against the other arguments.
3982
+ number_of_trials : int or array_like
3983
+ Numbers of trials n >= 1.
3984
+ k : float or array_like
3985
+ Numbers of successes; a value that is not an integer in [0, n] has log mass ``-inf``.
3986
+
3987
+ Returns
3988
+ -------
3989
+ float or numpy.ndarray
3990
+ The log masses, shaped like the broadcast arguments, each bitwise equal to
3991
+ ``Binomial(probability, number_of_trials).log_pdf(k)``.
3992
+
3993
+ Raises
3994
+ ------
3995
+ ValueError
3996
+ If a probability is outside [0, 1], a number of trials is not a positive integer that fits
3997
+ in 32 bits (booleans, text and fractional values are refused), or the arguments do not broadcast.
3998
+ """
3999
+ return _binomial_static("LogPDF", probability, number_of_trials, k)
4000
+
4001
+
4002
+ def binomial_cdf(probability: Any, number_of_trials: Any, k: Any) -> Any:
4003
+ """Return binomial cumulative probabilities with one parameter set per count (``Binomial.CDF``).
4004
+
4005
+ Calls ``Binomial.CDF(double[], int[], double[])``.
4006
+
4007
+ Parameters
4008
+ ----------
4009
+ probability : float or array_like
4010
+ Success probabilities in [0, 1], broadcast against the other arguments.
4011
+ number_of_trials : int or array_like
4012
+ Numbers of trials n >= 1.
4013
+ k : float or array_like
4014
+ Numbers of successes, floored when not integers.
4015
+
4016
+ Returns
4017
+ -------
4018
+ float or numpy.ndarray
4019
+ P(X <= k), shaped like the broadcast arguments, each bitwise equal to
4020
+ ``Binomial(probability, number_of_trials).cdf(k)``.
4021
+
4022
+ Raises
4023
+ ------
4024
+ ValueError
4025
+ If a probability is outside [0, 1], a number of trials is not a positive integer that fits
4026
+ in 32 bits (booleans, text and fractional values are refused), or the arguments do not broadcast.
4027
+ """
4028
+ return _binomial_static("CDF", probability, number_of_trials, k)
4029
+
4030
+
4031
+ __all__ = [
4032
+ "FAMILIES", "FrozenDistribution", "Bernoulli", "Beta", "Binomial", "Cauchy", "ChiSquared", "CompetingRisks",
4033
+ "Deterministic", "Empirical", "Exponential", "GammaDistribution", "GeneralizedBeta", "GeneralizedExtremeValue",
4034
+ "GeneralizedLogistic", "GeneralizedNormal", "GeneralizedPareto", "Geometric", "Gumbel", "InverseChiSquared",
4035
+ "InverseGamma", "KappaFour", "KernelDensity", "LnNormal", "LogNormal", "LogPearsonTypeIII", "Logistic", "Mixture",
4036
+ "NoncentralT", "Normal", "Pareto", "PearsonTypeIII", "Pert", "PertPercentile", "PertPercentileZ", "Poisson",
4037
+ "Rayleigh", "StudentT", "Triangular", "TruncatedDistribution", "TruncatedNormal", "Uniform", "UniformDiscrete",
4038
+ "UnivariateDistribution", "VonMises", "Weibull", "binomial_cdf", "binomial_log_pmf", "create_distribution",
4039
+ "from_xml", "poisson_cdf", "poisson_inverse_cdf", "poisson_log_pmf", "standard_cdf", "standard_log_pdf",
4040
+ "standard_pdf", "standard_z", "try_create_distribution", "wrap",
4041
+ ]
4042
+
4043
+ translate_public(globals())