Vine Copulas¶
Overview¶
Vine copulas decompose a \(d\)-dimensional copula into \(d(d-1)/2\) bivariate
copulas arranged in a tree structure. VineCopula is the primary runtime and
supports three structural modes:
- auto R-vine: data-driven structure selected by Dissmann's algorithm;
- fixed C-vine: successive-root star trees;
- fixed D-vine: path trees.
Any other valid regular vine can be supplied as an RVineMatrix. C/D/R are
structure modes of one model, not separate generic runtime classes.
Each edge copula is selected automatically from the configured candidate families via AIC, and can use constant (MLE) or time-varying (SCAR, GAS) parameters.
The shared transform_type option is passed to candidate constructors.
Archimedean edge families support softplus, xtanh, exp, and logistic.
For exp or logistic, use an Archimedean-only candidate pool as shown in
Parameter Transforms.
BivariateGaussianCopula accepts the same argument for constructor
uniformity, but Gaussian edges always use their bounded GaussianTanh
correlation mapping.
Use candidates= to configure the automatic family-selection pool. For
Gaussian pair-copula edges, use BivariateGaussianCopula; the multivariate
GaussianCopula is not a valid vine edge family. The lower-level copulas=
fit argument has a different purpose: it fixes every edge family and rotation
as (CopulaClass, rotation) specs in a list-of-lists matching the tree
layout.
C-vine¶
A C-vine uses a star structure where the first variable is the root of tree 0, the second variable is the root of tree 1, and so on.
from pyscarcopula import VineCopula
vine = VineCopula.cvine(d=u.shape[1])
vine.fit(u, method='scar-tm-ou',
truncation_level=2,
min_edge_logL=10,
to_pobs=True)
vine.summary()
Static MLE models fitted to the same C-vine structure and fixed edge families have equivalent edge semantics and likelihood. Internal edge ordering and seeded sampling trajectories are not an API parity guarantee.
Conditional sampling uses matrix-based suffix conditioning and a DAG+MCMC
fallback for arbitrary conditioning sets;
prediction supports return_diagnostics, MCMC controls, and
dynamic_conditioning.
Auto-selected R-vine¶
An R-vine selects the tree structure from data using Dissmann's algorithm: at each tree level, a maximum spanning tree is built on \(|\text{Kendall's tau}|\), subject to the proximity condition.
from pyscarcopula import VineCopula
vine = VineCopula()
vine.fit(u, method='scar-tm-ou',
truncation_level=2,
min_edge_logL=10,
to_pobs=True)
vine.summary()
An auto-selected R-vine can place the strongest available pairwise dependencies at each tree level instead of being constrained to a C-vine star structure. Compare fitted structures with the same likelihood and information criterion.
If the conditioning set is known in advance, you can bias structure selection toward an R-vine that supports the fast exact conditional sampler for that set, with the fixed variables placed at the end of the R-vine variable order:
vine = VineCopula()
vine.fit(
u,
method='scar-tm-ou',
truncation_level=2,
min_edge_logL=10,
to_pobs=True,
given_vars=[0, 2],
)
given_vars is a fit-time structure-selection target. With the default
conditional_strict=True, fit raises ValueError if no suffix-compatible
exact structure is constructed. With conditional_strict=False, prediction can
still use other conditioning sets, but the unsupported fit-time target remains
rejected by predict. To use the approximate fallback for that set, fit without
given_vars and supply the set only to predict(given=...).
Fixed D-vine¶
Use a D-vine factory when the first-tree path order is part of the model:
from pyscarcopula import VineCopula
vine = VineCopula.dvine(
d=u.shape[1],
order=[0, 2, 1, 3],
).fit(u, method="mle")
VineCopula.cvine(...) and .dvine(...) both configure a fixed
RVineMatrix; fitting their pair copulas never runs structure selection.
Arbitrary fixed regular vine¶
Prefer decoded tree edges over manually writing either matrix convention:
from pyscarcopula import VineCopula
from pyscarcopula.vine import RVineMatrix
structure = RVineMatrix.from_trees(
d=4,
trees=[
[
(frozenset({0, 1}), frozenset()),
(frozenset({1, 2}), frozenset()),
(frozenset({1, 3}), frozenset()),
],
[
(frozenset({0, 2}), frozenset({1})),
(frozenset({0, 3}), frozenset({1})),
],
[
(frozenset({2, 3}), frozenset({0, 1})),
],
],
)
vine = VineCopula(structure=structure).fit(u, method="mle")
RVineMatrix.from_trees validates tree sizes, indices, and the proximity
condition before any optimizer is called.
Candidate families versus fixed families¶
Constructor candidates= defines the pool searched independently for every
edge:
from pyscarcopula import BivariateGaussianCopula, FrankCopula
vine = VineCopula.dvine(
d=u.shape[1],
candidates=[BivariateGaussianCopula, FrankCopula],
).fit(u, method="mle")
Fit argument copulas= instead fixes every (family, rotation) in the
structure's decoded tree order:
trees = vine.structure.to_trees()
fixed_specs = [
[(BivariateGaussianCopula, 0) for _ in level]
for level in trees
]
fixed = VineCopula(structure=vine.structure).fit(
u,
method="mle",
copulas=fixed_specs,
)
copulas= contains classes and rotations, not fitted copula instances.
Inspecting structure¶
print(vine.structure) # canonical RVineMatrix
print(vine.structure.to_trees()) # decoded semantic edges
print(vine.natural_order_matrix) # numerical/integration convention
vine.matrix returns the same natural-order representation for compatibility
with older code. Prefer structure for modelling and natural_order_matrix
when an external integration explicitly needs that convention.
RVineCopula is a compatibility name for the same VineCopula type.
Truncation¶
For large d, not all edges benefit from dynamic parameters:
# Trees 0-1: SCAR, trees 2+: MLE
vine.fit(u, method='scar-tm-ou', truncation_level=2,
truncation_fill='mle')
# Edges below min_edge_logL become independent
vine.fit(u, method='scar-tm-ou', min_edge_logL=10)
# Default truncation_fill='independent': truncate and prune to independence
vine.fit(u, method='scar-tm-ou',
truncation_level=2, min_edge_logL=10)
Edges where no parametric copula beats independence by AIC are set to IndependentCopula automatically.
MLE optimizer options and NumericalConfig also apply to the refinement stage
of automatic family selection. Screening uses the configured native thread
count. An explicit natural-parameter alpha0 requires exactly one
non-independent candidate family, or fixed edge families via copulas=;
with several candidate families, omit it to use family-specific itau starts.
Internally generated itau starts are projected onto each family's MLE bounds;
explicit alpha0 values outside those bounds are rejected. Exact Kendall
dependence (tau = ±1) is retained for selection: Gaussian starts at its
admissible correlation bound, and bounded Archimedean families start at their
upper parameter bound. An unbounded Archimedean itau limit is reported as a
candidate numerical failure, rather than being replaced with an arbitrary
finite parameter. The default pool can still select Gaussian; a pool with no
evaluable candidate raises and preserves the previous fit. Interior itau
mappings and public tau_to_param domains are unchanged. A fitted MLE point
must pass a final native likelihood and gradient check: an optimizer's finite
numerical-failure penalty is not accepted as a likelihood.
Unexpected native, allocation, or configuration errors abort fitting and
preserve any previous fitted model. A candidate's numerical failure is reported
with a warning; if no candidate can be evaluated, fitting raises instead of
reporting a successful independent model.
dynamic_failure_policy='fallback' replaces an unsuccessful dynamic edge fit
with its MLE selection result. Use 'keep' to retain the unsuccessful dynamic
result and its diagnostics, or 'raise' to abort fitting while preserving any
previous fitted model. This policy applies to fixed structures and every
candidate in auto, beam, and multi-start structure selection. Falling back does
not turn an unsuccessful MLE result into a successful fit.
Goodness of fit¶
from pyscarcopula.stattests import gof_test
gof = gof_test(vine, u, to_pobs=False)
The gof_test function dispatches to the correct Rosenblatt transform for
both C-vine and R-vine models, including mixed SCAR/MLE edges.
Sampling and prediction¶
import numpy as np
# Predict: next-step conditional sampling (for VaR/CVaR)
predictions = vine.predict(
n=10000,
u=u,
horizon='next',
rng=np.random.default_rng(2025),
)
# Sample: reproduce fitted model (for parameter recovery)
samples = vine.sample(n=10000, rng=np.random.default_rng(2024))
For a vine with only built-in static MLE or independent edges, unconditional
predict uses the same bounded row batches as sample. Static prediction
does not compute or cache historical pseudo-observations, and static edge
parameters remain scalar during conditional prediction.
Conditional generation is supported via given={var_index: u_value} in
pseudo-observation space:
# Fit with given_vars=[0] when this exact conditioning target is known.
pred_cond = vine.predict(
n=5000,
u=u,
given={0: 0.6},
horizon='current',
rng=np.random.default_rng(2026),
)
Use a fresh np.random.default_rng(seed) for each call when exact
reproducibility is required. Reusing the same generator object advances its
random stream.
VineCopula automatically selects exact suffix conditioning when the fitted
structure supports it and otherwise uses an approximate DAG+MCMC fallback.
VineCopula.predict does not accept conditional_method; that name is
reported only in diagnostics. See
Prediction Semantics for the shared
meaning of predict, sample, and horizon, and
R-vine Conditioning for exact-path requirements,
MCMC controls, and diagnostic fields.
For SCAR-TM edges, predict(..., horizon='current') uses the posterior latent
state after the fitted history and predict(..., horizon='next') uses the
one-step-ahead latent state. For SCAR-TM-OU edges, sample simulates
independent OU trajectories.
SCAR vines use edge-wise pseudo likelihood for fitting and likelihood
evaluation. Each edge is optimized separately. Pseudo-observations for higher
trees are constructed from the fitted lower-tree h-functions, using mixture
h-functions for latent edges. sample reproduces the latent-trajectory vine,
while predict uses edge-wise posterior or one-step predictive states from
the observed history. The edge-wise criterion approximates the full joint
marginal likelihood; joint latent-state optimization and filtering are outside
this contract.
For SCAR-TM predictive parameter sampling, predictive_r_mode may be None,
"grid", or "histogram".