flopscope 0.2.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 (115) hide show
  1. benchmarks/__init__.py +1 -0
  2. benchmarks/__main__.py +6 -0
  3. benchmarks/_baseline.py +171 -0
  4. benchmarks/_bitwise.py +231 -0
  5. benchmarks/_complex.py +176 -0
  6. benchmarks/_contractions.py +291 -0
  7. benchmarks/_fft.py +198 -0
  8. benchmarks/_impl_urls.py +139 -0
  9. benchmarks/_linalg.py +197 -0
  10. benchmarks/_linalg_delegates.py +407 -0
  11. benchmarks/_metadata.py +141 -0
  12. benchmarks/_misc.py +653 -0
  13. benchmarks/_perf.py +321 -0
  14. benchmarks/_perm_group_calibration.py +175 -0
  15. benchmarks/_pointwise.py +372 -0
  16. benchmarks/_polynomial.py +193 -0
  17. benchmarks/_random.py +209 -0
  18. benchmarks/_reductions.py +136 -0
  19. benchmarks/_sorting.py +289 -0
  20. benchmarks/_stats.py +137 -0
  21. benchmarks/_window.py +92 -0
  22. benchmarks/accumulation/__init__.py +0 -0
  23. benchmarks/accumulation/bench_cost_compute.py +138 -0
  24. benchmarks/dashboard.py +312 -0
  25. benchmarks/runner.py +636 -0
  26. flopscope/__init__.py +273 -0
  27. flopscope/_accumulation/__init__.py +13 -0
  28. flopscope/_accumulation/_bipartite.py +121 -0
  29. flopscope/_accumulation/_burnside.py +51 -0
  30. flopscope/_accumulation/_cache.py +146 -0
  31. flopscope/_accumulation/_components.py +153 -0
  32. flopscope/_accumulation/_cost.py +1414 -0
  33. flopscope/_accumulation/_cost_descriptions.py +63 -0
  34. flopscope/_accumulation/_detection.py +318 -0
  35. flopscope/_accumulation/_ladder.py +191 -0
  36. flopscope/_accumulation/_output_orbit.py +104 -0
  37. flopscope/_accumulation/_partition.py +290 -0
  38. flopscope/_accumulation/_path_info.py +211 -0
  39. flopscope/_accumulation/_public.py +169 -0
  40. flopscope/_accumulation/_reduction.py +310 -0
  41. flopscope/_accumulation/_regimes.py +303 -0
  42. flopscope/_accumulation/_shape.py +33 -0
  43. flopscope/_accumulation/_wreath.py +209 -0
  44. flopscope/_budget.py +1027 -0
  45. flopscope/_config.py +118 -0
  46. flopscope/_counting_ops.py +451 -0
  47. flopscope/_display.py +478 -0
  48. flopscope/_docstrings.py +59 -0
  49. flopscope/_dtypes.py +20 -0
  50. flopscope/_einsum.py +717 -0
  51. flopscope/_errstate.py +25 -0
  52. flopscope/_flops.py +282 -0
  53. flopscope/_free_ops.py +2654 -0
  54. flopscope/_ndarray.py +1126 -0
  55. flopscope/_opt_einsum/LICENSE +21 -0
  56. flopscope/_opt_einsum/NOTICE +59 -0
  57. flopscope/_opt_einsum/__init__.py +209 -0
  58. flopscope/_opt_einsum/_contract.py +1478 -0
  59. flopscope/_opt_einsum/_helpers.py +164 -0
  60. flopscope/_opt_einsum/_hsluv.py +273 -0
  61. flopscope/_opt_einsum/_path_random.py +462 -0
  62. flopscope/_opt_einsum/_paths.py +1653 -0
  63. flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
  64. flopscope/_opt_einsum/_symmetry.py +140 -0
  65. flopscope/_opt_einsum/_typing.py +37 -0
  66. flopscope/_perm_group.py +717 -0
  67. flopscope/_pointwise.py +2522 -0
  68. flopscope/_polynomial.py +278 -0
  69. flopscope/_registry.py +3216 -0
  70. flopscope/_sorting_ops.py +571 -0
  71. flopscope/_symmetric.py +812 -0
  72. flopscope/_symmetry_transport.py +510 -0
  73. flopscope/_symmetry_utils.py +669 -0
  74. flopscope/_type_info.py +12 -0
  75. flopscope/_unwrap.py +70 -0
  76. flopscope/_validation.py +83 -0
  77. flopscope/_version_check.py +46 -0
  78. flopscope/_weights.py +195 -0
  79. flopscope/_window.py +177 -0
  80. flopscope/accounting.py +565 -0
  81. flopscope/data/default_weights.json +462 -0
  82. flopscope/data/weights.csv +509 -0
  83. flopscope/errors.py +197 -0
  84. flopscope/numpy/__init__.py +878 -0
  85. flopscope/numpy/fft/__init__.py +55 -0
  86. flopscope/numpy/fft/_free.py +51 -0
  87. flopscope/numpy/fft/_transforms.py +695 -0
  88. flopscope/numpy/linalg/__init__.py +105 -0
  89. flopscope/numpy/linalg/_aliases.py +126 -0
  90. flopscope/numpy/linalg/_compound.py +161 -0
  91. flopscope/numpy/linalg/_decompositions.py +353 -0
  92. flopscope/numpy/linalg/_properties.py +533 -0
  93. flopscope/numpy/linalg/_solvers.py +444 -0
  94. flopscope/numpy/linalg/_svd.py +122 -0
  95. flopscope/numpy/random/__init__.py +684 -0
  96. flopscope/numpy/random/_cost_formulas.py +115 -0
  97. flopscope/numpy/random/_counted_classes.py +241 -0
  98. flopscope/numpy/testing/__init__.py +13 -0
  99. flopscope/numpy/typing/__init__.py +30 -0
  100. flopscope/py.typed +0 -0
  101. flopscope/stats/__init__.py +84 -0
  102. flopscope/stats/_base.py +77 -0
  103. flopscope/stats/_cauchy.py +146 -0
  104. flopscope/stats/_erf.py +190 -0
  105. flopscope/stats/_expon.py +146 -0
  106. flopscope/stats/_laplace.py +150 -0
  107. flopscope/stats/_logistic.py +148 -0
  108. flopscope/stats/_lognorm.py +160 -0
  109. flopscope/stats/_ndtri.py +133 -0
  110. flopscope/stats/_norm.py +149 -0
  111. flopscope/stats/_truncnorm.py +186 -0
  112. flopscope/stats/_uniform.py +141 -0
  113. flopscope-0.2.0.dist-info/METADATA +23 -0
  114. flopscope-0.2.0.dist-info/RECORD +115 -0
  115. flopscope-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,148 @@
