---
title: ProcessExt
description: The trait every stochastic process implements — sample, sample_par, with the contract for path length, time grid, and parallel determinism.
category: concept
since: 2.0.0
status: stable
---

# `ProcessExt<T>`

Every stochastic process in `stochastic-rs-stochastic` — diffusion, jump,
volatility, interest, rough, noise — implements `ProcessExt<T>`:

```rust
pub trait ProcessExt<T: FloatExt> {
    /// Sample one path of length `n`.
    fn sample(&self) -> Array1<T>;

    /// Sample `m` independent paths of length `n` in parallel.
    fn sample_par(&self, m: usize) -> Array2<T>;
}
```

## Path length and time grid

Construction of any process takes `(n, x0, t)`:

- `n: usize` — number of steps (the returned path is length `n`)
- `x0: Option<T>` — initial value; `None` ⇒ a sensible default per
  process (e.g. 0 for OU, $\log(s_0)$ for log-GBM)
- `t: Option<T>` — horizon; `None` ⇒ `1.0`

The time grid is uniform: $\Delta t = t / n$. Non-uniform grids are
handled by user code (sample at fine $\Delta t$, then resample).

## Parallel sampling and determinism

`sample_par(m)` uses Rayon. Each path gets a deterministic seed derived
from the process's master seed plus the path index:

```rust
let seed_i = master_seed.wrapping_add(i as u64);
```

This means:

- Same master seed ⇒ same `(m, n)` matrix, regardless of thread count.
- `sample_par(1)` ≡ `sample()` only when both share the same master seed
  initialisation path; in practice these have separate code paths and
  are **not** byte-equivalent.

FGN ships a `sample_pair` fast path that produces two independent
paths in one FFT — a 2× shortcut over calling `sample()` twice while
maintaining exact independence.

## Acceleration

Default implementation: CPU SIMD via `f64x4` / `f32x8` where the sampler
allows. GPU backends (`cuda`, `metal`) are opt-in features and currently
ship for FGN / fBM only — see the [add-gpu-sampler SKILL](https://github.com/dancixx/stochastic-rs/blob/main/.claude/skills/add-gpu-sampler/SKILL.md).

## Construction patterns

Every process follows the same canonical `new(args, seed)` constructor
(see the [seeding concept page](/docs/concepts/seeding) for the full
design):

```rust
use stochastic_rs::simd_rng::{Deterministic, Unseeded};

let p = Foo::<T, _>::new(/* params */, n, x0, t, Unseeded);                  // auto-seeded
let p = Foo::<T, _>::new(/* params */, n, x0, t, Deterministic::new(42));    // reproducible
let p = Foo::<T, _>::new(/* params */, n, x0, t, shared_seed_source);        // chain seeds
```

Use `Deterministic` in tests so they replay bit-exactly. Pass a shared
seed source when chaining correlated processes — each `seed.rng()` /
`seed.rng_ext()` call atomically advances the internal counter, so the
streams diverge despite the shared root. `SeedExt::reseed(u64)` swaps a
`Deterministic` source in place to sweep seeds without rebuilding the
process.
