Skip to main content

JAX-based transition probability computation for multi-state models

Project description

jact

JAX-based transition probability and expected cashflow computation for multi-state models with duration-dependent transition intensities.

What is jact?

jact computes transition probabilities and expected cashflows in semi-Markov multi-state models. It takes fitted intensity models — parametric functions, GLMs, neural networks, or any JIT-compatible callable — and produces transition probabilities and cashflow streams for thousands of individuals in a single vectorized pass on GPU. Computations are optimized for JIT-compiled GPU execution.

Quick example

import jax.numpy as jnp
import jact

# Define the state space
state_space = jact.StateSpace(
    states=["healthy", "disabled", "dead"],
    transitions=[
        ("healthy", "disabled"),
        ("healthy", "dead"),
        ("disabled", "dead"),
    ],
)

# Build a model with intensity functions
model = state_space.build(
    transitions={
        ("healthy", "disabled"): onset_fn,
        ("healthy", "dead"): mortality_fn,
        ("disabled", "dead"): disabled_mort_fn,
    }
)

# Compute transition probabilities for 1000 individuals
ages = jnp.linspace(30, 80, 1_000)
result = model.solve(initial="healthy", horizon=30, steps_per_unit=12, age=ages)

Fitted-model intensity wrappers

Use jact.wrappers.bind_intensity() when a fitted model has a separate feature builder and apply function:

def features(t, d, *, age):
    return jnp.stack(
        [
            jnp.broadcast_to(age[:, None] + t, (age.shape[0], d.shape[-1])),
            jnp.broadcast_to(d, (age.shape[0], d.shape[-1])),
        ],
        axis=-1,
    )


def apply(params, x):
    linear = params["intercept"] + jnp.sum(x * params["coef"], axis=-1)
    return jnp.exp(linear)


onset_fn = jact.wrappers.bind_intensity(apply, fitted_params, features)

For fitted models that emit several hazards at once, use jact.wrappers.bind_grouped_intensity(..., output_count=K) or jact.wrappers.bind_exit_intensity(..., output_count=K). The wrappers clamp outputs to non-negative hazards and normalize grouped outputs to (K, batch, D).

Cashflow example

Using the same state_space, model, and ages as above:

import jax.numpy as jnp
import jact


def annual_premium(t, d, *, age):
    return jnp.full((age.shape[0], d.shape[-1]), -1_200.0)


def death_benefit(t, d, *, age):
    return jnp.full((age.shape[0], d.shape[-1]), 100_000.0)


cashflows = state_space.cashflows(
    {
        "premium": jact.cashflows.StateRate({"healthy": annual_premium}),
        "death_benefit": jact.cashflows.TransitionLump(
            {
                ("healthy", "dead"): death_benefit,
                ("disabled", "dead"): death_benefit,
            }
        ),
    }
)

result = model.solve(
    initial="healthy",
    horizon=30,
    steps_per_unit=12,
    record_every=12,
    probability=None,
    cashflows=cashflows,
    cashflow_views={
        "raw": jact.cashflows.Raw(),
        "pv_total": jact.cashflows.Total(
            weight=lambda t, **kwargs: jnp.exp(-0.03 * t),
            terminal=True,
        ),
    },
    age=ages,
)

premium_stream = result.cashflows["raw"]["premium"]
present_value = result.cashflows["pv_total"]

Key features

  • Plug in any model: Gompertz, GLM, neural network — anything that's JIT-compatible.
  • Swap and compare: Same StateSpace, different intensity models. Experiment easily.
  • Probabilities and cashflows together: Emit both in one fused solve, with solve-time cashflow views for grouping and valuation.
  • Compute only what's needed: The solver reduces to states reachable from the initial state.
  • Exact seeded starts: Initial point masses preserve per-individual starting duration d_0 exactly.
  • Batch-first: Designed for 100K+ individuals in a single pass.
  • Agent-ready guidance: Ship jact modeling instructions to AI coding agents with the bundled jact-agent-skill helper.

Documentation

See the documentation index for the public documentation set. For the full API contract, use the API specification. For a runnable walkthrough of the main workflow, see the example notebook. For a fitting-to-solver workflow with neural-network intensities, see the fitted neural-network notebook.

AI agent skill

Installed packages include an application-focused AI agent skill for writing jact modeling code. The skill helps coding agents choose the right StateSpace, intensity wrappers, probability reducers, initial distributions, and cashflow views. It is user-facing modeling guidance, separate from repository development guidance such as AGENT.md.

The skill is packaged at jact/agents/jact/SKILL.md and includes YAML frontmatter (name and description) for agent CLIs that auto-discover skills. Use the generic helper to locate, print, or install it into the directory your agent CLI expects:

jact-agent-skill path
jact-agent-skill print
jact-agent-skill install --target ~/.config/my-agent/skills/jact

For example, to install it for a project-local skill-aware agent directory:

jact-agent-skill install --target .github/skills/jact

The same helper is available as a module:

python -m jact.agents install --target ~/.config/my-agent/skills/jact

Namespace

The top-level jact namespace exposes the core types: jact.StateSpace, jact.Model, jact.InitialDistribution, jact.ModelResult, and jact.solve. Domain types and fitted-model helpers live under submodules:

  • jact.cashflows for declarations and views (StateRate, TransitionLump, ScheduledEvent, DurationEvent, Raw, Group, Total, ByState, ByKind).
  • jact.probability for output reducers (StateProbability, DensityProbability, Density, PointMass, MarginalComponents, Full).
  • jact.wrappers for fitted-model intensity helpers (bind_intensity, bind_grouped_intensity, bind_exit_intensity).

Advanced inspection types stay in private modules — for example jact.probability.StateCarry and jact.model.ReducedModel.

Installation

pip install jax jaxlib
pip install jact

For local development from this repository:

pip install -e '.[dev]'
pytest

The package uses a src/ layout, so editable install is the intended local workflow.

To run the example notebook with plotting support from a local checkout:

pip install -e '.[dev,notebook]'

Release checks

Before cutting a PyPI release:

rm -rf build dist src/*.egg-info
python -m build --no-isolation
python -m twine check dist/*
pytest -q

The tag-driven publish flow is documented in RELEASING.md.

Requirements

  • Python >= 3.10
  • JAX >= 0.4

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jact-0.1.8.tar.gz (43.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jact-0.1.8-py3-none-any.whl (45.3 kB view details)

Uploaded Python 3

File details

Details for the file jact-0.1.8.tar.gz.

File metadata

  • Download URL: jact-0.1.8.tar.gz
  • Upload date:
  • Size: 43.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jact-0.1.8.tar.gz
Algorithm Hash digest
SHA256 5fbc006e4e719907d7c56cbd707226d692e540a84f02a3bddd46f816472669d1
MD5 f6b94d81305d90b933673232bf55959c
BLAKE2b-256 d0df7f6e7209994faaae50e02cf1d497e6b7f3a4403d2a956a0dcd64bfa4e6c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for jact-0.1.8.tar.gz:

Publisher: publish.yml on stojl/jact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jact-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: jact-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 45.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jact-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 7c186d1a6ca3bfc048bcc2f7944e9614c90b6e3de2e10f7610a6f5eaa560bfbd
MD5 2569705abcf2defd3f2331b0b3f21ea7
BLAKE2b-256 07db1432d2429f1cbf23fb7780fe0e60ad65315861dfd5e0852253c63c53edc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for jact-0.1.8-py3-none-any.whl:

Publisher: publish.yml on stojl/jact

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page