Vector-output GEKPLS via PLS2 + multi-output BLUP - #554
Draft
ChrisRackauckas-Claude wants to merge 1 commit into
Draft
Vector-output GEKPLS via PLS2 + multi-output BLUP#554ChrisRackauckas-Claude wants to merge 1 commit into
ChrisRackauckas-Claude wants to merge 1 commit into
Conversation
GEKPLS previously assumed a scalar response: the y_matrix was hardcoded to
(nt, 1), the gradient block to (nt, dim), and the kriging BLUP collapsed to a
single output. This commit generalizes the model to vector-valued targets
f : R^dim -> R^ny while keeping the legacy scalar API unchanged.
Mathematical extensions:
* Taylor augmentation in `_ge_compute_pls`: `_y[bb, k] = y[i, k] +
Σⱼ bb_dx[bb, j] * ∂y_k/∂x_j` for each output dimension k. The local
augmented response becomes a (n_bb, ny) matrix.
* PLS step in `_get_first_singular_vectors_power_method`: extended from
PLS1 (one exact iteration for scalar Y) to NIPALS PLS2 (iterate to
convergence on the leading singular vectors of the cross-covariance) when
Y has multiple columns. The PLS1 path is preserved bit-for-bit. Reference:
Wold, Sjöström & Eriksson (2001), "PLS-regression".
* BLUP in `_reduced_likelihood_function`: under shared θ across outputs the
correlation matrix R and its Cholesky are shared, so β (1, ny) and γ
(nt, ny) are obtained from a single multi-RHS solve. Replaces the
scalar-collapsing `transpose(Q) ⋅ Yt` with `transpose(Q) * Yt`.
Internal representation:
* `y_matrix::Matrix{T}` (nt, ny), `grads::Array{T,3}` (nt, dim, ny),
`beta::Matrix{T}`, `y_mean::Matrix{T}`, `y_std::Matrix{T}` — all carry
one column per output.
* New `scalar_output::Bool` flag so the predict method returns the same
type as the user supplied: scalar for scalar inputs, length-ny `Vector`
for vector inputs.
API:
* `GEKPLS(x_vec, y_vec, grads_vec, ...)` autodetects scalar vs. vector y
from `eltype(y_vec)`. Vector y requires `grads_vec` entries to be
`(ny, dim)` Jacobian matrices (or 1-tuples wrapping them, matching
`Zygote.jacobian` output).
* `update!` mirrors the same auto-detection.
Tests:
* Existing 11 GEKPLS regression tests pass unchanged (bit-for-bit on the
PLS1 path; per-output BLUP modulo float reordering).
* New tests for: vector-output fit quality (Test 12), ny=1 wrapper matches
the legacy scalar surrogate to rtol=1e-10 (Test 13), and `update!` on a
vector-output model (Test 14).
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
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.
Note
Draft — please ignore until reviewed by @ChrisRackauckas.
Summary
GEKPLSpreviously assumed a scalar response:y_matrixwas hardcoded to(nt, 1), gradients to(nt, dim), and the kriging BLUP collapsed to a single output viatranspose(Q) ⋅ Yt. This PR generalizes the model to vector-valued targetsf : R^dim -> R^nywhile keeping the legacy scalar API bit-for-bit unchanged.Motivated by a Discourse thread asking for derivative-informed surrogates that handle vector responses: https://discourse.julialang.org/t/surrogate-with-derivatives/137277/5
Math
Taylor augmentation (
_ge_compute_pls) — the local FOTA response is now a(n_bb, ny)matrix:_y[bb, k] = y[i, k] + Σⱼ bb_dx[bb, j] · ∂y_k/∂x_j
PLS step (
_get_first_singular_vectors_power_method) — the scalar response case (PLS1) is exact after one power iteration; that path is preserved bit-for-bit. For vector response (PLS2) the same body is iterated to convergence following Wold's NIPALS algorithm. Initialy_scoreis the column of Y with the largest variance, matching scikit-learn'sPLSRegressiondefault.Multi-output BLUP (
_reduced_likelihood_function) — under shared θ across outputs the kriging correlation matrix R and its Cholesky factor are shared; β(1, ny)and γ(nt, ny)are then obtained from a single multi-RHS solve. The previoustranspose(Q) ⋅ Yt(dot product, scalar) becomestranspose(Q) * Yt(matrix product).References:
API
The constructor auto-detects shape from
eltype(y_vec):Vector{<:Number}— legacy scalar API, unchanged.Vector{<:AbstractVector{<:Number}}— vector output;grads_vecentries must be(ny, dim)Jacobian matrices (or 1-tuples wrapping them, matchingZygote.jacobianoutput).g(x)returns a scalar for scalar y and a length-nyVectorfor vector y.update!mirrors the same auto-detection.Internal representation
y_matrix::Matrix{T}(nt, ny)grads::Array{T,3}(nt, dim, ny) — stores∂y_k/∂x_jbeta::Matrix{T},gamma::Matrix{T},y_mean::Matrix{T},y_std::Matrix{T}— one column per outputscalar_output::Boolflag so predict returns the same type the user suppliedTests
update!on a vector-output model strictly improves per-output RMSE after additional samples.Also verified locally that the Radials and Kriging algorithm test suites pass (unrelated, sanity check).
Test plan
Pkg.testGEKPLS — 49 tests pass🤖 Generated with Claude Code