# [Backend Simulators](@id backend)

This page is the backend-focused companion to
[Choosing a Backend and Modeling Tradeoffs](@ref modeling-tradeoffs). It names
the current built-in backend families, the representation types that select
them, and the interface points used when attaching a new backend.

## Built-In Backend Families

### `QuantumClifford`

`CliffordRepr()` selects the stabilizer backend built on
`QuantumClifford.MixedDestabilizer`.

Use it when all of the following are approximately true:

- the modeled subsystems are qubits,
- the protocol stays near Clifford dynamics,
- the important noise can be represented as Pauli-like noise, and
- simulation scale matters more than generality.

This is the most specific built-in backend. In exchange for that restriction,
it is usually the cheapest option for repeater-style and stabilizer-native
network models.

### `QuantumOptics`

`QuantumOpticsRepr()` selects the general `QuantumOptics` backend, and
`QuantumMCRepr()` uses the same symbolic lowering path with a Monte Carlo style
state representation. For backgrounds with a Kraus representation,
`QuantumMCRepr()` samples a normalized pure-state trajectory instead of
converting the state to a density operator.

Slots using `QuantumMCRepr()` store that trajectory in an internal `MCKet`
wrapper around the underlying QuantumOptics `Ket`. The wrapper preserves Monte
Carlo semantics through initialization, instantaneous operations, compositions
whose factors are all `MCKet`s, and projective measurements. Composing with a
plain `Ket` produces a plain `Ket`, while composing with an `Operator` produces
an `Operator` after density-matrix promotion.

`ConstantHamiltonianEvolution` preserves `MCKet`: it uses Schrödinger evolution
without active backgrounds and Monte Carlo wave-function evolution when Lindblad
jump operators are present. Standalone background evolution also preserves
`MCKet`: it samples a Kraus branch when available and otherwise uses Monte Carlo
wave-function evolution with a zero Hamiltonian.

Partial trace also preserves `MCKet`. It samples the discarded subsystem in
that subsystem's native canonical basis and stores the corresponding conditional
pure-state trajectory. Individual trajectories therefore depend on this
canonical-basis unraveling, while their ensemble is the exact partial trace.
Use `QuantumOpticsRepr()` when an exact deterministic reduced density matrix is
needed, or [`project_traceout!`](@ref) when the sampled outcome itself is needed.
When one `traceout!` call includes every live slot of a shared state, the register
layer deletes the complete group without backend reduction or trajectory
sampling.

Because `MCKet` is the stored state type, `stateref.state[]` exposes it directly
and `StateRef` displays identify its implementation module as `QuantumSavory`.
Combining an `MCKet` with an existing `Operator` still promotes the ket through
`dm` and produces an `Operator`.

Use this family when:

- you need general qubit operations beyond the stabilizer regime,
- you need explicit ket or operator style simulation,
- you want one backend that can handle both qubits and bosonic modes, or
- you are validating a cheaper approximation on smaller systems.

This is the most flexible built-in path, but it also has the least structural
compression.

### `Gabs`

`GabsRepr(...)` selects the Gaussian backend from `Gabs`.

Use it when:

- the modeled subsystems are bosonic modes,
- the state stays in the Gaussian regime,
- the operations are Gaussian, and
- homodyne-style continuous-variable measurements are central to the model.

This is the right backend for continuous-variable models that would be awkward
or expensive to force into a generic wavefunction description.

## Choosing Precisely

The three built-in families answer different needs:

- `CliffordRepr()` for large qubit stabilizer workloads.
- `QuantumOpticsRepr()` for general qubit or mode simulations when flexibility
  matters more than asymptotic speed.
- `GabsRepr(...)` for Gaussian continuous-variable simulations.

If you are not sure which one to start with, the safest workflow is:

1. choose the cheapest backend that preserves the effect you care about;
2. validate that modeling choice on a smaller instance with a more general
   backend if needed.

## How Backend Selection Enters A Model

A register can specify representations slot by slot:

```julia
reg = Register(
    [Qubit(), Qumode()],
    [QuantumOpticsRepr(), GabsRepr(QuadBlockBasis)],
)
```

If you do not specify a representation, the slot trait decides the default.
Today, both `Qubit()` and `Qumode()` default to `QuantumOpticsRepr()`.

Symbolic states and operators cross the backend boundary through `express`.
That conversion is used by `initialize!`, `apply!`, `observable`, and related
register operations.

## Backend Extension Points

A new backend does not need to replace the register API. It needs to provide
the methods that let the register API lower symbolic objects and act on native
state types.

In practice, a backend integration usually defines:

- a representation type such as `CliffordRepr()`, `QuantumOpticsRepr()`, or
  `GabsRepr(...)`;
- `newstate(::QuantumStateTrait, ::YourRepr)` for empty-slot initialization;
- `default_repr(...)` for native backend state types;
- `nsubsystems` and `subsystemcompose` for factorized state management;
- native implementations of `apply!`, `observable`, `project_traceout!`, and
  `traceout!`;
- symbolic lowering through `express(..., ::YourRepr)` or `express_nolookup`;
- and, if the backend supports background evolution, `uptotime!` plus the
  background helpers it needs such as `paulinoise`, `krausops`, or
  `lindbladop`.

Those are the concrete points where the built-in backends connect today.

## What Changes And What Stays Stable

When you switch backends, the following usually stays the same:

- the register and network structure,
- the symbolic states and operators,
- the protocol control flow, and
- the metadata and messaging logic.

What changes is the numerical representation used once symbolic objects are
lowered and the set of operations that can be executed efficiently.

## Performance Considerations

Operations that span separately factorized states compose those states into a
larger tensor product. In particular, `apply!` and non-instant operations merge
the touched states, while `observable` builds a temporary composition
without changing the register. Repeated cross-state observables therefore repeat
that tensor-product work.

The cost of composition depends on the backend. `QuantumOpticsRepr()` tensors
state vectors or density operators; composing a ket with a density operator first
promotes the ket to a density operator. `CliffordRepr()` and `GabsRepr(...)` keep
their compact tableau and Gaussian representations, respectively.

Symbolic stabilizer projectors evaluate directly on selected, reordered subsystems
of pure or mixed Clifford tableaux. A dense `QuantumOpticsBase.Operator` observable
is however more problematic: for a pure Clifford state, QuantumSavory converts the
entire stabilizer state to a dense ket. Its size grows exponentially with the number
of qubits, even if the observable addresses only some subsystems.

## Where To Go Next

- Read [Choosing a Backend and Modeling Tradeoffs](@ref modeling-tradeoffs) for
  the higher-level modeling discussion.
- Read [Symbolic Frontend](@ref symbolic-frontend) for how symbolic objects
  reach these backends.
- Read [QuantumInterface.jl reference](API_Interface.md) for the representation
  types exposed to the docs.
