Estimation Methods¶
This page describes fitting methods exposed by pyscarcopula.api.fit,
including the static multivariate model contract. Performance controls are covered in
Performance Tuning.
For the compact formulas behind each dynamic model, including the transfer filters, predictive Rosenblatt transform, and numerical convergence criteria, see Mathematical Contracts.
Each estimation method supports a defined set of model families. Unsupported combinations fail before optimization starts.
Model and method compatibility¶
| Model / correlation policy | MLE | GAS | SCAR-TM-OU | SCAR-TM-JACOBI |
|---|---|---|---|---|
| Gumbel, Clayton, Frank, Joe, bivariate Gaussian | Yes | Yes | Yes | Yes |
| Static Gaussian: fixed, shrinkage, cholesky, factor two-stage | Yes | No | No | No |
| Static Student: fixed, shrinkage, cholesky, factor two-stage or joint | Yes | No | No | No |
| Equicorrelation Gaussian | Yes | Yes | Yes | No |
| Stochastic Student: fixed, shrinkage, factor two-stage | Yes | Yes | Yes | No |
| Stochastic Student: cholesky | Yes | No | Yes | No |
| Stochastic Student: factor joint | Yes | No | No | No |
| VineCopula: supported pair families on each active edge | Yes | Yes | Yes | Yes |
Independence has no fitted dependence parameter; independent vine edges stay
constant instead of running a dynamic optimizer. Jacobi evolves positive
family-scale tau; rotations determine the corresponding observed direction.
Gaussian and Frank use their positive-dependence branch for Jacobi.
For factor joint fits, d >= 2*k + 1 is required. Unknown Python subclasses
cannot acquire native support through inheritance.
Start with MLE, inspect the result, then select dynamics. Vine family selection uses an MLE baseline before any dynamic edge refit. See Vine Copulas for truncation and dynamic-failure policies.
Method Summary¶
| Method | Key | State | Main use |
|---|---|---|---|
| MLE | 'mle' |
Static/constant model parameters | Baseline fit, family selection, and static multivariate fitting |
| GAS | 'gas' |
Observation-driven score recursion | Fast dynamic dependence without latent integration |
| SCAR-TM-OU | 'scar-tm-ou' |
OU latent state mapped to bivariate dependence or Student degrees of freedom | Deterministic stochastic-latent likelihood |
| SCAR-TM-JACOBI | 'scar-tm-jacobi' |
Jacobi diffusion for Kendall's tau | Bounded tau dynamics with deterministic filtering |
GAS returns GASResult; SCAR methods return LatentResult. Both contain
params, log_likelihood, optimizer status, and fitted settings for
predict, predictive_mean, and GoF. See
Configuration and Results for result
types and their common and method-specific fields. Model sampling is available where the
strategy implements a path simulator. SCAR-TM-JACOBI supports both
unconditional sample and conditional or unconditional predict.
Its default unconditional sampler is the likelihood-consistent
sampling_method='tm_grid'; experimental sampling_method='lamperti_euler'
is available as an opt-in continuous-path approximation.
Gradient capability matrix¶
The model score used inside a recursion and the optimizer gradient are different quantities. The following table describes what is passed to the outer optimizer and the corresponding diagnostics.
| Method | Configuration | Optimizer gradient | model_score |
gradient_kind |
|---|---|---|---|---|
| MLE | Built-in supported model | Analytical | not_applicable |
analytical |
| GAS | Any supported scaling | Native finite differences | native |
native_finite_difference |
| SCAR-TM-OU | analytical_grad=True |
Analytical native Jacobian | not_applicable |
analytical |
| SCAR-TM-OU | analytical_grad=False |
Numerical finite differences | not_applicable |
numerical |
| SCAR-TM-JACOBI | analytical_grad=False |
Numerical finite differences | not_applicable |
numerical |
| SCAR-TM-JACOBI | local_fixed, analytical gradient |
Model-provided | not_applicable |
analytical |
| SCAR-TM-JACOBI | local, spectral_matrix, or auto, analytical gradient |
Model-provided | not_applicable |
semi_analytical |
| SCAR-TM-JACOBI | spectral_coeff, analytical_grad=True |
Native finite differences | not_applicable |
native_finite_difference |
For joint Stochastic Student SCAR-TM-OU fits, the analytical-gradient path
includes OU and static-correlation derivatives. Result diagnostics report the
correlation and joint-gradient routes in correlation_gradient and
joint_gradient. Some fits also include
prepared_native_evaluator, prepared_native_evaluator_count, and
prepared_native_fallback.
For SCAR-TM-OU and SCAR-TM-JACOBI,
result.diagnostics["initialization"] records how the optimizer initial point
was obtained. It contains requested_method, selected_method, the final
alpha0, and an ordered attempts list. Failed attempts retain only a
serializable error_type and error_message; traceback and exception objects
are not stored. A user-provided alpha0 is reported as user_provided.
MLE¶
MLE is the label for a static model. For bivariate scalar families it estimates one constant copula parameter and remains the default baseline for family screening and dynamic initialization. For multivariate Gaussian and Student models, the correlation procedure is an independent policy choice: an MLE-labelled result can use supplied, plug-in, or jointly optimized correlation.
The optimizer works directly in the natural copula parameter. An explicit
alpha0 therefore uses natural units:
from pyscarcopula import GumbelCopula
from pyscarcopula.api import fit
copula = GumbelCopula(rotate=180)
result = fit(copula, u, method='mle', alpha0=[2.0])
For static and stochastic Student copulas fitted with method='mle', the
optimized scalar is df itself and is constrained above the model's
finite-variance threshold. No latent softplus transform is applied inside the
MLE objective. The softplus transform remains part of dynamic SCAR/GAS models,
where a latent state drives time-varying degrees of freedom.
For static GaussianCopula and StudentCopula:
corr_mode |
Optimizer treatment | Intended use |
|---|---|---|
fixed |
No correlation optimizer coordinates; supplied R is fixed, otherwise correlation is a counted plug-in estimate |
Default compatibility and fast baseline |
shrinkage |
Joint analytical correlation score for one raw weight | Parsimonious joint static fit |
cholesky |
Joint analytical score for d*(d-1)/2 raw coordinates |
Small dimensions |
factor |
Two-stage compact loadings; static Student also supports joint identified loadings | Large dimensions under a factor assumption |
The result fields corr_n_params, corr_plugin_n_params, and
corr_effective_n_params separate optimizer coordinates from data-derived
plug-in parameters. corr_estimator records the actual procedure. Thus
fixed alone is not enough to tell whether correlation was supplied or
estimated.
For static and stochastic Student models with corr_mode='fixed', the analytical df
derivative is passed to L-BFGS-B. Joint df and estimated-correlation MLE
combines that derivative with the Student correlation score, then maps it into
the
configured shrinkage or Cholesky raw parameters. It reports
gradient_mode='analytical_joint' in result diagnostics.
GAS¶
GAS is observation-driven. The copula parameter is
where the unbounded recursion state follows
The score \(s_t\) is the scaled derivative of the current copula log-density with respect to the recursion state. Conditional on past data, GAS has a point state rather than a latent-state distribution, so its likelihood avoids the latent-state integration required by SCAR.
result = fit(
copula,
u,
method='gas',
scaling='unit',
ftol=1e-12,
maxfun=3000,
)
GAS uses the compiled evaluator for likelihood, score recursion, state updates, prediction, and the bivariate Rosenblatt path for supported built-in copulas. Unsupported copulas and missing compiled support fail immediately.
Use scaling='unit' as the numerical baseline. scaling='fisher' uses the
analytical copula score as its numerator and a finite-difference curvature
estimate with clipping/floor thresholds; its fitted optimum can be sensitive
to optimizer finite-difference steps.
The GAS copula score and filtering recursion are model calculations.
They are not the optimizer Jacobian with respect to
(omega, gamma, beta). The compiled evaluator computes that outer gradient
with two-point finite differences matching SciPy's forward-step and bound
adjustment conventions, and returns the objective and gradient through
one L-BFGS-B callback. Result diagnostics distinguish these concepts with
model_score='native', optimizer_gradient='native', and
gradient_kind='native_finite_difference'. maxfun and nfev use scalar
objective budget units, including numerical-gradient probes: four units
per native provider call, or five for joint GAS/shrinkage fitting. These equal
the scalar evaluation counts when the finite-difference call completes.
A numerical failure can abort the native call earlier while still charging
the full budget units, so nfev is not a physical likelihood-call counter.
An explicit finite_diff_rel_step takes precedence over the absolute eps step.
SCAR-TM-OU¶
SCAR-TM-OU uses a one-dimensional Ornstein-Uhlenbeck latent state,
What the state controls depends on the model. For a bivariate copula it is
mapped to that family's scalar dependence parameter. For
StochasticStudentCopula it is mapped to the time-varying degrees of freedom;
the multivariate correlation structure remains a separate static component.
The likelihood integrates the latent Markov path deterministically. The bivariate and Stochastic Student models use the same OU transition backends, but they do not use the same optimizer coordinates by default.
SCAR-TM-OU requires the package C++ extension; no Python fallback implements this public fitting path.
result = fit(
copula,
u,
method='scar-tm-ou',
transition_method='auto',
analytical_grad=True,
)
The public OU parameters are always kappa, mu, and nu. A user-supplied
alpha0 must use these physical coordinates in that order.
Bivariate copulas¶
For a bivariate family \(C\), the dynamic copula parameter is
Thus SCAR-TM-OU describes stochastic dependence: examples include a dynamic Gumbel parameter and a dynamic Gaussian correlation. The transform \(\Psi_C\) and its admissible range are family-specific.
By default, the bivariate optimizer uses scaled physical
[kappa, mu, nu] coordinates. Set
log_stationary_scale_optimization=True to optimize instead in
Stochastic Student copula¶
For StochasticStudentCopula, the OU state instead controls tail thickness:
The correlation structure remains static. This model uses the log coordinates
above by default. Set log_stationary_scale_optimization=False to use scaled
physical [kappa, mu, nu] coordinates instead.
Shared transition likelihood¶
The exact OU one-step transition is Gaussian, so all SCAR-TM-OU likelihood backends evaluate the same latent Markov model. They differ only in how the one-dimensional transition integral is approximated: Hermite spectral projection, a finite-grid transition matrix, or local Gauss-Hermite quadrature.
By default, transition_method='auto' uses a Hermite spectral likelihood
except in narrow-kernel regimes, where it uses local Gauss-Hermite. If spectral
evaluation fails numerically, auto tries the matrix grid likelihood first
and then the local method when the matrix path is not accepted.
LatentResult.diagnostics records objective evaluations,
spectral/matrix/local attempts, and transition fallback counters such as
fallback_spectral_to_matrix,
fallback_matrix_to_local, matrix_failures, and matrix_capped.
By default, spectral_basis_order='auto' selects the Hermite basis size inside
each objective evaluation from the current kappa / (T - 1): 128 below
0.015, 96 below 0.025, 64 below 0.06, and 32 otherwise. Use a fixed
positive integer when exact basis-size reproducibility is needed for numerical
comparisons.
SCAR-TM-JACOBI¶
SCAR-TM-JACOBI evolves Kendall's tau directly on (0, 1):
The copula parameter is recovered from tau through the copula's
tau_to_param mapping. This method is therefore available for copulas that
implement both tau_to_param and param_to_tau: Gumbel, Clayton, Frank,
Joe, and bivariate Gaussian copulas.
result = fit(
copula,
u,
method='scar-tm-jacobi',
transition_method='auto',
)
print(result.params.kappa, result.params.m, result.params.xi)
The fitted parameters are:
kappa: mean-reversion speedm: long-run Kendall's tau levelxi: Jacobi volatility
The Jacobi stationary law is beta, and the transition operators are built in tau space rather than in an unconstrained OU coordinate. The local backend uses the Lamperti coordinate to apply Gauss-Hermite steps while keeping tau inside its bounded state space.
transition_method='auto' first tries a Jacobi spectral transition matrix on
the tau quadrature grid. If the truncated spectral matrix has material
negative mass, or if row normalization fails numerically, it falls back to the
local Lamperti/Gauss-Hermite transition. The default tolerance for accepting
small spectral truncation errors is negative_mass_tol=1e-5.
The explicit Jacobi transition backends are spectral_matrix, local,
local_fixed, and spectral_coeff. With analytical_grad=True, the optimizer
receives a model-provided Jacobian. For local_fixed, both setup and filtering
derivatives are analytical. For local, spectral_matrix, and either backend
selected by auto, setup arrays are differentiated by finite differences and
the filtering recursion is differentiated analytically; these modes are
therefore semi-analytical. spectral_coeff is a coefficient-space comparison
backend. With analytical_grad=True, it supplies a complete native central
finite-difference objective gradient and reports
gradient_kind='native_finite_difference'; it is not an analytical derivative.
Jacobi fitting requires at least two observations because its transition time
step is dt = 1 / (T - 1). A one-row prepared evaluator remains valid for
conditioning an existing state, which does not construct a transition.
LatentResult.diagnostics reports gradient_requested, gradient_used,
gradient_kind, setup_derivative, filter_derivative, and the transition
backend actually selected at the fitted parameters.
The fitted result also retains every Jacobi option that changes subsequent
likelihood or prediction semantics: transition_method, gh_order,
spectral_basis_order, spectral_quad_order, tau_eps, theta_cap,
clip_negative, negative_mass_tol, stationary_shape_max,
transition_storage, stationarity_correction,
sampling_method, lamperti_substeps, lamperti_boundary, lamperti_eps,
lamperti_engine, lamperti_chunk_observations, and a configured
memory_budget_bytes. Stateless API calls and models restored from JSON
reconstruct the strategy from these fields. Explicit kwargs passed to a later
API call still override the stored values.
Unconditional sampling defaults to the transition-matrix grid used by the
likelihood. An experimental sampling_method='lamperti_euler' instead
simulates a continuous tau path with substepped Euler--Maruyama in the
Lamperti coordinate. It is useful as an independent discretization comparison
but does not change the fitting backend and is not an exact diffusion sampler.
Its mandatory native C++ kernel is strictly sequential and consumes Gaussian
innovations created by the caller's NumPy generator in bounded chunks. Legacy
lamperti_engine='numba' and 'python' labels normalize to 'native'; they
do not select separate production implementations.
For an explicit moving-grid local transition,
transition_storage='sparse' selects the O(K * gh_order) sparse filtering
and prediction backend. The default is 'dense'; sparse storage is not
currently available for auto or spectral transitions. Explicit
local_fixed supports sparse analytical-gradient filtering with shared
transition/derivative indices.
stationarity_correction='mh' and stationarity_correction='ipfp' are
experimental sparse-only options applied consistently to likelihood,
prediction, state filtering, and tm_grid sampling for the moving-grid
local backend. IPFP preserves the original sparse support and therefore
fails explicitly if that support cannot be balanced to both stationary
marginals. Neither correction is available for local_fixed; uncorrected
sparse local_fixed supports analytical_grad=True.
adaptive_quad_order=True is an experimental option for the uncorrected
sparse moving-grid local backend. It selects a quadrature order once, from
adaptive_quad_orders, using deterministic full-horizon gates before
optimization. The chosen order remains fixed for likelihood, prediction, and
sampling and is persisted in the fitted result. The final-parameter gate is
diagnostic only and never changes the fitted order.
For high-frequency data, the code uses dt = 1 / (T - 1). Large T therefore
produces very narrow one-step Jacobi transitions. In this regime the local
transition produces a nonnegative row-normalized matrix. Change
basis_order only when comparing the spectral approximation against the local
backend; otherwise leave backend selection to transition_method='auto'.
Fit options and initialization¶
Built-in strategies separate constructor settings from arguments to each
operation. For example, K configures SCAR-TM-OU numerical evaluation, while
alpha0, initial_mle_result, and maxiter belong to fitting; passing those
fit arguments to mlog_likelihood or a post-fit likelihood call raises
TypeError. Unknown keywords also raise TypeError instead of being ignored.
Sampling and prediction arguments such as rng and given are routed to the
sampler, separately from numerical constructor overrides.
For automatic SCAR-TM-OU initialization, config.mle_optimizer controls the
internal static MLE even when smart_init=True. The resulting static estimate
is reused by the initialization heuristics and their fallbacks. Supplying
initial_mle_result avoids that static fit; an explicit alpha0 bypasses
automatic initialization entirely. If the internal MLE raises with
smart_init=True, initialization uses the constant fallback and records the
error in its diagnostics. With smart_init=False, the MLE error propagates.
Sampling, Prediction, and Diagnostics¶
predictive_mean(copula, u, result) returns the predictive mean copula
parameter at each time step before the current observation is absorbed.
For predict, SCAR-TM methods support horizon='current' for the posterior
state after the last observation and horizon='next' for the one-step-ahead
state. The shared prediction terminology is described in
Prediction Semantics.