Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
08c8237
Expose weighted least squares through PHP Matrix API
ephpicman Oct 22, 2026
edff313
Test PHP weighted least squares contracts
ephpicman Oct 22, 2026
fee4b80
Add executable PHP weighted least squares example
ephpicman Oct 22, 2026
b46edff
Document PHP weighted least squares API
ephpicman Oct 22, 2026
1988e4c
Document PHP weighted least squares API
ephpicman Oct 22, 2026
745fc61
Document weighted least squares PHP workflow
ephpicman Oct 22, 2026
ce3c628
Align WLS milestone with PHP primitive API
ephpicman Oct 22, 2026
08a8832
Fix PHP namespace syntax in WLS files
ephpicman Oct 22, 2026
4db1449
Fix PHP namespace syntax in WLS files
ephpicman Oct 22, 2026
fce7ed9
Strengthen WLS tests for unequal weights and multiple RHS
ephpicman Oct 22, 2026
a9451e5
Run weighted least-squares tests under sanitizers
ephpicman Oct 22, 2026
98cfd34
Cover WLS RHS shape and non-finite matrix data
ephpicman Oct 22, 2026
0f558e7
Test unequal weights through PHP API
ephpicman Oct 22, 2026
0249aa7
Fix WLS PHPT expected output count
ephpicman Oct 22, 2026
5c1a2ba
Make WLS example an executable API smoke test
ephpicman Oct 22, 2026
99f9213
Document WLS example alongside OLS smoke test
ephpicman Oct 22, 2026
3553e64
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
9ca2ab2
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
6a8f25f
Prepare extension version 1.1.0
ephpicman Oct 22, 2026
a872861
Add 1.1.0 release notes
ephpicman Oct 22, 2026
1175811
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
2434342
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
4c24728
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
ff2b541
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
ad27ddb
Verify WLS in release source archive smoke test
ephpicman Oct 22, 2026
5af36f8
Prepare documentation for Numerus 1.1.0
ephpicman Oct 22, 2026
afb33af
Test WLS example end to end
ephpicman Oct 22, 2026
f42c618
Test WLS named argument metadata
ephpicman Oct 22, 2026
5fe3384
Add WLS 1.1 release gate audit
ephpicman Oct 22, 2026
d7ad1ef
Finalize 1.1 release documentation and smoke tests
ephpicman Oct 22, 2026
f432f87
Finalize 1.1 release documentation and smoke tests
ephpicman Oct 22, 2026
44fefbf
Finalize 1.1 release documentation and smoke tests
ephpicman Oct 22, 2026
81851ee
Finalize 1.1 release documentation and smoke tests
ephpicman Oct 22, 2026
2ebf913
Make WLS guide example self-contained
ephpicman Oct 22, 2026
316c08c
Update module version assertion for 1.1.0
ephpicman Oct 22, 2026
54262c8
Align release policy with the 1.1 minor release
ephpicman Oct 22, 2026
2185439
Align release policy with the 1.1 minor release
ephpicman Oct 22, 2026
d54a1e5
Index the 1.1 release audit
ephpicman Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/source-archive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,22 @@ jobs:
working-directory: ${{ env.archive_path }}
run: make test TESTS='tests/*.phpt'

