Optimizer Controls¶
Bivariate Models¶
All bivariate fits go through the strategy registry:
from pyscarcopula import JoeCopula
from pyscarcopula.api import fit
copula = JoeCopula(rotate=180)
# Default GAS fit.
result = fit(copula, u, method='gas')
# Tighten or relax optimizer controls for this run.
result = fit(
copula,
u,
method='gas',
gamma_bound=30.0,
beta_bound=0.995,
gtol=1e-4,
ftol=1e-12,
maxfun=3000,
)
The object method is equivalent:
result = copula.fit(u, method='scar-tm-ou', K=500, gtol=5e-3)
MLE¶
MLE labels a static model. For scalar bivariate families it estimates one constant copula parameter; the table in this subsection applies to that optimizer. Static multivariate Gaussian and Student correlation controls are described under Multivariate Native Paths.
| Parameter | Where | Default | Effect |
|---|---|---|---|
alpha0 |
fit kwarg | auto | Initial point in the copula's natural parameter space. |
gtol |
fit kwarg / mle_optimizer.gtol |
1e-3 |
L-BFGS-B projected-gradient tolerance. |
maxls |
fit kwarg / mle_optimizer.maxls |
20 |
Maximum L-BFGS-B line-search steps per iteration. |
from pyscarcopula import LBFGSBConfig, NumericalConfig
cfg = NumericalConfig(mle_optimizer=LBFGSBConfig(gtol=1e-6))
result = fit(copula, u, method='mle', config=cfg)
MLE evaluates the likelihood directly in the natural copula parameter. For
example, alpha0=[2.0] means a Gumbel parameter of 2.0, while a stochastic Student
initial value of 5.0 means five degrees of freedom. Parameter transforms are
used by dynamic latent-state methods, not by the MLE objective.
When alpha0 is omitted for a bivariate copula, the implementation calls
copula.transform([1.5]) only to obtain a family-valid natural starting
value. The returned value is passed directly to the MLE optimizer; it is not
treated as a latent coordinate.
GAS¶
GAS estimates an observation-driven recursion \(g_t = \omega + \beta g_{t-1} + \gamma\,score_{t-1}\).
| Parameter | Where | Default | Effect |
|---|---|---|---|
gamma0 |
fit kwarg | Two MLE-based starts | Explicit initial \([\omega, \gamma, \beta]\) selects a single start. |
gtol |
fit kwarg / gas_optimizer.gtol |
1e-3 |
L-BFGS-B projected-gradient tolerance. |
ftol |
fit kwarg / gas_optimizer.ftol |
1e-9 |
Relative objective decrease tolerance. |
maxfun |
fit kwarg / gas_optimizer.maxfun |
4000 |
Maximum scalar objective evaluations, including numerical-gradient probes. |
maxiter |
fit kwarg / gas_optimizer.maxiter |
1000 |
Maximum optimizer iterations. |
maxls |
fit kwarg / gas_optimizer.maxls |
100 |
Maximum L-BFGS-B line-search steps per iteration. |
eps |
fit kwarg / gas_optimizer.eps |
1e-8 |
Absolute native two-point step for the outer optimizer gradient when finite_diff_rel_step is unset. |
finite_diff_rel_step |
fit kwarg / gas_optimizer.finite_diff_rel_step |
None |
Relative two-point step in optimizer coordinates; takes precedence over eps. |
score_eps |
fit kwarg / gas_score_eps |
1e-4 |
Finite-difference step for Fisher curvature. |
gamma_bound |
fit kwarg / gas_gamma_bound |
20.0 |
Bounds score sensitivity to \([-\texttt{gamma\_bound}, \texttt{gamma\_bound}]\). |
beta_bound |
fit kwarg / gas_beta_bound |
0.999 |
Bounds persistence to \([-\texttt{beta\_bound}, \texttt{beta\_bound}]\); must be in \((0, 1)\). |
scaling |
strategy kwarg | 'unit' |
Recommended score scaling mode. 'fisher' is numerically sensitive. |
result = fit(
JoeCopula(rotate=180),
u,
method='gas',
scaling='unit',
ftol=1e-12,
maxfun=3000,
)
Automatic fitting tries the standard MLE-based score-driven start and a
nested static start with gamma=0. The latter shares the standard start's
intercept and persistence and exactly reproduces the static MLE path. An explicit gamma0
retains single-start semantics, including joint shrinkage fits. When ftol
is omitted, the best candidate is refined with ftol=1e-12. Each start and
refinement has its own maxfun and maxiter budget; nfev sums the runs.
The best finite terminal candidate is retained even if a worse candidate
reported convergence. A trial point that improves log likelihood by more
than 0.001 is retained with success=False if convergence there was not
established.
success additionally requires the optimizer's objective to agree with the
reported likelihood within 1e-6, and the likelihood to be no more than
0.001 below either the initial nested static likelihood (automatic starts)
or the constant path at the final intercept and persistence. Joint Student
shrinkage fitting preserves the quantile interpolation cache so optimization
and reporting evaluate the same function.
GASResult.diagnostics includes optimizer_stages (starts, final parameters,
objectives, raw convergence messages and evaluation counts),
optimizer_success, optimizer_message, projected_gradient_inf_norm,
static_baseline_log_likelihood, objective_discrepancy, and
likelihood_validation_passed. The projected gradient is informational; a
small relative function decrease can still occur with a large gradient.
Neither these checks nor multistart establish global optimality or guarantee
agreement with another version to 0.001. Inspect alternative starts and
finite-difference steps for sensitive fits. The smaller default eps limits
perturbations amplified by the recursion; explicit eps and
finite_diff_rel_step retain their documented precedence and behavior.
GAS uses the compiled numerical engine for likelihood, score recursion, filtering, prediction, and Rosenblatt operations. Unsupported copulas fail immediately.
The model score driving the GAS recursion is not an analytical gradient of the
complete likelihood with respect to omega, gamma, and beta. The compiled
evaluator obtains that optimizer gradient by two-point finite differences and
returns it together with the objective to SciPy L-BFGS-B. It follows SciPy's
forward-step convention, adjusts steps at parameter bounds, and divides by
the actual representable displacement. An explicit relative step follows
the sign and magnitude of the optimizer coordinate.
GASResult.diagnostics records these as model_score='native',
optimizer_gradient='native', and
gradient_kind='native_finite_difference'. maxfun and nfev use scalar
objective budget units, preserving the previous counts for completed
finite-difference calls. Each native objective/gradient call is charged four
units, or five for joint GAS/shrinkage fitting. An early numerical failure
may execute fewer likelihood evaluations but still incurs that full charge;
nfev therefore does not count physical likelihood calls. As with SciPy
numerical gradients, an iteration or line search can exceed maxfun.
Fisher scaling uses the analytical copula score
\(\partial\log c/\partial r\,\partial r/\partial g\) as its numerator and
computes only the local curvature by a second finite difference inside the GAS
recursion. The native evaluator still differentiates the outer objective
numerically. Together with the Fisher floor and score clipping, this can
produce a piecewise, step-sensitive objective. Prefer scaling='unit' unless
Fisher behavior is specifically under study.
Post-fit API operations inherit GASResult.scaling unless an explicit
scaling= override is supplied. The override applies to likelihood,
filtering, sampling, and prediction without changing the fitted result.
Likewise, GASStrategy() inherits the result's scaling for post-fit calls;
its fit default remains unit. Overriding scaling when predicting requires
the observation history, since the cached final parameter belongs to the
original scaling.
GAS sample and predict require a positive integer draw count. Both accept
memory_budget_bytes= as a pre-allocation guard. Fused bivariate sample
accounts for its caller-owned RNG draws, native result staging, and final
NumPy output: 6 * n * sizeof(float64) bytes. Other sample paths account for
their output. predict accounts for its output plus the predictive parameter
path. Unconditional bivariate sampling performs its causal score recursion in
one fused native call; conditional and multivariate sampling retain the
stepwise model-specific path. The causal GAS sample recursion is not split
into batches.
NumericalConfig¶
Use NumericalConfig when a setting should apply to many fits:
NumericalConfig() always uses n_threads=1, independently of process
environment variables. Native parallelism is enabled only by explicitly
passing n_threads to a method or by constructing
NumericalConfig(n_threads=N).
from pyscarcopula import LBFGSBConfig, NumericalConfig
cfg = NumericalConfig(
gas_optimizer=LBFGSBConfig(
gtol=1e-4,
ftol=1e-12,
maxfun=3000,
maxiter=3000,
maxls=50,
),
scar_optimizer=LBFGSBConfig(
gtol=1e-4,
maxls=50,
),
default_K=500,
)
result = fit(copula, u, method='gas', config=cfg)
Per-call keyword arguments override the config values for that fit.
For conditional bivariate api.sample and api.predict,
bisection_tol and bisection_maxiter configure the iterative inverse-h
solvers used by Gumbel and Joe (including rotations). The default tolerance
is 1e-10, and the iteration limit is 60. Gumbel tests a relative
transformed-equation residual; Joe tests a log(-log(h)) residual or a
certified root bracket at float64 resolution. Tolerance is an upper bound:
the kernels can apply a tighter criterion to preserve tail accuracy.
Exhausting the iteration budget raises an error instead of returning an
unchecked approximation. These settings do not affect analytical inverse-h
families or unconditional sampling. Direct low-level inverse-h calls use
80 iterations and 8e-15 for Gumbel, and 50 iterations and 1e-10 for Joe.
GAS fit uses config.fail_value when a numerical objective evaluation raises
FloatingPointError, including joint shrinkage fits. Argument and programming
errors such as TypeError and ValueError propagate to the caller.
Multivariate Student models use separate GAS optimizer defaults, so changing them does not affect bivariate GAS fits or vine edges:
cfg = NumericalConfig(
stochastic_student_gas_optimizer=LBFGSBConfig(ftol=1e-9),
)