Skip to content

Commit 268eaba

Browse files
authored
Merge pull request #2 from thisiscam/examples-and-docs
Curate examples + MkDocs docs site (with llms.txt)
2 parents ac51a59 + 9ddb329 commit 268eaba

32 files changed

Lines changed: 1348 additions & 1672 deletions

‎.github/workflows/docs.yml‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Build the MkDocs site and deploy to GitHub Pages.
2+
# Enable Pages once in repo Settings → Pages → Source: "GitHub Actions".
3+
name: docs
4+
5+
on:
6+
push:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
pages: write
13+
id-token: write
14+
15+
concurrency:
16+
group: pages
17+
cancel-in-progress: false
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v4
24+
- uses: astral-sh/setup-uv@v5
25+
with:
26+
python-version: "3.12"
27+
enable-cache: true
28+
# mkdocstrings imports acopula to introspect, so install the full env.
29+
- name: Build site
30+
run: uv run --extra docs mkdocs build --strict
31+
- uses: actions/upload-pages-artifact@v3
32+
with:
33+
path: site
34+
35+
deploy:
36+
needs: build
37+
runs-on: ubuntu-latest
38+
environment:
39+
name: github-pages
40+
url: ${{ steps.deployment.outputs.page_url }}
41+
steps:
42+
- id: deployment
43+
uses: actions/deploy-pages@v4

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ env/
1818
.idea/
1919
.vscode/
2020
*.swp
21+
22+
# MkDocs build output
23+
site/

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ forward pass via Taylor-mode AD, scaling polynomially in the dimension.
1212
For background, see the paper *Archimedean Copula Inference via Taylor-Mode
1313
AD* (arXiv:TBD).
1414

15+
**Documentation:** <https://thisiscam.github.io/acopula/> ·
16+
**Examples:** [`examples/`](examples/)
17+
1518
## Install
1619

1720
```bash

‎acopula/compile.py‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,13 @@ def visualize(
323323
annotations=None,
324324
ax=None,
325325
):
326-
from .visualize import draw
327-
return draw(
328-
self.graph,
329-
include_leaves=include_leaves,
326+
from .visualize import draw_networkx
327+
G = self.as_networkx(include_leaves=include_leaves, annotations=annotations)
328+
return draw_networkx(
329+
G,
330330
layout=layout,
331331
with_labels=with_labels,
332332
graphviz_prog=graphviz_prog,
333-
annotations=annotations,
334333
ax=ax,
335334
)
336335

‎acopula/compose.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
_FALLBACK = "implicit" # "implicit" (default, stable, O(d_c^3)) or "direct_jet" (fast, may overflow)
2929

3030

31-
def register_composition(outer_cls, inner_cls, fn):
31+
def register_composition(outer_cls: type, inner_cls: type, fn: Callable):
3232
"""Register a stable closed-form composition h(t) = psi_outer_inv(psi_inner(t)).
3333
3434
Args:

‎acopula/core.py‎

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,16 +1847,11 @@ def _full_mixed_partials(
18471847
# = F^{(k)}(S) * prod(|g'|) = Likelihood!
18481848
T_k = _kth_derivative(outer_fun, S, k, scale=scaling_factor)
18491849

1850-
# 5. Assemble log-likelihood (mixed partial)
1851-
log_derivative_term = jnp.log(T_k)
1852-
jax.debug.print(
1853-
"log_derivative_term: {} T_k: {} S: {} scaling_factor: {} g_primes: {}",
1854-
log_derivative_term,
1855-
T_k,
1856-
S,
1857-
scaling_factor,
1858-
g_primes,
1859-
)
1850+
# 5. Assemble log-likelihood (mixed partial). The mixed partial of an
1851+
# Archimedean copula is (-1)^k psi^{(k)}; take the magnitude so the log is
1852+
# well-defined for odd k as well (e.g. a single observed dimension under
1853+
# censoring), where the raw k-th derivative is negative.
1854+
log_derivative_term = jnp.log(jnp.abs(T_k))
18601855

18611856
return log_derivative_term
18621857

‎docs/api.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# API reference
2+
3+
The public surface of `acopula`.
4+
5+
## Defining copulas
6+
7+
::: acopula.copula
8+
9+
::: acopula.Copula
10+
11+
::: acopula.marginal
12+
13+
## Compiling and evaluating
14+
15+
::: acopula.compile_model
16+
17+
::: acopula.CompiledModel
18+
19+
## Composition registry
20+
21+
::: acopula.register_composition
22+
23+
::: acopula.set_composition_fallback
24+
25+
## Configuration
26+
27+
::: acopula.set_stable_log
28+
29+
::: acopula.set_compile_cache_dir

‎docs/assets/acopula_pairs.png‎

774 KB
Loading

‎docs/assets/acopula_tree.png‎

41.8 KB
Loading

‎docs/examples.md‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Examples
2+
3+
The [`examples/`](https://github.com/thisiscam/acopula/tree/main/examples)
4+
directory has runnable, self-contained scripts. Each uses small synthetic /
5+
copula-scale data — no external datasets required.
6+
7+
Run them from the repo root:
8+
9+
```bash
10+
uv run python examples/01_quickstart.py
11+
uv run python examples/02_fit_mle.py
12+
uv run python examples/03_censored_survival.py
13+
uv run --extra examples python examples/04_sampling_and_visualize.py [output_dir]
14+
```
15+
16+
## 01 — Quickstart
17+
18+
Build a two-level nested copula, evaluate the log-likelihood and its exact
19+
`jax.grad`. See [Quickstart](quickstart.md).
20+
21+
## 02 — Maximum-likelihood fitting
22+
23+
Simulate 1000 observations from a known `d=10` Clayton copula (two sectors of
24+
five), then recover the parameters by gradient-descent MLE with `optax`. The
25+
whole likelihood is differentiable, so the fit is plain
26+
`jax.value_and_grad` + Adam:
27+
28+
```
29+
parameter true fitted
30+
theta_root 2.00 1.958
31+
theta_sector 5.00 4.840
32+
```
33+
34+
## 03 — Censored survival
35+
36+
A bivariate Frank copula over two Weibull event times, where the second is
37+
right-censored. A censored leaf still enters the copula argument (through its
38+
survival function) but is not differentiated in the density — `acopula`
39+
assembles the correct mixed partial over only the observed dimensions.
40+
41+
## 04 — Sampling and visualization
42+
43+
Draw samples from a Clayton-over-Frank copula (one root, three sectors of two
44+
leaves) with the Marshall-Olkin sampler, and render the copula tree.
45+
46+
### Copula tree
47+
48+
`CompiledModel.visualize()` draws the structural graph — root and sector
49+
copulas in blue, leaves in orange:
50+
51+
![Copula tree](assets/acopula_tree.png)
52+
53+
### Sampled margins
54+
55+
Pairwise scatter of the six sampled margins. Within-sector pairs
56+
(`u[i,0]`–`u[i,1]`) are visibly more dependent than across-sector pairs,
57+
reflecting the nesting:
58+
59+
![Pairwise margins](assets/acopula_pairs.png)

0 commit comments

Comments
 (0)