Concepts
A self-contained tour of the ideas behind pyvinecopulib. The
emphasis is on the formulas you’ll see in docstrings and the API
classes that implement them. Skimming this page first should make
the rest of the documentation much easier to navigate; revisit it
whenever a docstring throws an unfamiliar symbol your way.
Sklar’s theorem and pseudo-observations
A copula is a multivariate distribution with uniform-\([0, 1]\) marginals. By Sklar’s theorem (Sklar, 1959), every joint distribution \(F\) on \(\mathbb{R}^d\) factorises as
where \(F_1, \ldots, F_d\) are the marginal CDFs and \(C : [0, 1]^d \to [0, 1]\) is the copula. When the marginals are continuous \(C\) is unique. Differentiating both sides gives the corresponding density factorisation
so the log-likelihood splits cleanly into a sum of marginal log-likelihoods and a copula log-likelihood:
This justifies a two-step inference-functions-for-margins estimator (Joe & Xu, 1996): first fit the \(d\) univariate marginals; then fit the copula on the pseudo-observations
The univariate side is well-trodden — pyvinecopulib uses
pyvinecopulib.utils.Kde1d (a boundary-corrected 1-d KDE that
also handles ordered-discrete data) for the marginals. Two
convenience helpers convert raw data to pseudo-observations and
measure dependence on them:
pyvinecopulib.utils.to_pseudo_obs()ranks each column to \((0, 1)\), the canonical input shape for the copula classes.pyvinecopulib.utils.wdm()computes weighted versions of Kendall’s \(\tau\), Spearman’s \(\rho\), Blomqvist’s \(\beta\), Hoeffding’s \(D\), and Pearson correlation — margin-free dependence measures that drive both diagnostics and Dissmann’s structure-selection heuristic.
The interesting part is \(c\), and the rest of this page is about how pyvinecopulib represents and fits it.
Bivariate copulas
The atomic object is the bivariate copula
pyvinecopulib.core.Bicop. It exposes the standard surface
expected from a \(d = 2\) distribution on
\([0, 1]^2\):
\(c(u_1, u_2)\) — copula density —
pyvinecopulib.core.Bicop.pdf();\(C(u_1, u_2)\) — copula CDF —
pyvinecopulib.core.Bicop.cdf();random sampling on \([0, 1]^2\) —
pyvinecopulib.core.Bicop.simulate().
Pair copulas inside a vine also need h-functions, the partial conditional CDFs
and their inverses \(h_1^{-1}\), \(h_2^{-1}\). These map to
pyvinecopulib.core.Bicop.hfunc1(),pyvinecopulib.core.Bicop.hfunc2(),pyvinecopulib.core.Bicop.hinv1(),pyvinecopulib.core.Bicop.hinv2().
H-functions are the workhorse of vine evaluation: they turn the
\([0, 1]^2\) outputs of one tree into the conditional
pseudo-observations consumed by the next, and their inverses drive
pyvinecopulib.core.Vinecop.simulate() and
pyvinecopulib.core.Vinecop.inverse_rosenblatt().
Every Bicop belongs to one of the families catalogued in
Available families below; switch families via
pyvinecopulib.core.FitControlsBicop.
Vine structures
Estimating a fully-flexible \(d\)-dimensional copula directly is hard once \(d\) is larger than a handful. Pair-copula constructions (Joe, 1996; Bedford & Cooke, 2001, 2002) sidestep that by decomposing the joint copula density into a product of \(d(d-1)/2\) bivariate building blocks. The order of conditioning is encoded by a graphical object called a vine.
Definition (regular vine)
A regular vine (R-vine) on \(d\) variables is a sequence of trees \((V_t, E_t)\), \(t = 1, \ldots, d - 1\), with
\(V_1 = \{1, \ldots, d\}\),
\(V_t = E_{t-1}\) for \(t \ge 2\),
the proximity condition: two nodes in \((V_{t+1}, E_{t+1})\) are connected only if the corresponding edges in \((V_t, E_t)\) share a node.
A vine copula labels each edge \(e \in E_t\) with a conditioned set \(\{j_e, k_e\}\) and a conditioning set \(D_e \subset \{1, \ldots, d\} \setminus \{j_e, k_e\}\), plus a bivariate pair-copula \(c_{j_e, k_e \mid D_e}\) describing the conditional dependence between \(U_{j_e}\) and \(U_{k_e}\) given \(\mathbf U_{D_e}\). See Czado & Nagler (2022) for a textbook treatment.
In pyvinecopulib the structure object is
pyvinecopulib.core.RVineStructure, with the two well-known
specialisations
pyvinecopulib.core.DVineStructure— every tree is a path; convenient for ordered-conditioning regression.pyvinecopulib.core.CVineStructure— every tree is a star; convenient when one variable drives the others.
Both can be passed anywhere an R-vine is accepted, and
simulate() draws structures uniformly at
random (Joe, 2011) — the basis of the
Structure selection section below.
Pair-copula construction
With the structure fixed, the vine-copula density factorises as
where the conditional pseudo-observations \(u_{j_e \mid D_e} = C_{j_e \mid D_e}(u_{j_e} \mid \mathbf u_{D_e})\) are obtained tree-by-tree from h-functions of the lower-tree pair copulas. Concretely, if an edge in tree \(t + 1\) has conditioned set \(\{j, k\}\) and conditioning set \(D \cup \{\ell\}\), then
i.e. the conditional pseudo-obs at level \(t + 1\) is an
h-function of the pair-copula one level down. This recursive
structure is what
pyvinecopulib.core.Vinecop.rosenblatt() evaluates forward
(data \(\to\) independent uniforms) and what
pyvinecopulib.core.Vinecop.inverse_rosenblatt() evaluates
backward (uniforms \(\to\) data — used by
pyvinecopulib.core.Vinecop.simulate()).
CDF evaluation is harder: there is no closed form for
\(C(\mathbf u)\) in general, so
pyvinecopulib.core.Vinecop.cdf() uses Monte-Carlo integration
with the quasi-random uniforms produced by
pyvinecopulib.utils.simulate_uniform()
(sobol() or
ghalton() sequences). Increase N to
trade compute for accuracy.
The same factorisation backs the PyTorch port
pyvinecopulib.torch.TorchVinecop (every pair copula is a
pyvinecopulib.torch.TorchBicop); its cascade matches the C++
evaluator byte-for-byte and additionally offers a batched=True
fast path (one stacked bicop call per tree level).
Simplifying assumption
The conditional pair-copulas in the previous section may, in principle, depend on the conditioning value \(\mathbf u_{D_e}\). The simplifying assumption states that they do not:
Under this assumption the model reduces to a collection of
two-dimensional copulas, which is what makes vines practical.
The choice of vine structure becomes load-bearing — different
structures yield different approximations of the same true
density. See Stoeber, Joe & Czado (2013), Spanhel & Kurz (2019)
and Nagler (2025) for the theoretical discussion. Every fit in
pyvinecopulib.core.Vinecop (and hence the sklearn /
torch wrappers) uses the simplified model;
pyvinecopulib.core.FitControlsVinecop controls which
families to consider, which structures to search, and how to
truncate the model in higher dimensions.
The backend-neutral VinecopBase can go
further: a ConditioningContext lets each
pair copula also depend on its conditioning value
\(\mathbf u_{D_e}\) (and on external covariates), giving a
genuinely non-simplified, conditional vine. This is an advanced
extension point (see Extending: custom and conditional pair copulas) — the built-in fit
stays simplified.
Available families
Every pair-copula in pyvinecopulib belongs to one of the families
below. The first column links the family constant, which lives on
pyvinecopulib.families and can be passed to
pyvinecopulib.core.FitControlsBicop.family_set
(or the same attribute on
pyvinecopulib.core.FitControlsVinecop) to restrict the
fit-time search space.
Parameter ranges below are the conventional textbook ones; the
exact bounds the C++ library enforces are visible via
pyvinecopulib.core.Bicop.get_parameters_lower_bounds() and
pyvinecopulib.core.Bicop.get_parameters_upper_bounds(). The
“Kendall’s \(\tau\)” column lists the closed-form mapping where
one exists; otherwise pyvinecopulib.core.Bicop.parameters_to_tau()
(and its inverse tau_to_parameters())
implement the numerical conversion.
Family |
Identifier |
Type |
Pars |
Range |
Rotations |
Tail dep. |
Kendall’s \(\tau\) |
|---|---|---|---|---|---|---|---|
Independence |
|
— |
0 |
— |
none |
none |
\(0\) |
Gaussian |
|
elliptical |
1 |
\(\rho \in (-1, 1)\) |
rotationless |
none |
\((2/\pi) \arcsin \rho\) |
Student |
|
elliptical |
2 |
\(\rho \in (-1, 1)\), \(\nu > 2\) |
rotationless |
symmetric |
\((2/\pi) \arcsin \rho\) |
Clayton |
|
Archimedean |
1 |
\(\theta > 0\) |
0° / 90° / 180° / 270° |
lower |
\(\theta / (\theta + 2)\) |
Gumbel |
|
Arch. / EV |
1 |
\(\theta \ge 1\) |
0° / 90° / 180° / 270° |
upper |
\(1 - 1/\theta\) |
Frank |
|
Archimedean |
1 |
\(\theta \in \mathbb{R} \setminus \{0\}\) |
rotationless |
none |
via Debye function |
Joe |
|
Archimedean |
1 |
\(\theta \ge 1\) |
0° / 90° / 180° / 270° |
upper |
series expansion |
BB1 |
|
Arch. (2-par) |
2 |
\(\theta > 0\), \(\delta \ge 1\) |
0° / 90° / 180° / 270° |
lower + upper |
closed form |
BB6 |
|
Arch. (2-par) |
2 |
\(\theta \ge 1\), \(\delta \ge 1\) |
0° / 90° / 180° / 270° |
upper |
closed form |
BB7 |
|
Arch. (2-par) |
2 |
\(\theta \ge 1\), \(\delta > 0\) |
0° / 90° / 180° / 270° |
lower + upper |
closed form |
BB8 |
|
Arch. (2-par) |
2 |
\(\theta \ge 1\), \(\delta \in (0, 1]\) |
0° / 90° / 180° / 270° |
upper |
closed form |
Tawn |
|
extreme-value |
3 |
bounded; see C++ bounds |
0° / 90° / 180° / 270° |
upper (asymmetric) |
via Pickands \(A\) |
TLL |
|
nonparametric |
— |
— |
data-driven |
data-driven |
rank-based |
The non-elliptical, non-radially-symmetric parametric families are
labelled with a rotation in
\(\{0°, 90°, 180°, 270°\}\); rotation 0° is the base form
(positive dependence, lower-tail-heavy for Clayton, upper-tail-heavy
for Gumbel / Joe, …), 180° is the survival copula (covers the
opposite tail), and 90° / 270° provide negative dependence variants.
pyvinecopulib.core.Bicop.flip() flips between rotations
0° \(\leftrightarrow\) 180° and 90° \(\leftrightarrow\) 270°.
Family-group constants (also in pyvinecopulib.families) are
pre-built lists you can pass directly to
family_set:
all— every family listed above.parametric— every family excepttll.nonparametric—indepandtll.one_par/two_par/three_par— grouped by parameter count (one-parameter parametric / two-parameter parametric / three-parameter parametric).elliptical—gaussian,student.archimedean—clayton,gumbel,frank,joe,bb1,bb6,bb7,bb8.extreme_value—tawn,gumbel.bb—bb1,bb6,bb7,bb8.rotationless— families that already cover both positive and negative dependence (indep,gaussian,student,frank,tll).lt/ut— families with lower- / upper-tail dependence.itau— families that support estimation by Kendall’s-\(\tau\) inversion (indep,gaussian,student,clayton,gumbel,frank,joe).
The notebook examples/01_bivariate_copulas.ipynb walks through
a fit on synthetic data for several of these families.
pyvinecopulib.utils.benchmark() compares several families on
standard test problems.
Estimation
Vine fitting is a two-step procedure inherited from Sklar’s theorem and pseudo-observations:
Marginals. Each \(F_j\) is estimated independently —
pyvinecopulib.utils.Kde1d(a boundary-corrected 1-d KDE) is the default both for the sklearn estimators and the notebook examples.Kde1dsupports continuous, ordered-discrete, and unordered-categorical input via itstypeargument.Copula. Given pseudo-observations \(\hat U_{i \cdot} = (\hat F_1(X_{i,1}), \ldots, \hat F_d(X_{i,d}))\), the joint copula is fit by
pyvinecopulib.core.Vinecop.from_data()(orpyvinecopulib.core.Vinecop.select()for in-place re-fitting).
The pair-copula estimator on each edge depends on the chosen
family. Three regimes are available via
pyvinecopulib.core.FitControlsBicop:
Maximum likelihood (
parametric_method="mle") — the default for parametric families; numerically optimises the per-edge log-likelihood under the family’s parameter constraints.Kendall’s :math:`tau` inversion (
parametric_method="itau") — restricted to families in theitaugroup; usestau_to_parameters()to back out the parameters from the empirical \(\hat\tau\). Cheaper than MLE and the conventional choice for very high-dimensional vines.Nonparametric TLL (family
tll—pyvinecopulib.families.tll) — Transformed Local Likelihood (Geenens, 2014; Nagler, 2018). The copula density is estimated on a grid in the inverse-normal-transformed space \((z_1, z_2) = (\Phi^{-1}(u_1), \Phi^{-1}(u_2))\), where local-likelihood machinery is well-behaved at the boundary of the unit square. Bandwidth selection is automatic; thenonparametric_methodandnonparametric_multknobs onFitControlsBicoptune the kernel order and the bandwidth multiplier respectively.
TLL is the default family for both the C++ and PyTorch backends
because it captures arbitrary non-Gaussian-like dependence (heavy
tails, asymmetry) without committing to a parametric form, and
because its density-grid representation is exactly what
pyvinecopulib.torch.TorchBicop consumes for GPU and
autograd evaluation.
Family selection across the parametric set runs by AIC / BIC /
mBIC inside pyvinecopulib.core.Bicop.select(); choose the
selection criterion via
pyvinecopulib.core.FitControlsBicop.selection_criterion.
Structure selection
The vine structure \(\mathcal V\) is rarely known in advance,
and the number of regular vines on \(d\) variables grows as
\(2^{(d-3)(d-2)/2 - 1} d!\) (Morales-Napoles, 2011; Joe, 2011)
— super-exponential, so exhaustive search is infeasible beyond a
handful of variables. Two algorithms are exposed via
pyvinecopulib.core.FitControlsVinecop.tree_algorithm:
"mst_prim"— Dissmann’s greedy heuristic (Dissmann et al., 2013), which is the default. Builds the trees one at a time as maximum-spanning-trees weighted by absolute Kendall’s \(\tau\) (or the criterion of your choice — seetree_criterion). It is fast and remains the de-facto standard (Czado & Nagler, 2022)."random_weighted"— Wilson-weighted random spanning trees; draws a random structure with edge probabilities proportional to the absolute dependence on each candidate edge. Used to seed the random search ensembles below.
When better accuracy matters more than runtime, the sklearn forest estimators do a Bayesian-style hold-out random search over a set of candidate structures and combine the survivors via a model confidence set (MCS) — see Vine forests and regression. The underlying algorithm is described in Vatter & Nagler (2026).
Vine truncation
For sparse models in higher dimensions, set
pyvinecopulib.core.FitControlsVinecop.trunc_lvl to an
integer \(T < d - 1\): pair copulas in trees
\(T + 1, \ldots, d - 1\) are forced to indep, reducing
both fit time and statistical degrees of freedom. The same effect
can be triggered automatically via the mBIC criterion (Nagler,
2019).
Vine forests and regression
When a single greedy fit is not enough, pyvinecopulib offers
ensemble estimators in pyvinecopulib.sklearn. The recipe is:
Generate \(M\) candidate structures \(\Theta = \{\mathcal V_1, \ldots, \mathcal V_M\}\) — either uniformly at random (Joe’s algorithm) or by Wilson-weighted local random walks around the Dissmann structure.
Fit a vine on each candidate.
Score each candidate on a held-out validation split with a loss \(L\) (negative log-likelihood for density estimation; conditional log-likelihood for regression).
Run a model-confidence-set selector (Hansen, Lunde & Nason, 2011) on the loss matrix to retain only candidates statistically indistinguishable from the best one. The implementation uses the dual-argmin (DA) test of Kim, Ramdas & Tibshirani (2025); see
pyvinecopulib.sklearnformethod="da_mcs_marg"(per-model coverage; default) vsmethod="da_mcs_unif"(familywise coverage).Combine the survivors \(\hat\Theta\) into the MCS mixture
\[\hat f_{\hat\Theta}(\mathbf z) \;=\; \frac{1}{|\hat\Theta|} \sum_{\mathcal V \in \hat\Theta} \hat f_{\mathcal V, \mathcal D}(\mathbf z).\]
This is what
pyvinecopulib.sklearn.VineForestDensity does for
joint-density estimation. The single-vine variant
pyvinecopulib.sklearn.VineDensity skips steps 1–4 and
fits one structure (Dissmann by default).
Vine regression
For conditional inference, fix one variable as the response \(Y\) and stack it with the predictors to model the joint distribution \((Y, \mathbf X)\). Any conditional summary of interest is then solved out of the estimating equation (Nagler, 2018)
with \(\psi_\beta(y) = y - \beta\) recovering the conditional mean \(\beta(\mathbf x) = \mathbb E[Y \mid \mathbf X = \mathbf x]\) and \(\psi_\beta(y) = \mathbb 1\{y < \beta(\mathbf x)\} - \tau\) recovering the conditional \(\tau\)-quantile. In practice the integral is replaced by a weighted sum over a grid \(\{y_1, \ldots, y_G\}\):
which pyvinecopulib.sklearn.VineRegressor (single vine)
and pyvinecopulib.sklearn.VineForestRegressor (MCS
ensemble) solve numerically. Pass the quantile levels you want
via the quantiles= constructor argument; the predicted
conditional mean is always returned when mean=True.
The forest classes share the same MCS plumbing as the density
forest, and parallelise over candidates via joblib
(n_jobs=).
Where to next
pyvinecopulib.core.Bicopandpyvinecopulib.core.Vinecop— the C++/nanobind classes that implement everything above. The notebooksexamples/01_bivariate_copulas.ipynb,examples/02_vine_copulas.ipynb, andexamples/03_vine_copulas_fit_sample.ipynbwalk through end-to-end use.pyvinecopulib.sklearn— scikit-learn-compatible estimatorsVineDensity,VineRegressor, plus the forest variantsVineForestDensityandVineForestRegressor. The notebooksexamples/08_sklearn_estimators.ipynbandexamples/09_sklearn_forest.ipynbdemonstrate them. All four estimators accept a backend (default C++, optional PyTorch) viapyvinecopulib.sklearn.backends.pyvinecopulib.torch— PyTorch evaluatorsTorchBicopandTorchVinecopfor GPU placement and autograd. Notebookexamples/10_torch_backend.ipynb.pyvinecopulib.utils—Kde1dfor the marginals (notebookexamples/07_kde1d.ipynb);wdm()for weighted dependence measures (notebookexamples/06_weighted_dependence_measures.ipynb);sobol(),ghalton(),simulate_uniform()for the low-discrepancy sequences that back Monte-Carlo CDF evaluation;to_pseudo_obs()andpairs_copula_data()for input preparation and pair-plot diagnostics.The API Documentation page is the autogenerated API reference; the Examples toctree lists all worked notebooks.
The examples/04_discrete_variables.ipynb notebook covers the
discrete-margin extension (Panagiotelis, Czado & Joe, 2012; Funk,
Nagler & Czado, 2025), which replaces the marginal CDF derivatives in
Sklar’s theorem and pseudo-observations by finite differences (transparent to the
user — pass var_types=["d", ...] to
pyvinecopulib.core.Vinecop.from_data() or set
type="d" on pyvinecopulib.utils.Kde1d).
Extending: custom and conditional pair copulas
The evaluators pyvinecopulib.core.Bicop /
Vinecop and their PyTorch counterparts
pyvinecopulib.torch.TorchBicop /
TorchVinecop are concrete implementations
of two backend-neutral contracts, evaluated on either NumPy or PyTorch
arrays:
BicopLike— a pair copula, exposingpdf/cdf/hfunc1/hfunc2/hinv1/hinv2/simulate;VinecopLike— a fitted vine, exposingpdf/cdf/rosenblatt/inverse_rosenblatt/simulateon anRVineStructure.
You can plug your own pair copula into a vine by implementing the
contract — most easily by subclassing the canonical partial
implementations BicopBase /
VinecopBase, which fill in almost
everything from a few primitives. A BicopBase subclass need only
define pdf / hfunc1 / hfunc2 and inherits numerical
hinv1 / hinv2, simulate, loglik, and plot; a
VinecopBase subclass need only return its pairs from
_get_pair_copula and inherits the whole tree-by-tree cascade. The
bases are pure Python (no PyTorch), so custom pairs also work in a
torch-less environment.
Every method carries an optional trailing conditioning matrix x.
For the common simplified, unconditional vine it is None
everywhere (the default
SimplifiedContext). To lift the
simplifying assumption, host the pairs
under a NonSimplifiedContext: the cascade
then assembles each edge’s conditioning-set values
\(\mathbf u_{D_e}\) (and any external covariates) into x and
threads them to the pair copula, giving a non-simplified /
conditional vine. pyvinecopulib.core.VinecopBase.fit()
is the seam for fitting such a vine edge by edge.
The examples/11_extending_pyvinecopulib.ipynb notebook is a
worked, end-to-end walk-through: a custom Gaussian pair copula hosted
first in a simplified vine (matching
pyvinecopulib.core.Vinecop.from_structure()) and then made
non-simplified and conditional.
References
Sklar (1959). Fonctions de répartition à n dimensions et leurs marges. Publ. Inst. Statist. Univ. Paris 8, 229–231.
Bedford & Cooke (2001, 2002). Probability density decomposition for conditionally dependent random variables modeled by vines / Vines — a new graphical model for dependent random variables. Annals of Mathematics and Artificial Intelligence 32, 245–268 / Annals of Statistics 30(4), 1031–1068.
Joe (1996). Families of m-variate distributions with given margins and m(m-1)/2 bivariate dependence parameters. In: Distributions with Fixed Marginals and Related Topics (IMS Lecture Notes 28), 120–141.
Joe & Xu (1996). The estimation method of inference functions for margins for multivariate models. Technical Report 166, Department of Statistics, University of British Columbia.
Aas, Czado, Frigessi & Bakken (2009). Pair-copula constructions of multiple dependence. Insurance: Mathematics and Economics 44(2), 182–198.
Dissmann, Brechmann, Czado & Kurowicka (2013). Selecting and estimating regular vine copulae and application to financial returns. Computational Statistics & Data Analysis 59, 52–69.
Joe (2011). Dependence comparisons of vine copulae with four or more variables. In: Dependence Modeling: Vine Copula Handbook, 139–164.
Geenens (2014). Probit Transformation for Kernel Density Estimation on the Unit Interval. JASA 109(505), 346–358.
Hansen, Lunde & Nason (2011). The Model Confidence Set. Econometrica 79(2), 453–497.
Nagler (2018). A Generic Approach to Nonparametric Function Estimation with Mixed Data. Statistics & Probability Letters 137, 326–330.
Nagler, Schepsmeier, Stoeber, Brechmann, Graeler & Erhardt (2018). VineCopula: Statistical inference of vine copulas. R package.
Nagler (2019). Model selection in sparse high-dimensional vine copula models with an application to portfolio risk. Journal of Multivariate Analysis 172, 180–198.
Spanhel & Kurz (2019). Simplified vine copula models: approximations based on the simplifying assumption. Electronic Journal of Statistics 13(1), 1254–1291.
Czado & Nagler (2022). Vine Copula Based Modeling. Annual Review of Statistics and Its Application 9, 453–477.
Panagiotelis, Czado & Joe (2012). Pair Copula Constructions for Multivariate Discrete Data. JASA 107(499), 1063–1072.
Funk, Nagler & Czado (2025). Discrete and mixed pair-copula constructions revisited. (In press.)
Kim, Ramdas & Tibshirani (2025). Locally simultaneous inference for the model confidence set. arXiv:2410.16092.
Vatter & Nagler (2026). Throwing Vines at the Wall: Structure Learning via Random Search. (Preprint.)