1
+ """Logistic distribution for :mod:`flopscope.stats`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as _np
6
+
7
+ from flopscope.stats._base import ContinuousDistribution
8
+
9
+
10
+ class LogisticDistribution(ContinuousDistribution):
11
+ """Logistic continuous random variable.
12
+
13
+ This object mirrors ``scipy.stats.logistic``.
14
+
15
+ Methods
16
+ -------
17
+ pdf(x, loc=0, scale=1)
18
+ Evaluate the probability density function.
19
+ cdf(x, loc=0, scale=1)
20
+ Evaluate the cumulative distribution function.
21
+ ppf(q, loc=0, scale=1)
22
+ Evaluate the percent-point function.
23
+
24
+ Notes
25
+ -----
26
+ ``loc`` is the center of the distribution and ``scale`` controls the
27
+ spread. The CDF is the sigmoid function and the PPF is the logit
28
+ function. Each public method deducts ``1 * numel(input)`` FLOPs from the
29
+ active budget.
30
+ """
31
+
32
+ def __init__(self):
33
+ super().__init__("logistic")
34
+
35
+ def pdf(self, x, loc=0, scale=1):
36
+ """Evaluate the probability density function.
37
+
38
+ Parameters
39
+ ----------
40
+ x : array_like
41
+ Points at which to evaluate the density.
42
+ loc : float, optional
43
+ Location parameter of the distribution. Defaults to ``0``.
44
+ scale : float, optional
45
+ Scale parameter of the distribution. Defaults to ``1``.
46
+
47
+ Returns
48
+ -------
49
+ FlopscopeArray
50
+ Probability density evaluated elementwise at ``x``.
51
+
52
+ Notes
53
+ -----
54
+ Equivalent to ``scipy.stats.logistic.pdf(x, loc, scale)``.
55
+ FLOP cost: ``1 * numel(x)``.
56
+
57
+ Examples
58
+ --------
59
+ >>> import numpy as np
60
+ >>> import flopscope as flops
61
+ >>> x = np.array([-1.0, 0.0, 1.0])
62
+ >>> np.round(flops.stats.logistic.pdf(x), 3)
63
+ array([0.197, 0.25 , 0.197])
64
+ """
65
+ return self._deduct_and_call("pdf", 1, x, loc=loc, scale=scale)
66
+
67
+ def cdf(self, x, loc=0, scale=1):
68
+ """Evaluate the cumulative distribution function.
69
+
70
+ Parameters
71
+ ----------
72
+ x : array_like
73
+ Points at which to evaluate the cumulative probability.
74
+ loc : float, optional
75
+ Location parameter of the distribution. Defaults to ``0``.
76
+ scale : float, optional
77
+ Scale parameter of the distribution. Defaults to ``1``.
78
+
79
+ Returns
80
+ -------
81
+ FlopscopeArray
82
+ Cumulative probability evaluated elementwise at ``x``.
83
+
84
+ Notes
85
+ -----
86
+ Equivalent to ``scipy.stats.logistic.cdf(x, loc, scale)``.
87
+ FLOP cost: ``1 * numel(x)``.
88
+
89
+ Examples
90
+ --------
91
+ >>> import numpy as np
92
+ >>> import flopscope as flops
93
+ >>> x = np.array([-1.0, 0.0, 1.0])
94
+ >>> np.round(flops.stats.logistic.cdf(x), 3)
95
+ array([0.269, 0.5 , 0.731])
96
+ """
97
+ return self._deduct_and_call("cdf", 1, x, loc=loc, scale=scale)
98
+
99
+ def ppf(self, q, loc=0, scale=1):
100
+ """Evaluate the percent-point function.
101
+
102
+ Parameters
103
+ ----------
104
+ q : array_like
105
+ Probabilities in ``[0, 1]``.
106
+ loc : float, optional
107
+ Location parameter of the distribution. Defaults to ``0``.
108
+ scale : float, optional
109
+ Scale parameter of the distribution. Defaults to ``1``.
110
+
111
+ Returns
112
+ -------
113
+ FlopscopeArray
114
+ Quantiles corresponding to ``q``.
115
+
116
+ Notes
117
+ -----
118
+ Equivalent to ``scipy.stats.logistic.ppf(q, loc, scale)``.
119
+ FLOP cost: ``1 * numel(q)``.
120
+
121
+ Examples
122
+ --------
123
+ >>> import numpy as np
124
+ >>> import flopscope as flops
125
+ >>> q = np.array([0.25, 0.5, 0.75])
126
+ >>> np.round(flops.stats.logistic.ppf(q), 3)
127
+ array([-1.099, 0. , 1.099])
128
+ """
129
+ return self._deduct_and_call("ppf", 1, q, loc=loc, scale=scale)
130
+
131
+ def _compute_pdf(self, x, loc=0, scale=1):
132
+ z = (x - loc) / scale
133
+ ez = _np.exp(-z)
134
+ return ez / (scale * (1.0 + ez) ** 2)
135
+
136
+ def _compute_cdf(self, x, loc=0, scale=1):
137
+ z = (x - loc) / scale
138
+ return 1.0 / (1.0 + _np.exp(-z))
139
+
140
+ def _compute_ppf(self, q, loc=0, scale=1):
141
+ result = loc + scale * _np.log(q / (1.0 - q))
142
+ result = _np.where((q > 0) & (q < 1), result, _np.nan)
143
+ result = _np.where(q == 0, -_np.inf, result)
144
+ result = _np.where(q == 1, _np.inf, result)
145
+ return result
146
+
147
+
148
+ logistic = LogisticDistribution()
@@ -0,0 +1,160 @@
1
+ """Log-normal distribution for :mod:`flopscope.stats`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as _np
6
+
7
+ from flopscope.stats._base import ContinuousDistribution
8
+ from flopscope.stats._erf import _erf
9
+ from flopscope.stats._ndtri import _ndtri
10
+
11
+ _SQRT2 = _np.sqrt(2.0)
12
+ _INV_SQRT_2PI = 1.0 / _np.sqrt(2.0 * _np.pi)
13
+
14
+
15
+ class LognormDistribution(ContinuousDistribution):
16
+ """Log-normal continuous random variable.
17
+
18
+ This object mirrors ``scipy.stats.lognorm``.
19
+
20
+ Methods
21
+ -------
22
+ pdf(x, s, loc=0, scale=1)
23
+ Evaluate the probability density function.
24
+ cdf(x, s, loc=0, scale=1)
25
+ Evaluate the cumulative distribution function.
26
+ ppf(q, s, loc=0, scale=1)
27
+ Evaluate the percent-point function.
28
+
29
+ Notes
30
+ -----
31
+ ``s`` is the shape parameter: the standard deviation of the underlying
32
+ normal distribution. It is the first positional argument, ahead of
33
+ ``loc`` and ``scale``, matching SciPy's ``lognorm`` signature. Each
34
+ public method deducts ``1 * numel(input)`` FLOPs from the active budget.
35
+ """
36
+
37
+ def __init__(self):
38
+ super().__init__("lognorm")
39
+
40
+ def pdf(self, x, s, loc=0, scale=1):
41
+ """Evaluate the probability density function.
42
+
43
+ Parameters
44
+ ----------
45
+ x : array_like
46
+ Points at which to evaluate the density.
47
+ s : float
48
+ Shape parameter of the distribution.
49
+ loc : float, optional
50
+ Location parameter. Defaults to ``0``.
51
+ scale : float, optional
52
+ Scale parameter. Defaults to ``1``.
53
+
54
+ Returns
55
+ -------
56
+ FlopscopeArray
57
+ Probability density evaluated elementwise at ``x``.
58
+
59
+ Notes
60
+ -----
61
+ Equivalent to ``scipy.stats.lognorm.pdf(x, s, loc, scale)``.
62
+ FLOP cost: ``1 * numel(x)``.
63
+
64
+ Examples
65
+ --------
66
+ >>> import numpy as np
67
+ >>> import flopscope as flops
68
+ >>> x = np.array([0.5, 1.0, 2.0])
69
+ >>> np.round(flops.stats.lognorm.pdf(x, s=0.5), 3)
70
+ array([0.61 , 0.798, 0.153])
71
+ """
72
+ return self._deduct_and_call("pdf", 1, x, s, loc=loc, scale=scale)
73
+
74
+ def cdf(self, x, s, loc=0, scale=1):
75
+ """Evaluate the cumulative distribution function.
76
+
77
+ Parameters
78
+ ----------
79
+ x : array_like
80
+ Points at which to evaluate the cumulative probability.
81
+ s : float
82
+ Shape parameter of the distribution.
83
+ loc : float, optional
84
+ Location parameter. Defaults to ``0``.
85
+ scale : float, optional
86
+ Scale parameter. Defaults to ``1``.
87
+
88
+ Returns
89
+ -------
90
+ FlopscopeArray
91
+ Cumulative probability evaluated elementwise at ``x``.
92
+
93
+ Notes
94
+ -----
95
+ Equivalent to ``scipy.stats.lognorm.cdf(x, s, loc, scale)``.
96
+ FLOP cost: ``1 * numel(x)``.
97
+
98
+ Examples
99
+ --------
100
+ >>> import numpy as np
101
+ >>> import flopscope as flops
102
+ >>> x = np.array([0.5, 1.0, 2.0])
103
+ >>> np.round(flops.stats.lognorm.cdf(x, s=0.5), 3)
104
+ array([0.083, 0.5 , 0.917])
105
+ """
106
+ return self._deduct_and_call("cdf", 1, x, s, loc=loc, scale=scale)
107
+
108
+ def ppf(self, q, s, loc=0, scale=1):
109
+ """Evaluate the percent-point function.
110
+
111
+ Parameters
112
+ ----------
113
+ q : array_like
114
+ Probabilities in ``[0, 1]``.
115
+ s : float
116
+ Shape parameter of the distribution.
117
+ loc : float, optional
118
+ Location parameter. Defaults to ``0``.
119
+ scale : float, optional
120
+ Scale parameter. Defaults to ``1``.
121
+
122
+ Returns
123
+ -------
124
+ FlopscopeArray
125
+ Quantiles corresponding to ``q``.
126
+
127
+ Notes
128
+ -----
129
+ Equivalent to ``scipy.stats.lognorm.ppf(q, s, loc, scale)``.
130
+ FLOP cost: ``1 * numel(q)``.
131
+
132
+ Examples
133
+ --------
134
+ >>> import numpy as np
135
+ >>> import flopscope as flops
136
+ >>> q = np.array([0.25, 0.5, 0.75])
137
+ >>> np.round(flops.stats.lognorm.ppf(q, s=0.5), 3)
138
+ array([0.714, 1. , 1.401])
139
+ """
140
+ return self._deduct_and_call("ppf", 1, q, s, loc=loc, scale=scale)
141
+
142
+ def _compute_pdf(self, x, s, loc=0, scale=1):
143
+ y = (x - loc) / scale
144
+ safe_y = _np.where(y > 0, y, 1.0) # avoid log(0)
145
+ lny = _np.log(safe_y)
146
+ result = _INV_SQRT_2PI / (s * safe_y * scale) * _np.exp(-0.5 * (lny / s) ** 2)
147
+ return _np.where(y > 0, result, 0.0)
148
+
149
+ def _compute_cdf(self, x, s, loc=0, scale=1):
150
+ y = (x - loc) / scale
151
+ safe_y = _np.where(y > 0, y, 1.0)
152
+ z = _np.log(safe_y) / (s * _SQRT2)
153
+ result = 0.5 * (1.0 + _erf(z))
154
+ return _np.where(y > 0, result, 0.0)
155
+
156
+ def _compute_ppf(self, q, s, loc=0, scale=1):
157
+ return loc + scale * _np.exp(s * _ndtri(q))
158
+
159
+
160
+ lognorm = LognormDistribution()
@@ -0,0 +1,133 @@
1
+ """Inverse standard normal CDF (ndtri) via Acklam's algorithm + Newton refinement.
2
+
3
+ Accuracy: ~1e-12 against scipy.special.ndtri.
4
+
5
+ References
6
+ ----------
7
+ .. [1] Peter J. Acklam, "An algorithm for computing the inverse normal
8
+ cumulative distribution function," 2004.
9
+ https://web.archive.org/web/20151030215612/http://home.online.no/~pjacklam/notes/invnorm/
10
+ (Rational approximation coefficients; algorithm made freely available by the author.)
11
+
12
+ .. [2] StackedBoxes, "Acklam's Normal Quantile Function," 2017.
13
+ https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import numpy as np
19
+
20
+ from flopscope.stats._erf import _erf
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Acklam coefficients
24
+ # ---------------------------------------------------------------------------
25
+
26
+ _A = (
27
+ -3.969683028665376e01,
28
+ 2.209460984245205e02,
29
+ -2.759285104469687e02,
30
+ 1.383577518672690e02,
31
+ -3.066479806614716e01,
32
+ 2.506628277459239e00,
33
+ )
34
+ _B = (
35
+ -5.447609879822406e01,
36
+ 1.615858368580409e02,
37
+ -1.556989798598866e02,
38
+ 6.680131188771972e01,
39
+ -1.328068155288572e01,
40
+ )
41
+ _C = (
42
+ -7.784894002430293e-03,
43
+ -3.223964580411365e-01,
44
+ -2.400758277161838e00,
45
+ -2.549732539343734e00,
46
+ 4.374664141464968e00,
47
+ 2.938163982698783e00,
48
+ )
49
+ _D = (
50
+ 7.784695709041462e-03,
51
+ 3.224671290700398e-01,
52
+ 2.445134137142996e00,
53
+ 3.754408661907416e00,
54
+ )
55
+
56
+ _P_LOW = 0.02425
57
+ _P_HIGH = 1.0 - _P_LOW
58
+
59
+ _SQRT2 = np.sqrt(2.0)
60
+ _INV_SQRT_2PI = 1.0 / np.sqrt(2.0 * np.pi)
61
+
62
+
63
+ def _norm_pdf_internal(x):
64
+ """Standard normal PDF (internal helper, no budget deduction)."""
65
+ return _INV_SQRT_2PI * np.exp(-0.5 * x * x)
66
+
67
+
68
+ def _norm_cdf_internal(x):
69
+ """Standard normal CDF (internal helper, no budget deduction)."""
70
+ return 0.5 * (1.0 + _erf(x / _SQRT2))
71
+
72
+
73
+ def _ndtri(p):
74
+ """Inverse standard normal CDF.
75
+
76
+ Maps probability *p* in (0, 1) to the quantile *x* such that
77
+ Phi(x) = p, where Phi is the standard normal CDF.
78
+
79
+ Uses Acklam's rational approximation [1]_ with one Newton-Raphson
80
+ refinement step for ~1e-12 accuracy.
81
+
82
+ Edge cases: p=0 -> -inf, p=1 -> +inf, p<0 or p>1 -> nan.
83
+ """
84
+ p = np.asarray(p, dtype=np.float64)
85
+ scalar = p.ndim == 0
86
+ p = np.atleast_1d(p)
87
+
88
+ out = np.empty_like(p)
89
+
90
+ # Edge cases
91
+ out[p == 0.0] = -np.inf
92
+ out[p == 1.0] = np.inf
93
+ out[(p < 0.0) | (p > 1.0)] = np.nan
94
+
95
+ # Lower region: 0 < p < P_LOW
96
+ m_low = (p > 0.0) & (p < _P_LOW)
97
+ if np.any(m_low):
98
+ q = np.sqrt(-2.0 * np.log(p[m_low]))
99
+ num = ((((_C[0] * q + _C[1]) * q + _C[2]) * q + _C[3]) * q + _C[4]) * q + _C[5]
100
+ den = (((_D[0] * q + _D[1]) * q + _D[2]) * q + _D[3]) * q + 1.0
101
+ out[m_low] = num / den
102
+
103
+ # Central region: P_LOW <= p <= P_HIGH
104
+ m_mid = (p >= _P_LOW) & (p <= _P_HIGH)
105
+ if np.any(m_mid):
106
+ q = p[m_mid] - 0.5
107
+ r = q * q
108
+ num = ((((_A[0] * r + _A[1]) * r + _A[2]) * r + _A[3]) * r + _A[4]) * r + _A[5]
109
+ den = ((((_B[0] * r + _B[1]) * r + _B[2]) * r + _B[3]) * r + _B[4]) * r + 1.0
110
+ out[m_mid] = q * num / den
111
+
112
+ # Upper region: P_HIGH < p < 1
113
+ m_high = (p > _P_HIGH) & (p < 1.0)
114
+ if np.any(m_high):
115
+ q = np.sqrt(-2.0 * np.log(1.0 - p[m_high]))
116
+ num = ((((_C[0] * q + _C[1]) * q + _C[2]) * q + _C[3]) * q + _C[4]) * q + _C[5]
117
+ den = (((_D[0] * q + _D[1]) * q + _D[2]) * q + _D[3]) * q + 1.0
118
+ out[m_high] = -(num / den)
119
+
120
+ # Newton refinement (one step) for the interior
121
+ m_interior = (p > 0.0) & (p < 1.0)
122
+ if np.any(m_interior):
123
+ x0 = out[m_interior]
124
+ phi = _norm_cdf_internal(x0)
125
+ pdf = _norm_pdf_internal(x0)
126
+ # Avoid division by zero in pdf
127
+ safe = pdf > 1e-300
128
+ correction = np.where(safe, (phi - p[m_interior]) / pdf, 0.0)
129
+ out[m_interior] = x0 - correction
130
+
131
+ if scalar:
132
+ return float(out[0])
133
+ return out
@@ -0,0 +1,149 @@
1
+ """Normal distribution for :mod:`flopscope.stats`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from flopscope.stats._base import ContinuousDistribution
8
+ from flopscope.stats._erf import _erf
9
+ from flopscope.stats._ndtri import _ndtri
10
+
11
+ _SQRT2 = np.sqrt(2.0)
12
+ _INV_SQRT_2PI = 1.0 / np.sqrt(2.0 * np.pi)
13
+
14
+
15
+ class NormDistribution(ContinuousDistribution):
16
+ """Normal (Gaussian) continuous random variable.
17
+
18
+ This object mirrors ``scipy.stats.norm`` and uses the standard
19
+ ``loc``/``scale`` parameterization.
20
+
21
+ Methods
22
+ -------
23
+ pdf(x, loc=0, scale=1)
24
+ Evaluate the probability density function.
25
+ cdf(x, loc=0, scale=1)
26
+ Evaluate the cumulative distribution function.
27
+ ppf(q, loc=0, scale=1)
28
+ Evaluate the percent-point function.
29
+
30
+ Notes
31
+ -----
32
+ ``loc`` is the mean and ``scale`` is the standard deviation. Each public
33
+ method deducts ``1 * numel(input)`` FLOPs from the active budget.
34
+ """
35
+
36
+ def __init__(self):
37
+ super().__init__("norm")
38
+
39
+ def pdf(self, x, loc=0, scale=1):
40
+ """Evaluate the probability density function.
41
+
42
+ Parameters
43
+ ----------
44
+ x : array_like
45
+ Points at which to evaluate the density.
46
+ loc : float, optional
47
+ Mean of the distribution. Defaults to ``0``.
48
+ scale : float, optional
49
+ Standard deviation of the distribution. Defaults to ``1``.
50
+
51
+ Returns
52
+ -------
53
+ FlopscopeArray
54
+ Probability density evaluated elementwise at ``x``.
55
+
56
+ Notes
57
+ -----
58
+ Equivalent to ``scipy.stats.norm.pdf(x, loc, scale)``.
59
+ FLOP cost: ``1 * numel(x)``.
60
+
61
+ Examples
62
+ --------
63
+ >>> import numpy as np
64
+ >>> import flopscope as flops
65
+ >>> x = np.array([-1.0, 0.0, 1.0])
66
+ >>> np.round(flops.stats.norm.pdf(x), 3)
67
+ array([0.242, 0.399, 0.242])
68
+ """
69
+ return self._deduct_and_call("pdf", 1, x, loc=loc, scale=scale)
70
+
71
+ def cdf(self, x, loc=0, scale=1):
72
+ """Evaluate the cumulative distribution function.
73
+
74
+ Parameters
75
+ ----------
76
+ x : array_like
77
+ Points at which to evaluate the cumulative probability.
78
+ loc : float, optional
79
+ Mean of the distribution. Defaults to ``0``.
80
+ scale : float, optional
81
+ Standard deviation of the distribution. Defaults to ``1``.
82
+
83
+ Returns
84
+ -------
85
+ FlopscopeArray
86
+ Cumulative probability evaluated elementwise at ``x``.
87
+
88
+ Notes
89
+ -----
90
+ Equivalent to ``scipy.stats.norm.cdf(x, loc, scale)``.
91
+ FLOP cost: ``1 * numel(x)``.
92
+
93
+ Examples
94
+ --------
95
+ >>> import numpy as np
96
+ >>> import flopscope as flops
97
+ >>> x = np.array([-1.0, 0.0, 1.0])
98
+ >>> np.round(flops.stats.norm.cdf(x), 3)
99
+ array([0.159, 0.5 , 0.841])
100
+ """
101
+ return self._deduct_and_call("cdf", 1, x, loc=loc, scale=scale)
102
+
103
+ def ppf(self, q, loc=0, scale=1):
104
+ """Evaluate the percent-point function.
105
+
106
+ Parameters
107
+ ----------
108
+ q : array_like
109
+ Probabilities in ``[0, 1]``.
110
+ loc : float, optional
111
+ Mean of the distribution. Defaults to ``0``.
112
+ scale : float, optional
113
+ Standard deviation of the distribution. Defaults to ``1``.
114
+
115
+ Returns
116
+ -------
117
+ FlopscopeArray
118
+ Quantiles corresponding to ``q``.
119
+
120
+ Notes
121
+ -----
122
+ Equivalent to ``scipy.stats.norm.ppf(q, loc, scale)``.
123
+ FLOP cost: ``1 * numel(q)``.
124
+
125
+ Examples
126
+ --------
127
+ >>> import numpy as np
128
+ >>> import flopscope as flops
129
+ >>> q = np.array([0.25, 0.5, 0.75])
130
+ >>> np.round(flops.stats.norm.ppf(q), 3)
131
+ array([-0.674, 0. , 0.674])
132
+ """
133
+ return self._deduct_and_call("ppf", 1, q, loc=loc, scale=scale)
134
+
135
+ # --- Pure-NumPy implementations (no budget deduction) ---
136
+
137
+ def _compute_pdf(self, x, loc=0, scale=1):
138
+ z = (x - loc) / scale
139
+ return _INV_SQRT_2PI / scale * np.exp(-0.5 * z * z)
140
+
141
+ def _compute_cdf(self, x, loc=0, scale=1):
142
+ z = (x - loc) / (scale * _SQRT2)
143
+ return 0.5 * (1.0 + _erf(z))
144
+
145
+ def _compute_ppf(self, q, loc=0, scale=1):
146
+ return loc + scale * _ndtri(q)
147
+
148
+
149
+ norm = NormDistribution()