- name: Verify extension version and OLS example
- name: Verify extension version, OLS, and WLS examples
working-directory: ${{ env.archive_path }}
shell: bash
run: |
set -euo pipefail
php -d extension="$(pwd)/modules/numerus.so" -r '
if (phpversion("numerus") !== "1.0.0") {
fwrite(STDERR, "Expected Numerus 1.0.0, got " . var_export(phpversion("numerus"), true) . PHP_EOL);
if (phpversion("numerus") !== "1.1.0") {
fwrite(STDERR, "Expected Numerus 1.1.0, got " . var_export(phpversion("numerus"), true) . PHP_EOL);
exit(1);
}
'
output="$(php -d extension="$(pwd)/modules/numerus.so" examples/ols.php)"
printf '%s\n' "$output"
grep -Fxq 'intercept: 1.000000' <<< "$output"
grep -Fxq 'slope: 2.000000' <<< "$output"
wls_output="$(php -d extension="$(pwd)/modules/numerus.so" examples/wls.php)"
printf '%s\n' "$wls_output"
grep -Fxq 'intercept: 1.000000' <<< "$wls_output"
grep -Fxq 'slope: 1.000000' <<< "$wls_output"
8 changes: 8 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,14 @@ jobs:
-o tests/matrix_cholesky_sanitized tests/matrix_cholesky_test.c src/matrix/numerus_matrix_cholesky.c src/matrix/numerus_matrix.c src/matrix/numerus_matrix_view_nodes.c src/matrix/numerus_matrix_constructors.c src/storage/numerus_storage.c src/matrix/numerus_matrix_lu.c -lm
ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 UBSAN_OPTIONS=halt_on_error=1 ./tests/matrix_cholesky_sanitized

- name: Run weighted least-squares tests with AddressSanitizer and UBSan
run: |
cc -Isrc/core -Isrc/storage -Isrc/matrix -Isrc/statistics -std=c11 -g -O1 -Wall -Wextra -Wpedantic -Werror \
-fsanitize=address,undefined -fno-omit-frame-pointer \
-DNUMERUS_MATRIX_USE_LIBC_ALLOC -DNUMERUS_STORAGE_USE_LIBC_ALLOC \
-o tests/matrix_weighted_least_squares_sanitized tests/matrix_weighted_least_squares_test.c src/matrix/numerus_matrix_weighted_least_squares.c src/matrix/numerus_matrix_least_squares.c src/matrix/numerus_matrix_qr.c src/matrix/numerus_matrix.c src/matrix/numerus_matrix_view_nodes.c src/matrix/numerus_matrix_constructors.c src/storage/numerus_storage.c src/matrix/numerus_matrix_lu.c -lm
ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 UBSAN_OPTIONS=halt_on_error=1 ./tests/matrix_weighted_least_squares_sanitized

- name: Run Matrix QR tests with AddressSanitizer and UBSan
run: |
cc -Isrc/core -Isrc/storage -Isrc/matrix -Isrc/statistics -std=c11 -g -O1 -Wall -Wextra -Wpedantic -Werror \
Expand Down
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

Notable user-facing changes are recorded here. This project follows Semantic Versioning for its public PHP API.

## 1.1.0 — Unreleased

### Added
- Public `Matrix::weightedLeastSquares(Matrix $rightHandSide, array $weights): Matrix` API for weighted least-squares solving.
- WLS uses square-root row scaling and the existing pivoted-QR least-squares solver; it does not form weighted normal equations.
- Support for multiple right-hand-side columns and zero-weight observations.
- PHP and native regression tests for equal/unequal/zero weights, invalid weights, dimension mismatches, non-finite values, and solver failure cases.
- A runnable, checked WLS example and updated user/API documentation.

### Numerical and compatibility notes
- Weights must be numeric, finite, non-negative, and supplied one per observation. A zero weight removes that observation's contribution to the objective, but input matrix values are still validated by the solver.
- The square-root-scaled design matrix must have full column rank. Rank-deficient and underdetermined weighted designs are rejected; minimum-norm solutions are not exposed through the PHP API.
- WLS materializes scaled design and response matrices, using additional memory proportional to their sizes.
- OLS remains available through `leastSquares()`; neither `fit_wls()` nor other model-specific estimator APIs are introduced.
- Linux CI targets PHP 8.2–8.5. Windows build configuration exists, but Windows support remains unverified because Windows build/test CI is not yet present.

## 1.0.0

### Added
Expand All @@ -17,4 +33,3 @@ Notable user-facing changes are recorded here. This project follows Semantic Ver
- Linux CI covers PHP 8.2–8.5, including debug builds, PHPTs, native tests, and native coverage.
- Windows build configuration exists, but Windows support remains unverified because Windows build/test CI is not yet present.
- Weighted least squares (WLS), generalized least squares (GLS), model inference, and MLE-enabling PHP workflows are outside the 1.0 scope and remain roadmap work.

13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
[![Native coverage](https://github.com/ephpicman/Numerus/actions/workflows/coverage.yml/badge.svg?branch=main)](https://github.com/ephpicman/Numerus/actions/workflows/coverage.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Numerus is a native PHP extension that provides reusable numerical-computing primitives. Its current PHP-facing API focuses on matrix operations that can be composed into workflows such as ordinary least squares (OLS).
Numerus is a native PHP extension that provides reusable numerical-computing primitives. Its current PHP-facing API focuses on matrix operations that can be composed into ordinary least squares (OLS) and weighted least squares (WLS) workflows.

The project intentionally exposes general-purpose primitives rather than model-specific functions such as `fit_ols()`, `fit_wls()`, `fit_gls()`, or `fit_mle()`.

## Current status

The initial PHP API and executable OLS workflow are implemented. The 1.0 release gate is tracked in the [release roadmap](docs/release-roadmap.md) and [PHP OLS release-gate audit](docs/php-api-release-audit.md). Linux CI currently covers PHP 8.2–8.5, a debug build, PHPTs, native tests, and native line/branch coverage. Windows build configuration exists, but Windows support remains unverified because Windows build/test CI is not yet present.
The 1.0 OLS foundation is established, and the 1.1 release candidate adds a tested PHP weighted least-squares (WLS) primitive. The 1.1 acceptance criteria are tracked in the [release roadmap](docs/release-roadmap.md) and [WLS release-gate audit](docs/php-api-wls-release-audit.md). Linux CI currently covers PHP 8.2–8.5, a debug build, PHPTs, native tests, and native line/branch coverage. Windows build configuration exists, but Windows support remains unverified because Windows build/test CI is not yet present.

## Quick start

Expand Down Expand Up @@ -75,10 +75,11 @@ The PHP-facing API currently includes:
- `rows()`, `columns()`, and zero-based `get($row, $column)`
- `transpose()`, `multiply(Matrix $other)`, and `inverse()`
- `leastSquares(Matrix $rightHandSide)`, using pivoted QR
- `weightedLeastSquares(Matrix $rightHandSide, array $weights)`, using square-root row scaling and pivoted QR

The least-squares operation supports one or more right-hand-side columns and requires a full-column-rank design matrix. Rank-deficient and underdetermined inputs are rejected explicitly; the initial PHP API does not expose a minimum-norm solution. Prefer `leastSquares()` to explicitly forming normal equations: forming (X^T X) squares the condition number, and explicitly forming an inverse can amplify rounding error.

See the [PHP API reference](docs/php-api.md) for exact contracts and exceptions, the [user guide](docs/user-guide.md) for examples and limitations, and [examples/ols.php](examples/ols.php) for the executable workflow.
Run the checked WLS example with `php -d extension="$(pwd)/modules/numerus.so" examples/wls.php`. See the [PHP API reference](docs/php-api.md) for exact contracts and exceptions, the [user guide](docs/user-guide.md) for examples and limitations, and [examples/ols.php](examples/ols.php) / [examples/wls.php](examples/wls.php) for executable OLS and WLS workflows.

## Architecture

Expand Down Expand Up @@ -149,6 +150,7 @@ The allocator defines are for standalone test executables only; the extension bu
- `src/statistics/` — RNG, sampling, probability, and optimizer primitives.
- Root-level C headers are forwarding includes retained for existing native test include paths; declarations and implementations live under `src/`.
- `examples/ols.php` — runnable PHP OLS coefficient workflow.
- `examples/wls.php` — runnable PHP WLS coefficient workflow.
- `tests/` — PHPT and native C tests.
- `.github/workflows/` — build/test matrix and native coverage workflows.

Expand All @@ -166,11 +168,12 @@ The allocator defines are for standalone test executables only; the extension bu

Start at the [documentation index](docs/README.md):

- [User guide](docs/user-guide.md) — installation, current PHP API, OLS example, and limitations.
- [User guide](docs/user-guide.md) — installation, current PHP API, OLS/WLS examples, and limitations.
- [Developer guide](docs/developers/README.md) — architecture, testing, contribution workflow, and release engineering.
- [PHP API reference](docs/php-api.md) — exact public method contracts.
- [Release roadmap](docs/release-roadmap.md) — user-facing milestones and acceptance criteria.
- [Release-gate audit](docs/php-api-release-audit.md) — evidence and remaining limitations for 1.0.
- [1.0 OLS release-gate audit](docs/php-api-release-audit.md) — historical evidence for the 1.0 foundation.
- [1.1 WLS release-gate audit](docs/php-api-wls-release-audit.md) — acceptance evidence and remaining release checks.

The user guide documents only capabilities exposed through PHP. Internal C primitives and planned roadmap items are labelled separately and should not be mistaken for shipped PHP APIs.

Expand Down
5 changes: 4 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ Numerus is a native PHP extension providing composable numerical-computing primi

## User guide

- [User guide](user-guide.md) — requirements, build instructions, Matrix API, OLS example, errors, and current limitations.
- [User guide](user-guide.md) — requirements, build instructions, Matrix API, OLS/WLS examples, errors, and current limitations.
- [PHP API reference](php-api.md) — currently exposed PHP methods.
- [Runnable OLS example](../examples/ols.php) — executable example source.
- [Runnable WLS example](../examples/wls.php) — executable example source.

## Developer and contributor guide

Expand All @@ -25,6 +26,8 @@ These documents describe implementation details and internal C APIs. They are no
- [Native test coverage](native-test-coverage.md)
- [Statistical API boundary](statistical-api-boundary.md)
- [Release roadmap](release-roadmap.md)
- [PHP 1.1 WLS release-gate audit](php-api-wls-release-audit.md)
- [Historical PHP 1.0 OLS release-gate audit](php-api-release-audit.md)
- [Statistical foundation roadmap](v1-statistical-foundation-roadmap.md)

## Documentation rule
Expand Down
12 changes: 6 additions & 6 deletions docs/developers/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Use Semantic Versioning for the public PHP API:
- **Minor**: backward-compatible new public capabilities.
- **Major**: intentional incompatible changes to the public API or documented behavior.

Before 1.0, public API contracts may still evolve; nevertheless, avoid gratuitous breaking changes and document intentional ones. A 0.x version should not be represented as a stable 1.0 contract.
After 1.0, the public API is a compatibility commitment. Avoid gratuitous breaking changes; document intentional behavior changes and treat parameter names, error behavior, and numerical semantics as part of the contract.

Keep the extension version macro in `php_numerus.h`, Git tag, release notes, and any package metadata synchronized. Do not bump versions merely because internal files were reorganized unless a release is being cut.

Expand All @@ -19,7 +19,7 @@ Keep the extension version macro in `php_numerus.h`, Git tag, release notes, and
- [ ] Ensure public API documentation matches registered methods and PHPT contracts.
- [ ] Run PHPT and native C tests.
- [ ] Verify required CI jobs on the exact release commit.
- [ ] Verify the **Source archive smoke test** passes on the exact release commit. This workflow creates an archive from tracked Git files, extracts it without a working `.git` directory, builds the extension, runs PHPTs, verifies the extension version, and executes the OLS example.
- [ ] Verify the **Source archive smoke test** passes on the exact release commit. This workflow creates an archive from tracked Git files, extracts it without a working `.git` directory, builds the extension, runs PHPTs, verifies the extension version, and executes both OLS and WLS examples.
- [ ] Review supported PHP versions and state unverified platforms honestly (Windows is currently unverified).
- [ ] Review ownership, error behavior, numerical limitations, and known issues.
- [ ] Verify extension version, Git tag, release notes, and package metadata agree.
Expand All @@ -30,9 +30,9 @@ Keep the extension version macro in `php_numerus.h`, Git tag, release notes, and

1. Merge release-preparation changes only after the regular test, native coverage, and source-archive workflows pass.
2. Confirm the merged commit on `main` has all three workflows green. Do not tag a PR head or a commit whose checks are still running.
3. Review the final diff and verify `PHP_NUMERUS_VERSION` and the `CHANGELOG.md` release entry both describe `1.0.0`.
4. Create an annotated Git tag named `1.0.0` on that exact green `main` commit.
5. Create a GitHub Release from tag `1.0.0`, using the matching changelog entry and including the source-build prerequisites and the Windows support caveat.
3. Review the final diff and verify `PHP_NUMERUS_VERSION` and the `CHANGELOG.md` release entry both describe `1.1.0`.
4. Create an annotated Git tag named `1.1.0` on that exact green `main` commit.
5. Create a GitHub Release from tag `1.1.0`, using the matching changelog entry and including source-build prerequisites, WLS numerical limitations, and the Windows support caveat.
6. Verify that the release page and auto-generated source archives are available and correspond to the tagged commit.

Do not create a tag or publish a release while any required workflow is pending or failing. This repository currently has no package manifest for a registry-specific distribution; do not imply PIE, Packagist, or PECL installation support unless that distribution is separately implemented and verified.
Expand All @@ -45,4 +45,4 @@ The packaging choice should not change release milestone scope or force a major-

## No premature stable release

Do not label Numerus 1.0.0 stable until the documented 1.0 gate has passed. A green build is necessary but not sufficient: the PHP API must be coherent, tested, documented, and usable in a clean installation workflow.
Do not publish a release until its documented milestone gate has passed. A green build is necessary but not sufficient: the PHP API must be coherent, tested, documented, and usable in a clean installation workflow. For 1.1.0, also verify the WLS example and WLS PHPT from a clean source archive.
32 changes: 32 additions & 0 deletions docs/php-api-wls-release-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# PHP 1.1 WLS Release-Gate Audit

This audit tracks the user-facing 1.1 milestone in the [release roadmap](release-roadmap.md). The prior [1.0 OLS audit](php-api-release-audit.md) remains historical evidence for the initial API.

## Acceptance evidence

| Gate | Evidence | Assessment |
|---|---|---|
| Public PHP API | `Matrix::weightedLeastSquares(Matrix $rightHandSide, array $weights): Matrix` is registered with named-argument metadata | Implemented; named arguments are covered by `tests/013-weighted-least-squares-named-arguments.phpt` |
| Numerical algorithm | Native implementation scales design and RHS rows by square-root weights and delegates to pivoted QR; it does not form `XᵀWX` | Implemented |
| OLS equivalence | Equal weights reproduce `leastSquares()` on a deterministic fixture | Covered by `tests/011-weighted-least-squares.phpt` |
| Weight semantics | Unequal weights, zero weight, negative weight, non-finite weight, wrong count, and non-numeric type | Covered by PHP and native WLS tests |
| Shape and data validation | Multiple RHS, RHS row mismatch, non-finite matrix values, infinite weights, and rank/solver failures | Covered by native tests and PHP-facing validation paths |
| Runnable user workflow | `examples/wls.php` checks expected coefficients; `tests/012-wls-example.phpt` executes it end to end | Implemented |
| User-facing documentation | README, user guide, API reference, documentation index, changelog, and roadmap describe WLS and its limits | Updated for 1.1 |
| Version and clean archive | `php_numerus.h` identifies version 1.1.0; source-archive workflow verifies that version and runs OLS and WLS examples | Workflow updated; final result must be green on the exact release commit |
| Supported Linux CI | PHP 8.2–8.5 matrix, debug build, PHPT suite, native tests, sanitizer-oriented jobs, native coverage | Must be verified on the exact release-preparation commit and again after merge to `main` |
| Windows | `config.w32` exists, but Windows build/test CI is not present | Unverified; do not claim Windows support is validated |

## Public contract and known limitations

- The design matrix has shape `n × p`; the RHS has `n` rows and may contain one or more columns. The result has shape `p × k`.
- Supply exactly one PHP integer or float weight per observation. Weights must be finite and non-negative. Numeric strings are rejected.
- A zero weight removes that observation's contribution to the weighted objective. The current implementation still validates matrix entries in zero-weight rows; zero weight does not promise to ignore non-finite input data.
- The square-root-scaled design must have full column rank. Rank-deficient and underdetermined problems are rejected; the PHP API does not return a minimum-norm solution.
- The native implementation materializes scaled design and RHS matrices, requiring additional memory proportional to their sizes.
- Numerical solver failures are reported as exceptions; invalid dimensions and negative weights are reported as `ValueError`, non-numeric PHP weight values as `TypeError`, and non-finite weights as numerical exceptions.
- This release adds a reusable matrix primitive, not a model-specific `fit_wls()` API. GLS, inference workflows, and MLE workflows remain outside the 1.1 scope.

## Release decision

**Status: release preparation; not yet published.** Do not create tag `1.1.0` or publish a GitHub Release until the source-archive smoke test, the PHP 8.2–8.5 test matrix, native coverage, and post-merge `main` checks are green on the exact intended release commit. Review the final diff and changelog before tagging.
Loading
Loading