aersn 0.1.0__tar.gz

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.
@@ -0,0 +1,56 @@
1
+ # Changes
2
+
3
+ ## 0.1.0
4
+
5
+ - First public-release build, following the 0.1.0a3 Windows checks and TestPyPI
6
+ download-and-install verification.
7
+ - Update the version and installation instructions for PyPI, including optional
8
+ plotting, exact-version installation and local wheel/source installation.
9
+ - Retain the 0.1.0a3 statistical implementation, numerical safeguards, tests and
10
+ examples without changes. The package remains an early release (Beta classifier).
11
+
12
+ ## 0.1.0a3
13
+
14
+ - Preserve missing-value masks in lists and tuples of array rows before numeric
15
+ conversion. Reject masked rows through the mean, OLS and influence interfaces;
16
+ retain identical results for unmasked lists, tuples and arrays.
17
+ - Explain how to construct a justified lower-dimensional target when the full
18
+ fit is rejected before an object exists. Full-model nuisance effects must be
19
+ retained; neither the conditioning limit nor the statistical model is changed.
20
+ - Qualify boundary-membership guidance for large centers and ill-conditioned
21
+ paths. The gauge tolerance and hypothesis-test rule are unchanged.
22
+ - Update installation instructions for this local candidate and distinguish them
23
+ from commands to use after an announced PyPI release.
24
+ - Print the tested artifact version and a final pass summary in the platform
25
+ runner; verify that the installed version matches the selected artifact.
26
+
27
+ This candidate passed 150 tests after both wheel and source installation on
28
+ Windows and was uploaded to TestPyPI on 27 September 2026. Both downloaded
29
+ artifacts matched the tested files and passed 150 tests each on macOS.
30
+
31
+ ## 0.1.0a2
32
+
33
+ - Reject masked observations before converting NumPy arrays. Fully unmasked
34
+ masked-array containers remain accepted; no observations are silently removed.
35
+ - Check the path's condition number after coordinate range scaling. Values above
36
+ 1e8 now raise `NumericalError`; exactly/numerically rank-deficient paths retain
37
+ `DegeneratePathError`. Original-coordinate dual constraints and objectives are
38
+ checked after the LP. No regularization is introduced.
39
+ - Add high-precision stress fixtures, including the actual Windows failure, and
40
+ explicit tests of rejection outside the supported numerical range. Tighten
41
+ ordinary R comparisons and supported affine-transformation tolerances.
42
+ - Default plots to independent axis scales; add `equal_scale=True`, reduce 3D
43
+ tick crowding, and explain how to install the optional Matplotlib dependency.
44
+ - Make the user guide's runnable examples work in order, distinguish the
45
+ model-specific schematic call, and test README and guide examples automatically.
46
+ - Document membership tolerance versus strict test decisions. Scalar candidate
47
+ lists now accept one candidate per element.
48
+ - Give wheel installation instructions and remove repository-relative Markdown
49
+ links from the package-index description. Preserve the illustrative random seed.
50
+ - Select artifacts by the current package version in the Windows runner. Keep
51
+ old-version logs separate. The unchanged external audit can be requested with
52
+ `--legacy-audit`; its expected-failure marks and unconditional acceptance of
53
+ extremely ill-conditioned paths describe 0.1.0a1, not the revised contract.
54
+
55
+ This version has not been uploaded to TestPyPI or PyPI. It does not modify the
56
+ separately submitted R 0.2.3 package.
aersn-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md USER_GUIDE.md VALIDATION.md CHANGELOG.md
2
+ recursive-include tests *.py *.json
3
+ recursive-include examples *.py
4
+ recursive-include tools *.R
aersn-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: aersn
3
+ Version: 0.1.0
4
+ Summary: Affine-equivariant adjusted-range self-normalization for time-series inference
5
+ Author: Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey
6
+ Author-email: Jiajing Sun <jiajing.sun@gmail.com>
7
+ Maintainer-email: Jiajing Sun <jiajing.sun@gmail.com>
8
+ License-Expression: MIT
9
+ Project-URL: Paper, https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: numpy>=1.24
18
+ Requires-Dist: scipy>=1.11
19
+ Provides-Extra: plot
20
+ Requires-Dist: matplotlib>=3.7; extra == "plot"
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=8; extra == "test"
23
+ Requires-Dist: pytest-cov>=5; extra == "test"
24
+ Requires-Dist: matplotlib>=3.7; extra == "test"
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1.2; extra == "dev"
27
+ Requires-Dist: twine>=6; extra == "dev"
28
+ Requires-Dist: ruff>=0.11; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # aersn for Python
32
+
33
+ Affine-equivariant adjusted-range self-normalization for time-series inference.
34
+
35
+ **Version 0.1.0.** This is a native
36
+ NumPy/SciPy implementation: installation and use require neither R nor the R
37
+ package. The related R package has its own version and release process.
38
+
39
+ ## What it does
40
+
41
+ Given an estimate and observation-level influence contributions, `aersn` computes
42
+ an adjusted-range test and joint confidence region without estimating the
43
+ long-run covariance matrix. It also reports simultaneous intervals for linear
44
+ contrasts. Built-in entry points cover sample means and ordinary least squares
45
+ (OLS). Other asymptotically linear estimators can use `from_influence`.
46
+
47
+ For vector parameters, the self-normalizer is the convex hull of increments of
48
+ the centered influence path. The gauge of a vector is the smallest nonnegative
49
+ multiple of this hull that contains the vector. It is computed by linear programming; the
50
+ support in each direction is the range of the projected path. The construction
51
+ is affine equivariant. Two- and three-dimensional regions are polygons and
52
+ polyhedra, not fitted ellipses or ellipsoids.
53
+
54
+ ## Install
55
+
56
+ Python 3.11 or newer is required. Install from PyPI, preferably in a virtual
57
+ environment:
58
+
59
+ ```sh
60
+ python -m pip install aersn
61
+ ```
62
+
63
+ To include two- and three-dimensional plotting:
64
+
65
+ ```sh
66
+ python -m pip install "aersn[plot]"
67
+ ```
68
+
69
+ The base installation needs NumPy and SciPy. The `plot` extra adds Matplotlib.
70
+ To reproduce results with this specific version:
71
+
72
+ ```sh
73
+ python -m pip install "aersn[plot]==0.1.0"
74
+ ```
75
+
76
+ For an offline copy supplied by the authors, run
77
+ `python -m pip install "./aersn-0.1.0-py3-none-any.whl[plot]"` from the directory
78
+ containing the wheel. To install an extracted source distribution instead,
79
+ run `python -m pip install ".[plot]"` from its top-level directory.
80
+
81
+ ## A first example
82
+
83
+ ```python
84
+ import numpy as np
85
+ import aersn
86
+
87
+ rng = np.random.default_rng(10)
88
+ y = rng.normal(size=(300, 2))
89
+ for t in range(1, len(y)):
90
+ y[t] += 0.35 * y[t - 1]
91
+
92
+ fit = aersn.mean(y, names=["Mean 1", "Mean 2"])
93
+ ref = fit.reference(draws=2_000, seed=71)
94
+ test = fit.test([0, 0], reference=ref)
95
+ print(test.statistic, test.pvalue, test.mcse)
96
+ print(fit.confint(reference=ref))
97
+
98
+ region = fit.region(reference=ref)
99
+ ax = region.plot()
100
+ ax.figure.savefig("joint-region.png", dpi=180)
101
+ ```
102
+
103
+ The small reference simulation above is for illustration. The default is 10,000
104
+ draws; increase it when Monte Carlo uncertainty affects a substantive decision.
105
+ Reuse a reference for the same dimension and grid, rather than simulating it
106
+ again for each null hypothesis. Reusing a reference from a different sample
107
+ size, dimension or grid raises an error.
108
+ The particular random sample may reject its generating mean; the example is
109
+ not a demonstration of an exact finite-sample rejection rate.
110
+
111
+ ## Statistical interpretation
112
+
113
+ - The input convention is
114
+ `sqrt(n) * (estimate - theta) = sum(psi_true) / sqrt(n) + o_p(1)`.
115
+ Rows of `psi` are observations in time order, not resampled or sorted records.
116
+ - A functional central limit theorem, asymptotic linearity and a valid estimated
117
+ influence path are required. Numerical full rank does not verify these assumptions.
118
+ - Matched-grid Brownian quantiles account for the grid used to evaluate the
119
+ reference path. They do not make inference finite-sample exact for general
120
+ dependent data.
121
+ - `confint` and `contrast` project a joint region. These are simultaneous
122
+ intervals, not separately constructed marginal intervals.
123
+ - A supplied nonlinear variance-accumulation profile needs a model-specific
124
+ justification. Merely substituting such a profile for a sample mean does not
125
+ construct the required path. See `USER_GUIDE.md` in the source distribution.
126
+ - Missing values, including masked NumPy entries, are rejected. Paths that
127
+ remain extremely ill-conditioned after coordinate scaling raise an error;
128
+ the package does not regularize them silently.
129
+
130
+ ## Documentation and examples
131
+
132
+ The source distribution includes:
133
+
134
+ - `USER_GUIDE.md`: statistical interpretation and API conventions.
135
+ - `VALIDATION.md`: numerical validation and release checks.
136
+ - `examples/mean_regions.py`: executable 2D and 3D example.
137
+ - `examples/regression.py`: OLS and influence-contribution example.
138
+
139
+ Run the examples from this directory:
140
+
141
+ ```sh
142
+ python examples/mean_regions.py
143
+ python examples/regression.py
144
+ ```
145
+
146
+ Figures are saved in `examples/output/` with labeled axes. Function and class
147
+ docstrings are also available through Python's `help()`.
148
+ Plots use independent axis scales by default, which is useful for parameters
149
+ measured in different units. Use `region.plot(equal_scale=True)` for equal
150
+ distance per data unit. Both choices display the same computed vertices.
151
+
152
+ ## Development
153
+
154
+ ```sh
155
+ python -m pip install -e ".[plot,test,dev]"
156
+ python -m pytest --cov=aersn
157
+ python -m ruff check .
158
+ python -m build
159
+ python -m twine check dist/*
160
+ ```
161
+
162
+ Tests use fixed inputs and results exported from R `aersn` 0.2.3. They do not
163
+ require R. The LP is also checked against an independent primal calculation.
164
+ This release implements the adjusted-range method, not the five comparison
165
+ methods or every model-specific interface in the R package.
166
+
167
+ ## Paper and authors
168
+
169
+ Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun (2026),
170
+ *Affine-Equivariant Adjusted-Range Self-Normalization*, Cambridge Working Papers
171
+ in Economics, No. 2678. The paper is available as a
172
+ [Cambridge working paper](https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization).
173
+ This citation does not imply journal acceptance.
174
+
175
+ Copyright belongs to the five authors. Distributed under the MIT license.
176
+ Maintainer: Jiajing Sun, <jiajing.sun@gmail.com>.
aersn-0.1.0/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # aersn for Python
2
+
3
+ Affine-equivariant adjusted-range self-normalization for time-series inference.
4
+
5
+ **Version 0.1.0.** This is a native
6
+ NumPy/SciPy implementation: installation and use require neither R nor the R
7
+ package. The related R package has its own version and release process.
8
+
9
+ ## What it does
10
+
11
+ Given an estimate and observation-level influence contributions, `aersn` computes
12
+ an adjusted-range test and joint confidence region without estimating the
13
+ long-run covariance matrix. It also reports simultaneous intervals for linear
14
+ contrasts. Built-in entry points cover sample means and ordinary least squares
15
+ (OLS). Other asymptotically linear estimators can use `from_influence`.
16
+
17
+ For vector parameters, the self-normalizer is the convex hull of increments of
18
+ the centered influence path. The gauge of a vector is the smallest nonnegative
19
+ multiple of this hull that contains the vector. It is computed by linear programming; the
20
+ support in each direction is the range of the projected path. The construction
21
+ is affine equivariant. Two- and three-dimensional regions are polygons and
22
+ polyhedra, not fitted ellipses or ellipsoids.
23
+
24
+ ## Install
25
+
26
+ Python 3.11 or newer is required. Install from PyPI, preferably in a virtual
27
+ environment:
28
+
29
+ ```sh
30
+ python -m pip install aersn
31
+ ```
32
+
33
+ To include two- and three-dimensional plotting:
34
+
35
+ ```sh
36
+ python -m pip install "aersn[plot]"
37
+ ```
38
+
39
+ The base installation needs NumPy and SciPy. The `plot` extra adds Matplotlib.
40
+ To reproduce results with this specific version:
41
+
42
+ ```sh
43
+ python -m pip install "aersn[plot]==0.1.0"
44
+ ```
45
+
46
+ For an offline copy supplied by the authors, run
47
+ `python -m pip install "./aersn-0.1.0-py3-none-any.whl[plot]"` from the directory
48
+ containing the wheel. To install an extracted source distribution instead,
49
+ run `python -m pip install ".[plot]"` from its top-level directory.
50
+
51
+ ## A first example
52
+
53
+ ```python
54
+ import numpy as np
55
+ import aersn
56
+
57
+ rng = np.random.default_rng(10)
58
+ y = rng.normal(size=(300, 2))
59
+ for t in range(1, len(y)):
60
+ y[t] += 0.35 * y[t - 1]
61
+
62
+ fit = aersn.mean(y, names=["Mean 1", "Mean 2"])
63
+ ref = fit.reference(draws=2_000, seed=71)
64
+ test = fit.test([0, 0], reference=ref)
65
+ print(test.statistic, test.pvalue, test.mcse)
66
+ print(fit.confint(reference=ref))
67
+
68
+ region = fit.region(reference=ref)
69
+ ax = region.plot()
70
+ ax.figure.savefig("joint-region.png", dpi=180)
71
+ ```
72
+
73
+ The small reference simulation above is for illustration. The default is 10,000
74
+ draws; increase it when Monte Carlo uncertainty affects a substantive decision.
75
+ Reuse a reference for the same dimension and grid, rather than simulating it
76
+ again for each null hypothesis. Reusing a reference from a different sample
77
+ size, dimension or grid raises an error.
78
+ The particular random sample may reject its generating mean; the example is
79
+ not a demonstration of an exact finite-sample rejection rate.
80
+
81
+ ## Statistical interpretation
82
+
83
+ - The input convention is
84
+ `sqrt(n) * (estimate - theta) = sum(psi_true) / sqrt(n) + o_p(1)`.
85
+ Rows of `psi` are observations in time order, not resampled or sorted records.
86
+ - A functional central limit theorem, asymptotic linearity and a valid estimated
87
+ influence path are required. Numerical full rank does not verify these assumptions.
88
+ - Matched-grid Brownian quantiles account for the grid used to evaluate the
89
+ reference path. They do not make inference finite-sample exact for general
90
+ dependent data.
91
+ - `confint` and `contrast` project a joint region. These are simultaneous
92
+ intervals, not separately constructed marginal intervals.
93
+ - A supplied nonlinear variance-accumulation profile needs a model-specific
94
+ justification. Merely substituting such a profile for a sample mean does not
95
+ construct the required path. See `USER_GUIDE.md` in the source distribution.
96
+ - Missing values, including masked NumPy entries, are rejected. Paths that
97
+ remain extremely ill-conditioned after coordinate scaling raise an error;
98
+ the package does not regularize them silently.
99
+
100
+ ## Documentation and examples
101
+
102
+ The source distribution includes:
103
+
104
+ - `USER_GUIDE.md`: statistical interpretation and API conventions.
105
+ - `VALIDATION.md`: numerical validation and release checks.
106
+ - `examples/mean_regions.py`: executable 2D and 3D example.
107
+ - `examples/regression.py`: OLS and influence-contribution example.
108
+
109
+ Run the examples from this directory:
110
+
111
+ ```sh
112
+ python examples/mean_regions.py
113
+ python examples/regression.py
114
+ ```
115
+
116
+ Figures are saved in `examples/output/` with labeled axes. Function and class
117
+ docstrings are also available through Python's `help()`.
118
+ Plots use independent axis scales by default, which is useful for parameters
119
+ measured in different units. Use `region.plot(equal_scale=True)` for equal
120
+ distance per data unit. Both choices display the same computed vertices.
121
+
122
+ ## Development
123
+
124
+ ```sh
125
+ python -m pip install -e ".[plot,test,dev]"
126
+ python -m pytest --cov=aersn
127
+ python -m ruff check .
128
+ python -m build
129
+ python -m twine check dist/*
130
+ ```
131
+
132
+ Tests use fixed inputs and results exported from R `aersn` 0.2.3. They do not
133
+ require R. The LP is also checked against an independent primal calculation.
134
+ This release implements the adjusted-range method, not the five comparison
135
+ methods or every model-specific interface in the R package.
136
+
137
+ ## Paper and authors
138
+
139
+ Yongmiao Hong, Zhuo Lin, Oliver Linton, Whitney K. Newey and Jiajing Sun (2026),
140
+ *Affine-Equivariant Adjusted-Range Self-Normalization*, Cambridge Working Papers
141
+ in Economics, No. 2678. The paper is available as a
142
+ [Cambridge working paper](https://www.janeway.econ.cam.ac.uk/publication/affine-equivariant-adjusted-range-self-normalization).
143
+ This citation does not imply journal acceptance.
144
+
145
+ Copyright belongs to the five authors. Distributed under the MIT license.
146
+ Maintainer: Jiajing Sun, <jiajing.sun@gmail.com>.
@@ -0,0 +1,275 @@
1
+ # Using aersn in Python
2
+
3
+ ## 1. Observations, estimates and influence contributions
4
+
5
+ Let `estimate` contain q estimated parameters. The input `psi` is an n-by-q
6
+ matrix: row t is the estimated influence contribution of observation t, and
7
+ column j corresponds to parameter j. Observations must remain in time order.
8
+ The sample size is always the number of rows; it is not the number of grid
9
+ nodes, regression coefficients or reference draws.
10
+
11
+ The centered path is
12
+
13
+ ```text
14
+ G[k] = (sum(psi[:k]) - nodes[k] * sum(psi)) / sqrt(n), k=0,...,n.
15
+ ```
16
+
17
+ With the default `nodes[k]=k/n`, this is calendar-time centering. Both endpoints
18
+ are zero. The increment hull is `K = conv{G[i]-G[j]}`. For a null vector v,
19
+ the statistic is `gamma_K(sqrt(n)*(estimate-v))`. Larger values provide greater
20
+ evidence against the null.
21
+ The gauge `gamma_K(z)` is the smallest nonnegative number a such that z belongs
22
+ to a*K.
23
+
24
+ For the sample mean, use `aersn.mean(y)`; it calculates the mean and contributions
25
+ `y-y.mean(axis=0)`. The following equivalent influence-contribution construction
26
+ also provides the runnable example used in Sections 2 and 3:
27
+
28
+ ```python
29
+ import numpy as np
30
+ import aersn
31
+
32
+ y = np.random.default_rng(10).normal(size=(300, 2))
33
+ estimate = y.mean(axis=0)
34
+ psi = y - estimate
35
+ fit = aersn.from_influence(estimate, psi, names=["parameter 1", "parameter 2"])
36
+ ```
37
+
38
+ The contributions must account for all estimated nuisance parameters. For
39
+ example, the influence contribution of an ordinary least squares (OLS) slope
40
+ depends on the inverse moment matrix of the full regression, not just that
41
+ slope's regressor. Missing values (including NumPy masked entries) and nonfinite
42
+ values are rejected rather than silently
43
+ dropped; deleting time observations can change the statistical problem.
44
+
45
+ ## 2. Tests and reference distributions
46
+
47
+ ```python
48
+ ref = fit.reference(draws=10_000, seed=1)
49
+ result = fit.test([0, 0], reference=ref, level=0.95)
50
+ print(result.statistic, result.critical_value, result.reject)
51
+ print(result.pvalue, result.mcse, result.tail_interval)
52
+ ```
53
+
54
+ Here `level=0.95` means a 5 percent test and a 95 percent confidence region.
55
+ The default alternative is `"two-sided"`. Rejection occurs when the statistic
56
+ strictly exceeds the estimated reference quantile. For a scalar parameter,
57
+ `alternative="greater"` or `"less"` uses the signed endpoint-to-range ratio.
58
+ One-sided alternatives are not available for a joint vector hypothesis.
59
+
60
+ For each reference draw, Brownian increments have variances equal to the grid
61
+ spacings. They form a Brownian bridge at those nodes. An independent standard
62
+ normal vector provides the numerator. The same gauge calculation is used for
63
+ data and reference paths. A failed draw stops the calculation instead of being
64
+ discarded. The generator is a local NumPy PCG64 generator; the global random
65
+ state is unchanged. With the same environment, seed, dimension and grid, the
66
+ first B draws remain the same when more draws are requested.
67
+
68
+ Empirical quantiles use the median-unbiased convention, equivalent to R's
69
+ `quantile(..., type=8)`. Reference p-values are the fraction of draws at least
70
+ as large as the observed statistic, matching R `aersn` 0.2.3. They do not use
71
+ a plus-one correction. `mcse` is the binomial Monte Carlo standard error, and
72
+ `tail_interval` is a 95 percent binomial interval for the reference tail
73
+ probability. A reported zero p-value does not establish that the true tail
74
+ probability is zero. These quantities describe simulation error, not the
75
+ accuracy of the statistical approximation for the observed data.
76
+
77
+ ```python
78
+ print(ref.quantile([0.90, 0.95, 0.99]))
79
+ print(ref.quantile_mcse([0.90, 0.95, 0.99]))
80
+ ```
81
+
82
+ Quantile standard errors are estimated using interleaved batches. Tail counts
83
+ and interpolated quantiles need not give exactly identical decisions at a
84
+ finite Monte Carlo boundary. The reported `reject` always uses the critical
85
+ value comparison.
86
+
87
+ For a scalar parameter only, an analytic alternative is available:
88
+
89
+ ```python
90
+ ref_continuous = aersn.Reference.continuous()
91
+ print(aersn.scalar_quantile([0.90, 0.95, 0.99]))
92
+ # approximately 1.397390, 1.705776, 2.367378
93
+ ```
94
+
95
+ This is the continuous-path reference, not the matched finite-grid reference.
96
+ `scalar_cdf` and `scalar_quantile` describe the absolute ratio; `scalar_density`
97
+ is the density of the signed ratio. Its density integrates to one over the
98
+ whole real line.
99
+
100
+ ## 3. Confidence regions and linear contrasts
101
+
102
+ At a reference quantile c, the joint region is
103
+
104
+ ```text
105
+ C = estimate + c/sqrt(n) * K.
106
+ ```
107
+
108
+ ```python
109
+ region = fit.region(reference=ref, level=0.95)
110
+ print(region.contains([0, 0]))
111
+ print(region.confint())
112
+ intervals = region.contrast([[1, -1], [0.5, 0.5]])
113
+ print(intervals.estimate, intervals.lower, intervals.upper)
114
+ ```
115
+
116
+ Each contrast interval is `a @ estimate +/- c*range(G @ a)/sqrt(n)`.
117
+ These intervals use the q-dimensional critical value and inherit simultaneous
118
+ coverage from the joint region. They are not tests of a single contrast using
119
+ a one-dimensional critical value. For that different question, first form a
120
+ lower-dimensional target and then generate the corresponding reference:
121
+
122
+ ```python
123
+ target = fit.transform([[1, -1]], names=["difference"])
124
+ target_ref = target.reference(draws=10_000, seed=1)
125
+ target.test(0, reference=target_ref)
126
+ ```
127
+
128
+ `region.contains` permits a default tolerance of 1e-9 in region-gauge units;
129
+ `fit.test` uses the strict critical-value rule.
130
+ Their Boolean answers can differ within numerical rounding distance of a
131
+ boundary. `region.contains(..., tol=0)` removes the membership tolerance but
132
+ does not remove floating-point roundoff. Computed vertices can fall outside
133
+ this tolerance when the estimate is large relative to the region width or
134
+ the path is ill-conditioned; membership at such boundary points is sensitive
135
+ to rounding.
136
+
137
+ For a nonsingular q-by-q transformation H, `fit.transform(H, offset=b)` implements
138
+ `H @ estimate + b` and `psi @ H.T`. The corresponding confidence region is the
139
+ same affine transformation of the original region, up to numerical precision.
140
+ The original reference can then be reused because q and the grid are unchanged.
141
+
142
+ ## 4. Regression
143
+
144
+ ```python
145
+ import numpy as np
146
+ import aersn
147
+
148
+ rng = np.random.default_rng(19)
149
+ x = rng.normal(size=(200, 2))
150
+ y_ols = 1 + x @ [0.5, -0.3] + rng.normal(size=200)
151
+ fit_ols = aersn.ols(y_ols, x) # an intercept is added
152
+ print(fit_ols.names, fit_ols.estimate)
153
+ # For two regressors, select the first slope, retaining nuisance effects:
154
+ slope = fit_ols.transform([[0, 1, 0]], names=["first slope"])
155
+ ```
156
+
157
+ To use a design matrix that already contains an intercept, set `intercept=False`.
158
+ The interface uses QR decomposition and calendar-time centering. It assumes
159
+ the usual asymptotic linear representation and strong identification. Generalized
160
+ method of moments (GMM) and conditional-likelihood convenience interfaces are
161
+ not yet included. Their appropriately constructed influence contributions can
162
+ be supplied through `from_influence`; the package does not fit those models.
163
+
164
+ ## 5. Two- and three-dimensional figures
165
+
166
+ ```python
167
+ import numpy as np
168
+ import aersn
169
+
170
+ data3 = np.random.default_rng(21).normal(size=(200, 3))
171
+ fit3 = aersn.mean(data3, names=["Mean 1", "Mean 2", "Mean 3"])
172
+ ref3 = fit3.reference(draws=2_000, seed=7)
173
+ region3 = fit3.region(reference=ref3)
174
+ ax = region3.plot()
175
+ ax.figure.savefig("region.png", dpi=180)
176
+ ```
177
+
178
+ For a coordinate projection, specify two or three coordinate indices. For
179
+ example, project this three-dimensional region onto its first and third axes:
180
+
181
+ ```python
182
+ ax = region3.plot(coords=[0, 2]) # Python uses zero-based indices
183
+ vertices = region3.projection([0, 1, 2])
184
+ ```
185
+
186
+ This is a projection of the full joint region, not a slice holding other
187
+ parameters fixed. Axis labels use the parameter names. Boundaries come from
188
+ actual hull vertices, with straight edges and planar faces wherever present.
189
+ No smoothing is applied. There is no general requirement that the region
190
+ become an ellipse or ellipsoid as the sample grows.
191
+ Axes have independent scales by default. `region3.plot(equal_scale=True)` gives
192
+ equal distance per data unit instead. Changing this option changes the display,
193
+ not the numerical region, and can make coefficients with different units harder
194
+ to compare visually.
195
+
196
+ The gauge uses only the path values. Explicit plotting computes differences
197
+ of path-hull vertices; it can be more expensive. A `max_pairs` guard on
198
+ `region.vertices()` prevents unexpectedly large allocations. It does not
199
+ restrict the gauge calculation.
200
+
201
+ ## 6. Nonuniform variance accumulation
202
+
203
+ `from_influence` accepts a function of k/n or a vector of n+1 nodes. This is a
204
+ schematic call: `estimate` and `justified_psi` must come from an appropriate
205
+ model-specific construction, not the sample-mean example above.
206
+
207
+ ```text
208
+ fit = aersn.from_influence(estimate, justified_psi, profile=lambda r: r**3)
209
+ ref = fit.reference(draws=10_000, seed=1)
210
+ ```
211
+
212
+ This interface is appropriate only when the model satisfies a common scalar
213
+ variance-accumulation condition and the estimated path consistently approximates
214
+ the required profile-centered path. The code checks nondecreasing nodes and
215
+ endpoints, not these statistical conditions. In particular, demeaned sample-mean
216
+ contributions sum to zero. Changing the nodes then leaves the fitted path
217
+ unchanged and generally does not construct the required profile-centered path.
218
+ For this reason `mean` and `ols` use calendar centering rather than offering
219
+ a profile argument. Consult the paper's conditional-score construction for an
220
+ example where feasible profile inference is justified.
221
+
222
+ ## 7. Rank, numerical accuracy and reproducibility
223
+
224
+ `fit.hull.rank`, `condition_number` and `coordinate_ranges` describe the path.
225
+ The condition number is the ratio of largest to smallest singular values of
226
+ the original centered path matrix, not of a covariance estimator. Numerical
227
+ rank is assessed after coordinate range scaling. The LP also uses pivoted QR
228
+ to handle unequal units and near collinearity. No ridge or random jitter is
229
+ added to singular paths. Such paths raise `DegeneratePathError`.
230
+
231
+ `scaled_condition_number` reports the condition number after dividing each
232
+ coordinate by its range. Values above 1e8 raise `NumericalError`, even if the
233
+ path has numerical full rank. This conservative double-precision limit does
234
+ not claim an exact error bound. Unit differences alone do not trigger it.
235
+ The recovered LP direction is also checked in the original coordinates.
236
+ When a path is rejected, inspect near-dependent parameters and consider a
237
+ lower-dimensional target only when it is justified by the scientific question.
238
+ No unreliable statistic, confidence region, or reference draw is returned.
239
+
240
+ If `mean`, `ols` or `from_influence` raises during construction, no `fit` object
241
+ exists, so `fit.transform(A)` is not available. With a reliably computed
242
+ full-model estimate and influence contributions obtained separately, construct
243
+ the target directly as `aersn.from_influence(A @ estimate, psi @ A.T)`.
244
+ For OLS, the contributions must come from the full regression and retain
245
+ nuisance-parameter effects; simply dropping regressors changes the model.
246
+ The target path must itself pass the numerical checks, and its statistical
247
+ validity still requires the assumptions stated above.
248
+
249
+ R and Python use different random generators and LP solvers. Equal seed values
250
+ do not yield equal reference draws across languages. The distributed tests
251
+ instead share fixed contributions, paths, directions and reference draws.
252
+ Record the package, NumPy and SciPy versions along with the seed and grid for
253
+ reproducibility. Bitwise agreement across different numerical-library versions
254
+ is not promised.
255
+
256
+ ## API overview
257
+
258
+ | Entry point | Purpose |
259
+ |---|---|
260
+ | `mean(data, names=None)` | Sample-mean estimate and influence path |
261
+ | `ols(y, x, intercept=True, names=None)` | Full OLS coefficient inference |
262
+ | `from_influence(estimate, psi, profile=None, names=None)` | Other estimators |
263
+ | `InfluencePath(psi, profile=None)` | Inspect centered partial sums |
264
+ | `IncrementHull(path)` | Gauge, support and vertices |
265
+ | `simulate_reference(q, n, profile=None, draws=..., seed=...)` | Matched-grid reference |
266
+ | `Reference(q, nodes, draws, batches=20)` | Import known reference draws |
267
+ | `Reference.continuous()` | Scalar continuous-path reference |
268
+ | `fit.test(null, reference=ref)` | Test and Monte Carlo uncertainty |
269
+ | `fit.region(reference=ref)` | Joint confidence region |
270
+ | `fit.confint(reference=ref)` | Simultaneous coordinate intervals |
271
+ | `fit.contrast(A, reference=ref)` | Simultaneous contrast intervals |
272
+ | `fit.transform(A, offset=None)` | Linear reparameterization or lower-dimensional target |
273
+
274
+ Arguments after the first required arguments are generally keyword-only; see
275
+ `help(aersn.ols)` and other docstrings for exact signatures.