Add PLS model inversion (null space) and O-PLS support#470
Merged
Conversation
Given a desired response on the original Y scale, PLS.invert() runs the model backwards to return the minimum-norm input vector that achieves it, plus an orthonormal basis for the (A - r)-dimensional null space of input vectors that all yield the same prediction. Callers can move along that space (null_space_coordinates) to meet secondary criteria without changing the predicted response, and the solution's Hotelling's T2 is reported to flag extrapolation. Reproduces the null-space results of Garcia-Carrion et al. (2025, J. Chemometrics), whose single-response proof identifies this null space with the orthogonal space of an O-PLS model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
A newer ruff release promoted CPY001 from preview to stable. With select=["ALL"], it then fired on ~every file (292 hits) because the repo's "# (c) Kevin Dunn" house notice does not match ruff's default copyright regex, turning the lint gate red on all PRs regardless of their diff. Copyright notices are managed in the repo's own style, not via ruff, so ignore CPY001 to make the gate independent of the installed ruff version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
Adds tests/test_multivariate_inversion.py: reproduces the paper's Case Study 1 (Table 3), checks the invert round-trip, null-space invariance, input coercion, T2 reporting, the unique single-component case, the cheddar cheese hold-out design scenario, and the multi-response null-space dimension on the real LDPE data. Vendors cheddar-cheese.csv as an offline fixture. Also upper-bounds ruff (>=0.11.0,<0.16) in both dev dependency groups. CI's 'uv sync' has no --frozen flag and the old 'ruff>=0.11.0' let it resolve to a newer ruff than the lock, which promoted CPY001 and changed RUF100/ISC handling and turned the lint gate red on every PR. This mirrors the existing mypy upper bound and keeps the gate independent of the latest ruff release. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ale=False) Adds tests for the DataFrame and dict forms of y_desired, the wrong target-count and NaN-target error paths, and the unscaled (scale=False) code path, bringing patch coverage of invert() to full. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
New OPLS class for single-response Orthogonal PLS: fit (Trygg-Wold NIPALS), transform (predictive scores), correct (orthogonal-signal-corrected X), predict, and invert. OPLS.invert() reaches a desired response through one division and returns the orthogonal-space basis; for a single response this is the same set of designs as PLS.invert()'s null space (Garcia-Carrion et al., 2025). Tests confirm O-PLS(1; A-1) reproduces A-component PLS predictions and beta to ~1e-14, the orthogonal scores are Y-orthogonal, and the orthogonal space coincides with the PLS null space (cosine 1.0). Exported from methods.py and the multivariate package; added to the sklearn compatibility checks. Folded into the 1.59.0 changelog entry alongside PLS.invert (same PR / release). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
Adds a scale=False end-to-end test (predict/transform/invert) and a 2-D ndarray Y case, taking _opls.py to full patch coverage. Also broadens the .coveragerc TYPE_CHECKING exclude to match the 'if typing.TYPE_CHECKING:' form the codebase actually uses, so type-only import blocks no longer count as missed lines. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
Adds a two-response design test: seven solvent properties predicting logP and Solubility, inverted toward a target (logP, Solubility). Confirms the null-space dimension is A - rank(Y), the design round-trips to both targets, and moving along the null space leaves both predictions fixed. Vendors solvents.csv as an offline fixture. This is the boundary of the null-space / orthogonal-space equivalence, which is proven for a single response only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsTypY4FcRbrXRxorb9raz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Adds latent-variable model inversion and an O-PLS estimator to
process-improve, motivated by García-Carrión et al., "On the EquivalenceBetween Null Space and Orthogonal Space in Latent Variable Regression Modeling"
(Journal of Chemometrics, 2025, e70057). The paper proves that, for a single
response, the null space from PLS model inversion and the orthogonal space
from O-PLS are the same linear space. This PR brings both capabilities into the
package; it also underpins a companion page in the pid-book.
PLS.invert()- model inversion + null spaceGiven a desired response on the original Y scale,
PLS.invert():will achieve it;
(A - r)-dimensional null space (thefamily of input vectors that all yield the same prediction);
null_space_coordinatesto move along that space and meet secondarycriteria (cost, safety, operability) without changing the prediction;
Verified against the paper's Case Study 1 (Table 3).
OPLS- Orthogonal PLS estimator (single response)Trygg-Wold O-PLS NIPALS: one Y-predictive component plus
n_orthogonal_componentsY-orthogonal components. Methods:fit,transform(predictive scores),
correct(orthogonal-signal-corrected X),predict, andinvert.OPLS.invert()reaches a desired response through a single divisionand returns the orthogonal-space basis.
Tests confirm the paper's results on real and synthetic data:
To' y = 0);(principal cosine = 1.0).
Worked example (cheddar cheese)
The cheddar-cheese dataset (already used elsewhere in the pid-book PLS section)
is vendored as an offline fixture. A test trains on cheeses 5-30 and designs a
chemistry toward each held-out cheese's taste; every design round-trips to its
target, and the more ambitious targets land at higher T2 (a teaching point for
the book).
Housekeeping
CITATION.cffandCHANGELOG.mdupdated in step.
test_multivariate_inversion.py,test_multivariate_opls.py, plusOPLSadded to the sklearn-compatibility checks.ruffandmypyclean; noregressions in the multivariate suite.
uv synchad auto-upgraded ruffpast the lockfile to a version that enabled new rules (
CPY001,RUF100/ISCchanges), reddening lint on every PR. Upper-bounded
ruff<0.16in both devgroups (mirroring the existing
mypypin) and added an explicitCPY001ignore for the repo's house copyright style.
Follow-up (separate repos)
null-space / orthogonal-space equivalence, with a figure in
kgdunn/figures.