---
title: Getting Started
---

The **phasic** library provides fast and scalable algorithms for constructing and computing properties of phase-type distributions. It supports both continuous and discrete, unrewarded and rewarded, univariate and multivariate distributions. For sparse models, it can be **orders of magnitude faster** than traditional matrix-based methods.

**Key Features:**

- **Graph-based algorithms**: Replace expensive matrix operations with efficient graph traversal
- **Multiple language support**: C, C++, Python, and R interfaces
- **JAX integration**: Automatic differentiation, JIT compilation, and parallelization
- **Bayesian inference**: Built-in SVGD (Stein Variational Gradient Descent)
- **Distributed computing**: Scale from laptop to 100+ node SLURM clusters
- **Symbolic computation**: Extremely fast evaluation for parameterized models

The library computes:
- **Moments**: expectation, variance, covariance, higher-order moments
- **Distribution functions**: PDF, CDF, PMF
- **Transforms**: Laplace transforms, reward transformations
- **Markov properties**: stopping probabilities, residence times
- **Time-inhomogeneous models**: epoch-wise distributions and joint probabilities

<!-- The user iteratively builds the the state-space using a simple API. The state-space can be imported from and exported to a matrix representation allowing the library to seamlessly embed into matrix based workflows. -->

## Installation

::: {.panel-tabset}

## Pixi

Add conda channel:

```bash
pixi workspace channel add munch-group
```

Install without `jax`:

```bash
pixi install phasic
```

For gradient-based inference using SVGD, the `jax` library is also required:

```bash
pixi install phasic jax
```

## Conda

Install without `jax`:

```bash
conda install -c munch-group -c conda-forge phasic
```

For gradient-based inference using SVGD, the `jax` library is also required:

```bash
conda install -c munch-group -c conda-forge phasic jax
```

## Pip

Install without `jax`:

```bash
pip install phasic
```

For gradient-based inference using SVGD, the `jax` library is also required:

```bash
pip install phasic[jax]
```

:::

## Quick Start

### Python

Here's a minimal example to get you started:

```python
#| label: fig-erlang-quick
#| fig-cap: "Erlang distribution with 5 phases"
#| column: margin

import phasic as ptd
import numpy as np

# Create a simple Erlang distribution (5 phases with rate 2)
graph = ptd.Graph(1)

# Build a chain of 5 states
start = graph.starting_vertex()
vertices = [start]
for i in range(5):
    v = graph.find_or_create_vertex([i + 1])
    vertices.append(v)

# Connect with edges (rate = 2.0)
for i in range(5):
    vertices[i].add_edge(vertices[i + 1], weight=2.0)

# Compute properties
print(f"Expectation: {graph.expectation():.3f}")    # E[X] = 5/2 = 2.5
print(f"Variance: {graph.variance():.3f}")          # Var[X] = 5/4 = 1.25

# Evaluate distribution functions
times = np.linspace(0, 10, 100)
pdf = graph.pdf(times)
cdf = graph.cdf(times)

# Sample from the distribution
samples = graph.sample(1000)
print(f"Sample mean: {np.mean(samples):.3f}")

graph.plot()
```

**Output:**
```
Expectation: 2.500
Variance: 1.250
Sample mean: 2.487
```

### R

```r
library(phasic)

# Create Erlang distribution
g <- create_graph(state_length = 1)

# Build chain of states
start <- starting_vertex(g)
vertices <- list(start)
for (i in 1:5) {
    v <- find_or_create_vertex(g, i)
    vertices[[i + 1]] <- v
}

# Connect with edges
for (i in 1:5) {
    add_edge(vertices[[i]], vertices[[i + 1]], 2.0)
}

# Compute properties
expectation(g)  # 2.5
variance(g)     # 1.25

# Evaluate PDF
times <- seq(0, 10, length.out = 100)
pdf_vals <- pph(times, g)

# Sample
samples <- rph(1000, g)
mean(samples)   # ~2.5
```

## Core Concepts

### Graph Construction

Build phase-type distributions as directed graphs:

- **Vertices** represent states (integer vectors)
- **Edges** represent transitions with rates/probabilities
- States are found or created automatically using AVL trees (O(log n) lookup)

### Continuous vs Discrete

**Continuous** (time-based):
```python
graph = ptd.Graph(2)
# ... build graph ...
pdf = graph.pdf(times)        # Probability density
cdf = graph.cdf(times)        # Cumulative distribution
```

**Discrete** (step-based):
```python
graph = ptd.Graph(2)
# ... build graph ...
graph.normalize()             # Convert to discrete-time
pmf = graph.dph_pmf(steps)    # Probability mass
```

### Rewards

Add rewards to model accumulated values:

```python
# Define rewards per state
rewards = np.array([0, 1, 2, 1, 0])

# Compute expected accumulated reward
E_reward = graph.expectation(rewards)

# Or transform the distribution
reward_graph = graph.reward_transform(rewards)
pdf_reward = reward_graph.pdf(times)
```

### Parameterized Models

Build models that can be updated with different parameter values:

```python
def my_model_callback(state, n_samples=10):
    # Return (next_state, weight, edge_coefficients)
    # Edge coefficients multiply with parameter vector
    if not state.size:
        return [[[n_samples], 1, [1]]]

    transitions = []
    if state[0] > 1:
        n = state[0]
        rate = n * (n - 1) / 2
        transitions.append([[n - 1], 0, [rate]])
    return transitions

# Build parameterized graph
graph = ptd.Graph(my_model_callback,
                  parameterized=True,
                  n_samples=10)

# Update parameters and recompute
theta = np.array([2.0])
graph.update_weights_parameterized(theta)
E = graph.expectation()
```

### JAX Integration

Convert graphs to JAX functions for automatic differentiation:

```python
import jax
import jax.numpy as jnp

# Convert to JAX-compatible function
pmf_fn = ptd.Graph.pmf_from_graph(graph, discrete=False)

# Now you can use JAX transformations
pmf_jit = jax.jit(pmf_fn)           # JIT compile
pmf_grad = jax.grad(pmf_fn)          # Gradient
pmf_vmap = jax.vmap(pmf_fn)          # Vectorize

# Evaluate
theta = jnp.array([1.0])
times = jnp.linspace(0, 5, 50)
pdf_values = pmf_fn(theta, times)
```


