Copulas
All 15 bivariate and 8 multivariate copulas with the BivariateExt / MultivariateExt traits — Archimedean, extreme-value, elliptical, and vine families.
Copulas
The stochastic-rs-copulas crate ships bivariate and multivariate
copulas plus correlation utilities and an empirical copula.
Bivariate (BivariateExt<T>)
15 copulas — every variant of the crate's own bivariate::CopulaType
enum (grep -n "pub enum CopulaType" -A 15 stochastic-rs-copulas/src/bivariate.rs).
"Archimedean" below means the type overrides generator(), the trait's
Archimedean-generator hook — the default implementation returns an error,
so a concrete override is the tree-derived signal, not a label taken from
each module's own prose:
| Copula | Family | Tail dependence |
|---|---|---|
| Amh | Archimedean | None |
| Bb1 | Archimedean (two-parameter) | Both, asymmetric (, ) |
| Bb7 | Archimedean (two-parameter) | Both, asymmetric (, ) |
| Clayton | Archimedean | Lower only () |
| Fgm | Perturbation | None (bounded perturbation of independence) |
| Frank | Archimedean | None (symmetric) |
| Galambos | Extreme-value | Upper only () |
| Gaussian | Elliptical | None, for any |
| Gumbel | Archimedean | Upper only () |
| HuslerReiss | Extreme-value | Upper only () |
| Independence | Trivial | None |
| Joe | Archimedean | Upper only () |
| MarshallOlkin | Singular (shared-shock) | Upper only (); has a singular component on |
| Plackett | Constant odds ratio | None |
| TCopula | Elliptical | Both, symmetric () |
The bivariate samplers are consolidated under BivariateExt; the v1.x
NCopula2DExt was removed in v2.0.
Choosing a copula
Every family except Independence is fit the same way — moment-matching
Kendall's tau, then inverting to the family's own shape parameter — so
how you fit isn't the distinguishing axis. The crate's own doc comment
(stochastic-rs-copulas/src/bivariate.rs) has the full comparison,
including which common heuristics turn out to be wrong on the types in
this crate. Short version:
| Need | Reach for |
|---|---|
| Both positive and negative dependence | Frank, Gaussian, TCopula, Plackett — the only four spanning the full (-1, 1) range of τ |
| Only ever positive dependence, lower-tail | Clayton — the only family here with lower-tail dependence at all |
| Only ever positive dependence, upper-tail | Galambos, HuslerReiss, Gumbel, Joe — note Gumbel and Joe share the exact same upper-tail formula, so tail dependence alone won't distinguish them |
| Symmetric tail dependence (both tails, equal) | TCopula — the only one |
| Weak dependence in either direction (τ within roughly ±0.2-0.3) | Amh, Fgm — both structurally cannot represent stronger dependence, in either direction |
| Fast sampling at scale (millions of draws) | Clayton, Gaussian or Independence — the only three with a real closed-form percent_point; everything else pays a per-draw root-find |
| A non-exchangeable, shared-shock dependence structure | MarshallOlkin's with_alpha_beta constructor — the one family here that isn't symmetric in its two arguments in general |
All 13 require inputs already probability-integral-transformed to
[0, 1] — fit runs a Kolmogorov-Smirnov uniformity check before
touching Kendall's tau. Independence is the one exception: its fit
never reads the data at all.
Multivariate (MultivariateExt<T>)
8 copulas — every variant of multivariate::CopulaType
(grep -n "pub enum CopulaType" -A 10 stochastic-rs-copulas/src/multivariate.rs).
Two of these are not real pair-copula-construction vines despite the
name — each says so in its own module doc:
| Copula | Family | Notes |
|---|---|---|
| GaussianMultivariate | Elliptical | Cholesky-based |
| TMultivariate | Elliptical | Multivariate Student-t |
| NestedArchimedean | Archimedean, nested | Outer + per-cluster inner generators (NAC) |
| CVine | Vine, PCC | Canonical vine — genuine pair-copula construction |
| DVine | Vine, PCC | D-vine — genuine pair-copula construction |
| RVine | Vine, PCC | Regular vine — graph-theoretic generalisation of C-/D-vine |
| TreeMultivariate | Not a real vine | MST-derived implied correlation collapsed to a Gaussian copula — module doc: "Gaussian-collapsed implied-correlation copula, NOT a real R-vine" |
| VineMultivariate | Not a real vine | Star-C-vine-derived implied correlation collapsed to a Gaussian copula — same disclaimer, same doc |
Multivariate copulas ship in the default build — the linear algebra is the pure-Rust
faer, no feature flag and no system BLAS.
Examples
Clayton — lower-tail dependence
produces lower-tail dependence, useful for modelling joint crashes in equity returns.
// docs: copulas#clayton-lower-tail-dependence
//! Backs the Clayton example on the copulas catalog page.
use stochastic_rs::copulas::bivariate::clayton::Clayton;
use stochastic_rs::copulas::correlation::kendall_tau;
use stochastic_rs::traits::BivariateExt;
#[test]
fn clayton_set_tau_then_sample() {
let mut cop = Clayton::new();
cop.set_tau(0.5); // tau => theta via Kendall inversion
cop.set_theta(cop.compute_theta());
let uv = cop.sample_with_seed(10_000, 42).unwrap(); // Array2<f64>, shape (10_000, 2)
assert_eq!(uv.dim(), (10_000, 2));
assert!(uv.iter().all(|&x| (0.0..=1.0).contains(&x)));
let corr = kendall_tau(&uv); // pairwise Kendall tau matrix, shape (2, 2)
let tau_hat = corr[[0, 1]];
assert!((tau_hat - 0.5).abs() < 0.05, "tau_hat = {tau_hat}");
}import stochastic_rs as srs
cop = srs.Clayton(theta=2.0)
uv = cop.sample(10_000, seed=42) # shape (10_000, 2)
# kendall_tau_matrix takes an (n, k) matrix and returns the k×k pairwise-tau
# matrix; uv is already (n, 2), so [0, 1] is the one off-diagonal entry.
tau = srs.kendall_tau_matrix(uv)[0, 1]
print("Kendall's tau ≈", tau) # ≈ 0.5BB1 / BB7 — two-parameter tail asymmetry
The one-parameter Archimedean families tie the two tails together: Clayton
has only lower-tail, Gumbel only upper-tail dependence. BB1 (Clayton–Gumbel)
and BB7 (Joe–Clayton) carry two parameters and set the two
tails separately — BB1 has ,
; BB7 has ,
. fit is a two-parameter maximum-likelihood
estimate (Nelder–Mead on the log-density); set_tau + compute_theta
inverts Kendall's τ at the current δ (closed form for BB1,
; numerical for BB7).
gof::gof_cramer_von_mises is the Rosenblatt-transform Cramér–von Mises test
of Genest, Rémillard & Beaudoin (2009) with a parametric bootstrap p-value —
the valid procedure when the copula parameters were estimated on the same
sample. multivariate::fit::fit_vine selects a D-vine or C-vine structure on
Kendall's τ (Dißmann et al. 2013) and the family of every edge by AIC or BIC
among Independence, Gaussian, Student-t, Clayton, Frank, BB1 and BB7, with
the h-functions of the fitted pairs producing the pseudo-observations of the
next tree. Feed every fit and test normalised ranks (gof::pseudo_observations,
srs.pseudo_observations in Python): the statistics are rank statistics, and a
family's fit rejects margins that fail its uniformity check.
// docs: copulas#bb1-bb7-two-parameter-tail-asymmetry
//! Backs the BB1 / vine-fitting example on the copulas catalog page.
use stochastic_rs::copulas::bivariate::bb1::Bb1;
use stochastic_rs::copulas::gof::gof_cramer_von_mises;
use stochastic_rs::copulas::gof::pseudo_observations;
use stochastic_rs::copulas::multivariate::fit::PairFamily;
use stochastic_rs::copulas::multivariate::fit::SelectionCriterion;
use stochastic_rs::copulas::multivariate::fit::VineStructure;
use stochastic_rs::copulas::multivariate::fit::fit_vine;
use stochastic_rs::traits::BivariateExt;
#[test]
fn bb1_fit_gof_and_vine_selection() {
// BB1 with lower tail 2^{-1/(θδ)} and upper tail 2 - 2^{1/δ}: asymmetric tails.
let truth = Bb1::new(Some(0.8), Some(1.6), None);
let tails = truth.tail_dependence();
assert!(tails.lower > 0.5 && tails.upper > 0.4 && tails.lower != tails.upper);
// Every fit and test runs on pseudo-observations (normalised ranks), as with real data.
let uv = pseudo_observations(&truth.sample_with_seed(2_000, 42).unwrap()); // shape (2_000, 2)
// Maximum-likelihood fit of (θ, δ) recovers the generating pair.
let mut fitted = Bb1::default();
fitted.fit(&uv).unwrap();
assert!((fitted.theta.unwrap() - 0.8).abs() < 0.25 && (fitted.delta - 1.6).abs() < 0.25);
// Parametric-bootstrap Cramér–von Mises test does not reject the true family.
let gof = gof_cramer_von_mises(&fitted, &uv, 20, 7, |c, x| c.fit(x)).unwrap();
assert!(gof.p_value > 0.05, "p = {}", gof.p_value);
// A two-column D-vine fit selects a family on the single edge by AIC.
let fit = fit_vine(
&uv,
VineStructure::DVine,
&PairFamily::ALL,
SelectionCriterion::Aic,
)
.unwrap();
assert_eq!(fit.families.len(), 1);
assert!(fit.aic.is_finite() && fit.log_likelihood > 0.0);
}import numpy as np
import stochastic_rs as srs
truth = srs.Bb1(theta=0.8, delta=1.6)
uv = srs.pseudo_observations(truth.sample(2_000, seed=42))
lower, upper = truth.tail_dependence() # asymmetric: ≈ 0.58 vs ≈ 0.46
fitted = srs.Bb1()
fitted.fit(uv) # MLE of (theta, delta)
stat, p = srs.copula_gof("bb1", uv, replications=50, seed=7)
print(fitted.theta(), fitted.delta(), p) # p well above 0.05
# 3-column data: structure + family selection with BIC
u3 = srs.pseudo_observations(np.column_stack([uv, np.random.default_rng(1).uniform(size=2_000)]))
fit = srs.fit_vine(u3, structure="dvine", criterion="bic")
print(fit["order"], fit["families"], fit["aic"])Gaussian copula — multivariate sampling
// docs: copulas#gaussian-copula-multivariate-sampling
//! Backs the Gaussian multivariate copula example on the copulas catalog
//! page. The Cholesky factorisation runs on the pure-Rust `faer`, so the
//! file is gated on that feature.
use ndarray::array;
use stochastic_rs::copulas::multivariate::gaussian::GaussianMultivariate;
use stochastic_rs::traits::MultivariateExt;
#[test]
fn gaussian_multivariate_sample() {
let corr = array![[1.0, 0.7, 0.3], [0.7, 1.0, 0.5], [0.3, 0.5, 1.0]];
let cop = GaussianMultivariate::new_with_corr(corr).unwrap();
let samples = cop.sample(10_000).unwrap(); // Array2<f64>, shape (10_000, 3)
assert_eq!(samples.dim(), (10_000, 3));
}The multivariate Gaussian copula is not wrapped for Python — use the Rust API directly.
Adding a copula
See the
copula-bivariate
SKILL — file-by-file recipe for Clayton / Frank / Gumbel / Joe /
Plackett / FGM / extreme-value families.