statevector

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 17, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package statevector implements a full statevector quantum simulator supporting up to 28 qubits.

The simulator stores the full quantum state as a []complex128 slice of length 2^n, where each element is the probability amplitude for the corresponding computational basis state. Gates are applied in-place via the block-stride pattern: for a gate on qubit k, the 2^n amplitudes are partitioned into blocks of size 2^(k+1). Within each block, the first 2^k indices have bit k = 0 and the second 2^k have bit k = 1. Each index i in the first half pairs with i + 2^k in the second half, and the gate's 2x2 unitary is applied to that (amplitude[i], amplitude[i+2^k]) pair. This pattern generalizes to 2-qubit gates via nested block strides over two bit positions, and to N-qubit gates via bitmask-based index construction.

For circuits with 17 or more qubits (2^17 = 131,072 amplitudes), gate application is automatically parallelized across available cores. Below that threshold, goroutine scheduling overhead exceeds the benefit of parallelism for the simple multiply-accumulate inner loop, so a single goroutine is used.

Entry points:

  • Sim.Run resets the state to |0...0>, evolves through the circuit, then samples measurement outcomes via inverse-CDF sampling.
  • Sim.Evolve resets and evolves without measuring, leaving the statevector accessible via Sim.StateVector for inspection, expectation values, or further manipulation.
  • Sim.StateVector returns a defensive copy of the current amplitudes.

Package statevector implements a full statevector quantum simulator.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Sim

type Sim struct {
	// contains filtered or unexported fields
}

Sim simulates a circuit via full statevector evolution.

func New

func New(numQubits int) *Sim

New creates a simulator initialized to |0...0>.

func (*Sim) Apply added in v1.2.0

func (s *Sim) Apply(c *ir.Circuit) error

Apply applies the circuit's gate operations to the current state without resetting. Use this to compose circuits incrementally:

sim.Evolve(circuitA) // resets to |0>, then applies A
sim.Apply(circuitB)  // applies B to the state left by A

func (*Sim) Close added in v1.2.0

func (s *Sim) Close() error

Close is a no-op for the CPU statevector simulator. It satisfies sim.Simulator.

func (*Sim) Evolve

func (s *Sim) Evolve(c *ir.Circuit) error

Evolve resets the state to |0...0> and then applies the circuit's gate operations without measuring. The resulting statevector is accessible via Sim.StateVector. To apply gates without resetting (e.g. to compose circuits incrementally), use Sim.Apply instead.

Example
package main

import (
	"fmt"
	"math"

	"github.com/splch/goqu/circuit/builder"
	"github.com/splch/goqu/sim/statevector"
)

func main() {
	// Build a Bell circuit and inspect the statevector.
	c, err := builder.New("bell", 2).
		H(0).
		CNOT(0, 1).
		Build()
	if err != nil {
		panic(err)
	}

	sim := statevector.New(2)
	if err := sim.Evolve(c); err != nil {
		panic(err)
	}

	// The Bell state has non-zero amplitudes only for |00> and |11>.
	sv := sim.StateVector()
	for i, amp := range sv {
		p := real(amp)*real(amp) + imag(amp)*imag(amp)
		if p > 1e-10 {
			fmt.Printf("|%02b|^2 = %.1f\n", i, math.Round(p*10)/10)
		}
	}
}
Output:

|00|^2 = 0.5
|11|^2 = 0.5

func (*Sim) ExpectPauliString

func (s *Sim) ExpectPauliString(ps pauli.PauliString) float64

ExpectPauliString computes Re(⟨psi|P|psi⟩) for a Pauli string P. For Hermitian observables (real coefficients), the imaginary part is zero. For non-Hermitian observables, use pauli.Expect directly for complex128.

func (*Sim) ExpectPauliSum

func (s *Sim) ExpectPauliSum(ps pauli.PauliSum) float64

ExpectPauliSum computes Re(⟨psi|H|psi⟩) for a Hamiltonian H (sum of Pauli strings). For Hermitian observables (real coefficients), the imaginary part is zero. For non-Hermitian observables, use pauli.ExpectSum directly for complex128.

func (*Sim) ExpectationValue

func (s *Sim) ExpectationValue(qubits []int) float64

ExpectationValue computes <psi|O|psi> for a diagonal Pauli-Z observable specified as a list of qubit indices. For example, [0, 1] computes <Z0 Z1>. The result is rounded to 14 decimal places to clean up floating-point noise.

func (*Sim) Reset added in v1.2.0

func (s *Sim) Reset()

Reset returns the simulator state to |0...0>.

func (*Sim) Run

func (s *Sim) Run(c *ir.Circuit, shots int) (map[string]int, error)

Run executes the circuit and returns measurement counts. For dynamic circuits (mid-circuit measurement, feed-forward, reset), it automatically uses per-shot simulation with state collapse.

Example
package main

import (
	"fmt"

	"github.com/splch/goqu/circuit/builder"
	"github.com/splch/goqu/sim/statevector"
)

func main() {
	// Build a circuit that always produces |11>.
	c, err := builder.New("x2", 2).
		X(0).
		X(1).
		MeasureAll().
		Build()
	if err != nil {
		panic(err)
	}

	sim := statevector.New(2)
	counts, err := sim.Run(c, 100)
	if err != nil {
		panic(err)
	}
	fmt.Printf("11: %d shots\n", counts["11"])
}
Output:

11: 100 shots

func (*Sim) RunDynamic

func (s *Sim) RunDynamic(c *ir.Circuit, shots int) (map[string]int, error)

RunDynamic executes a dynamic circuit (mid-circuit measurement, feed-forward, reset) by simulating each shot independently with projective measurement and state collapse.

func (*Sim) StateVector

func (s *Sim) StateVector() []complex128

StateVector returns a copy of the current statevector.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL