| defmodule BobQuantum do
|
| @moduledoc """
|
| BOB Quantum Civilization Engine - Elixir OTP Bridge
|
|
|
| High-performance quantum simulation engine with Rustler NIF bindings
|
| for cryptographic RNG, lattice evolution, and quantum state management.
|
| """
|
|
|
| use Rustler, otp_app: :bob_quantum, crate: "bob_quantum_nif"
|
|
|
| @type rng_ref :: reference()
|
| @type lattice_ref :: reference()
|
| @type state_ref :: reference()
|
| @type energy :: float()
|
| @type entropy :: float()
|
| @type amplitude :: {float(), float()}
|
| @type amplitudes :: [amplitude()]
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Initialize a new quantum RNG instance with entropy seed.
|
|
|
| Returns: {:ok, rng_ref} | {:error, :initialization_failed}
|
| """
|
| @spec rng_init(binary()) :: {:ok, rng_ref()} | {:error, atom()}
|
| def rng_init(_seed), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Generate uniform random float in [0, 1).
|
|
|
| Returns: {:ok, float()} | {:error, :rng_exhausted}
|
| """
|
| @spec rng_uniform(rng_ref()) :: {:ok, float()} | {:error, atom()}
|
| def rng_uniform(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Generate normally distributed random float (Box-Muller transform).
|
|
|
| Returns: {:ok, float()} | {:error, :rng_exhausted}
|
| """
|
| @spec rng_normal(rng_ref(), float(), float()) :: {:ok, float()} | {:error, atom()}
|
| def rng_normal(_ref, _mean, _stddev), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Generate cryptographically secure random integer in range [min, max].
|
|
|
| Returns: {:ok, integer()} | {:error, :invalid_range} | {:error, :rng_exhausted}
|
| """
|
| @spec rng_integer(rng_ref(), integer(), integer()) :: {:ok, integer()} | {:error, atom()}
|
| def rng_integer(_ref, _min, _max), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Reseed RNG with additional entropy.
|
|
|
| Returns: :ok | {:error, :reseed_failed}
|
| """
|
| @spec rng_reseed(rng_ref(), binary()) :: :ok | {:error, atom()}
|
| def rng_reseed(_ref, _entropy), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get RNG internal state for checkpointing.
|
|
|
| Returns: {:ok, binary()} | {:error, :state_capture_failed}
|
| """
|
| @spec rng_get_state(rng_ref()) :: {:ok, binary()} | {:error, atom()}
|
| def rng_get_state(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Restore RNG from checkpointed state.
|
|
|
| Returns: {:ok, rng_ref()} | {:error, :invalid_state}
|
| """
|
| @spec rng_set_state(binary()) :: {:ok, rng_ref()} | {:error, atom()}
|
| def rng_set_state(_state), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Initialize quantum lattice with dimensions and boundary conditions.
|
|
|
| Args:
|
| - dimensions: {x, y, z} tuple
|
| - boundary: :periodic | :open | :reflective
|
| - coupling: float() - interaction strength
|
| - temperature: float() - initial temperature
|
|
|
| Returns: {:ok, lattice_ref} | {:error, :invalid_parameters}
|
| """
|
| @spec lattice_init({integer(), integer(), integer()}, atom(), float(), float()) ::
|
| {:ok, lattice_ref()} | {:error, atom()}
|
| def lattice_init(_dims, _boundary, _coupling, _temp), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Evolve lattice by one Monte Carlo step using Metropolis-Hastings.
|
|
|
| Returns: {:ok, {energy(), entropy()}} | {:error, :evolution_failed}
|
| """
|
| @spec lattice_evolve(lattice_ref(), integer()) :: {:ok, {energy(), entropy()}} | {:error, atom()}
|
| def lattice_evolve(_ref, _steps), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Compute total system energy (Hamiltonian expectation value).
|
|
|
| Returns: {:ok, energy()} | {:error, :computation_failed}
|
| """
|
| @spec lattice_energy(lattice_ref()) :: {:ok, energy()} | {:error, atom()}
|
| def lattice_energy(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Compute von Neumann entropy of lattice density matrix.
|
|
|
| Returns: {:ok, entropy()} | {:error, :computation_failed}
|
| """
|
| @spec lattice_entropy(lattice_ref()) :: {:ok, entropy()} | {:error, atom()}
|
| def lattice_entropy(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get lattice correlation function at distance r.
|
|
|
| Returns: {:ok, float()} | {:error, :invalid_distance}
|
| """
|
| @spec lattice_correlation(lattice_ref(), integer()) :: {:ok, float()} | {:error, atom()}
|
| def lattice_correlation(_ref, _distance), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Measure local magnetization at site (x, y, z).
|
|
|
| Returns: {:ok, float()} | {:error, :invalid_site}
|
| """
|
| @spec lattice_magnetization(lattice_ref(), {integer(), integer(), integer()}) ::
|
| {:ok, float()} | {:error, atom()}
|
| def lattice_magnetization(_ref, _site), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Apply external field to lattice region.
|
|
|
| Returns: :ok | {:error, :invalid_region}
|
| """
|
| @spec lattice_apply_field(lattice_ref(), {integer(), integer(), integer()}, {integer(), integer(), integer()}, float()) ::
|
| :ok | {:error, atom()}
|
| def lattice_apply_field(_ref, _min, _max, _strength), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get lattice configuration snapshot for visualization.
|
|
|
| Returns: {:ok, binary()} | {:error, :snapshot_failed}
|
| """
|
| @spec lattice_snapshot(lattice_ref()) :: {:ok, binary()} | {:error, atom()}
|
| def lattice_snapshot(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Initialize quantum state vector with n qubits.
|
|
|
| Args:
|
| - n_qubits: integer() - number of qubits (max 32)
|
| - initial_state: :zero | :plus | :random | binary() (amplitude data)
|
|
|
| Returns: {:ok, state_ref} | {:error, :invalid_qubit_count} | {:error, :invalid_initial_state}
|
| """
|
| @spec state_init(integer(), atom() | binary()) :: {:ok, state_ref()} | {:error, atom()}
|
| def state_init(_n_qubits, _initial), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Apply single-qubit gate to quantum state.
|
|
|
| Gates: :h, :x, :y, :z, :s, :t, :rx, :ry, :rz, :u3
|
| For parameterized gates, params = [theta] or [theta, phi, lambda]
|
|
|
| Returns: :ok | {:error, :invalid_gate} | {:error, :invalid_qubit} | {:error, :invalid_params}
|
| """
|
| @spec state_apply_gate(state_ref(), atom(), integer(), [float()]) :: :ok | {:error, atom()}
|
| def state_apply_gate(_ref, _gate, _qubit, _params), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Apply two-qubit controlled gate.
|
|
|
| Gates: :cx, :cy, :cz, :crx, :cry, :crz, :swap, :iswap
|
|
|
| Returns: :ok | {:error, :invalid_gate} | {:error, :invalid_qubit}
|
| """
|
| @spec state_apply_controlled(state_ref(), atom(), integer(), integer(), [float()]) :: :ok | {:error, atom()}
|
| def state_apply_controlled(_ref, _gate, _control, _target, _params), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Measure qubit in computational basis.
|
|
|
| Returns: {:ok, 0 | 1, probability()} | {:error, :invalid_qubit} | {:error, :measurement_failed}
|
| """
|
| @spec state_measure(state_ref(), integer()) :: {:ok, 0 | 1, float()} | {:error, atom()}
|
| def state_measure(_ref, _qubit), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Measure multiple qubits simultaneously.
|
|
|
| Returns: {:ok, [0 | 1], probability()} | {:error, :invalid_qubits}
|
| """
|
| @spec state_measure_multi(state_ref(), [integer()]) :: {:ok, [0 | 1], float()} | {:error, atom()}
|
| def state_measure_multi(_ref, _qubits), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get full amplitude vector (2^n complex numbers as {re, im} tuples).
|
|
|
| Returns: {:ok, amplitudes()} | {:error, :state_too_large}
|
| """
|
| @spec state_amplitudes(state_ref()) :: {:ok, amplitudes()} | {:error, atom()}
|
| def state_amplitudes(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get reduced density matrix for subsystem.
|
|
|
| Returns: {:ok, amplitudes()} | {:error, :invalid_subsystem}
|
| """
|
| @spec state_reduced_density(state_ref(), [integer()]) :: {:ok, amplitudes()} | {:error, atom()}
|
| def state_reduced_density(_ref, _qubits), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Normalize quantum state vector (L2 norm = 1).
|
|
|
| Returns: :ok | {:error, :zero_norm}
|
| """
|
| @spec state_normalize(state_ref()) :: :ok | {:error, atom()}
|
| def state_normalize(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Compute expectation value of Pauli operator string.
|
|
|
| Operator: string of 'I', 'X', 'Y', 'Z' (e.g., "XYZI")
|
|
|
| Returns: {:ok, float()} | {:error, :invalid_operator}
|
| """
|
| @spec state_expectation(state_ref(), String.t()) :: {:ok, float()} | {:error, atom()}
|
| def state_expectation(_ref, _operator), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Compute fidelity with target state.
|
|
|
| Returns: {:ok, float()} | {:error, :dimension_mismatch}
|
| """
|
| @spec state_fidelity(state_ref(), amplitudes()) :: {:ok, float()} | {:error, atom()}
|
| def state_fidelity(_ref, _target), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Get entanglement entropy for bipartition.
|
|
|
| Returns: {:ok, entropy()} | {:error, :invalid_partition}
|
| """
|
| @spec state_entanglement_entropy(state_ref(), [integer()]) :: {:ok, entropy()} | {:error, atom()}
|
| def state_entanglement_entropy(_ref, _partition), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Apply quantum channel (Kraus operators).
|
|
|
| Returns: :ok | {:error, :invalid_kraus}
|
| """
|
| @spec state_apply_channel(state_ref(), [amplitudes()]) :: :ok | {:error, atom()}
|
| def state_apply_channel(_ref, _kraus), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
| @doc """
|
| Clone quantum state (deep copy).
|
|
|
| Returns: {:ok, state_ref()} | {:error, :clone_failed}
|
| """
|
| @spec state_clone(state_ref()) :: {:ok, state_ref()} | {:error, atom()}
|
| def state_clone(_ref), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
|
|
|
|
|
|
|
|
| defdelegate rng_init(seed), to: BobQuantum.RNG
|
| defdelegate rng_uniform(ref), to: BobQuantum.RNG
|
| defdelegate rng_normal(ref, mean, stddev), to: BobQuantum.RNG
|
| defdelegate rng_integer(ref, min, max), to: BobQuantum.RNG
|
| defdelegate rng_reseed(ref, entropy), to: BobQuantum.RNG
|
| defdelegate rng_get_state(ref), to: BobQuantum.RNG
|
| defdelegate rng_set_state(state), to: BobQuantum.RNG
|
|
|
| defdelegate lattice_init(dims, boundary, coupling, temp), to: BobQuantum.Lattice
|
| defdelegate lattice_evolve(ref, steps), to: BobQuantum.Lattice
|
| defdelegate lattice_energy(ref), to: BobQuantum.Lattice
|
| defdelegate lattice_entropy(ref), to: BobQuantum.Lattice
|
| defdelegate lattice_correlation(ref, distance), to: BobQuantum.Lattice
|
| defdelegate lattice_magnetization(ref, site), to: BobQuantum.Lattice
|
| defdelegate lattice_apply_field(ref, min, max, strength), to: BobQuantum.Lattice
|
| defdelegate lattice_snapshot(ref), to: BobQuantum.Lattice
|
|
|
| defdelegate state_init(n_qubits, initial), to: BobQuantum.State
|
| defdelegate state_apply_gate(ref, gate, qubit, params), to: BobQuantum.State
|
| defdelegate state_apply_controlled(ref, gate, control, target, params), to: BobQuantum.State
|
| defdelegate state_measure(ref, qubit), to: BobQuantum.State
|
| defdelegate state_measure_multi(ref, qubits), to: BobQuantum.State
|
| defdelegate state_amplitudes(ref), to: BobQuantum.State
|
| defdelegate state_reduced_density(ref, qubits), to: BobQuantum.State
|
| defdelegate state_normalize(ref), to: BobQuantum.State
|
| defdelegate state_expectation(ref, operator), to: BobQuantum.State
|
| defdelegate state_fidelity(ref, target), to: BobQuantum.State
|
| defdelegate state_entanglement_entropy(ref, partition), to: BobQuantum.State
|
| defdelegate state_apply_channel(ref, kraus), to: BobQuantum.State
|
| defdelegate state_clone(ref), to: BobQuantum.State
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Start all BobQuantum subsystems.
|
| Typically called during application startup.
|
| """
|
| @spec start() :: :ok | {:error, term()}
|
| def start do
|
| children = [
|
| {BobQuantum.RNG, name: :bob_quantum_rng},
|
| {BobQuantum.Lattice, name: :bob_quantum_lattice},
|
| {BobQuantum.State, name: :bob_quantum_state}
|
| ]
|
|
|
| opts = [strategy: :one_for_one, name: BobQuantum.Supervisor]
|
| Supervisor.start_link(children, opts)
|
| end
|
|
|
| @doc """
|
| Stop all BobQuantum subsystems gracefully.
|
| """
|
| @spec stop() :: :ok
|
| def stop do
|
| Supervisor.stop(BobQuantum.Supervisor)
|
| end
|
|
|
| @doc """
|
| Health check for all subsystems.
|
|
|
| Returns: %{rng: :ok | :error, lattice: :ok | :error, state: :ok | :error}
|
| """
|
| @spec health_check() :: map()
|
| def health_check do
|
| %{
|
| rng: check_rng(),
|
| lattice: check_lattice(),
|
| state: check_state()
|
| }
|
| end
|
|
|
| defp check_rng do
|
| case BobQuantum.RNG.uniform() do
|
| {:ok, _} -> :ok
|
| _ -> :error
|
| end
|
| rescue
|
| _ -> :error
|
| end
|
|
|
| defp check_lattice do
|
| case BobQuantum.Lattice.energy() do
|
| {:ok, _} -> :ok
|
| _ -> :error
|
| end
|
| rescue
|
| _ -> :error
|
| end
|
|
|
| defp check_state do
|
| case BobQuantum.State.amplitudes() do
|
| {:ok, _} -> :ok
|
| _ -> :error
|
| end
|
| rescue
|
| _ -> :error
|
| end
|
| end
|
|
|
|
|
|
|
|
|
|
|
| defmodule BobQuantum.Supervisor do
|
| use Supervisor
|
|
|
| @impl true
|
| def init(children) do
|
| Supervisor.init(children, strategy: :one_for_one)
|
| end
|
| end
|
|
|
|
|
|
|
|
|
|
|
| defmodule BobQuantum.RNG do
|
| @moduledoc """
|
| GenServer wrapper for quantum random number generator.
|
| Manages RNG lifecycle, state persistence, and concurrent access.
|
| """
|
|
|
| use GenServer
|
|
|
| @type state :: %{
|
| ref: BobQuantum.rng_ref(),
|
| algorithm: atom(),
|
| seed: binary(),
|
| generation_count: integer(),
|
| last_reseed: DateTime.t()
|
| }
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Start RNG GenServer with optional seed.
|
|
|
| Options:
|
| - :seed - binary entropy (default: crypto:strong_rand_bytes(32))
|
| - :algorithm - :chacha20 | :aes256_ctr | :philox (default: :chacha20)
|
| - :name - registration name (default: __MODULE__)
|
| """
|
| @spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}
|
| def start_link(opts \\ []) do
|
| name = Keyword.get(opts, :name, __MODULE__)
|
| GenServer.start_link(__MODULE__, opts, name: name)
|
| end
|
|
|
| @doc """
|
| Generate uniform random float in [0, 1).
|
| """
|
| @spec uniform() :: {:ok, float()} | {:error, :not_started}
|
| def uniform do
|
| call(__MODULE__, :uniform)
|
| end
|
|
|
| @spec uniform(pid()) :: {:ok, float()} | {:error, :not_started}
|
| def uniform(pid) do
|
| call(pid, :uniform)
|
| end
|
|
|
| @doc """
|
| Generate normally distributed random float.
|
| """
|
| @spec normal(mean :: float(), stddev :: float()) :: {:ok, float()} | {:error, :not_started}
|
| def normal(mean, stddev) do
|
| call(__MODULE__, {:normal, mean, stddev})
|
| end
|
|
|
| @spec normal(pid(), mean :: float(), stddev :: float()) :: {:ok, float()} | {:error, :not_started}
|
| def normal(pid, mean, stddev) do
|
| call(pid, {:normal, mean, stddev})
|
| end
|
|
|
| @doc """
|
| Generate random integer in range [min, max].
|
| """
|
| @spec integer(min :: integer(), max :: integer()) :: {:ok, integer()} | {:error, :not_started | :invalid_range}
|
| def integer(min, max) do
|
| call(__MODULE__, {:integer, min, max})
|
| end
|
|
|
| @spec integer(pid(), min :: integer(), max :: integer()) :: {:ok, integer()} | {:error, :not_started | :invalid_range}
|
| def integer(pid, min, max) do
|
| call(pid, {:integer, min, max})
|
| end
|
|
|
| @doc """
|
| Reseed RNG with additional entropy.
|
| """
|
| @spec reseed(entropy :: binary()) :: :ok | {:error, :not_started | :reseed_failed}
|
| def reseed(entropy) do
|
| call(__MODULE__, {:reseed, entropy})
|
| end
|
|
|
| @spec reseed(pid(), entropy :: binary()) :: :ok | {:error, :not_started | :reseed_failed}
|
| def reseed(pid, entropy) do
|
| call(pid, {:reseed, entropy})
|
| end
|
|
|
| @doc """
|
| Get current RNG state for checkpointing.
|
| """
|
| @spec get_state() :: {:ok, binary()} | {:error, :not_started}
|
| def get_state do
|
| call(__MODULE__, :get_state)
|
| end
|
|
|
| @spec get_state(pid()) :: {:ok, binary()} | {:error, :not_started}
|
| def get_state(pid) do
|
| call(pid, :get_state)
|
| end
|
|
|
| @doc """
|
| Get server statistics.
|
| """
|
| @spec stats() :: {:ok, map()} | {:error, :not_started}
|
| def stats do
|
| call(__MODULE__, :stats)
|
| end
|
|
|
| @spec stats(pid()) :: {:ok, map()} | {:error, :not_started}
|
| def stats(pid) do
|
| call(pid, :stats)
|
| end
|
|
|
|
|
|
|
|
|
|
|
| @impl true
|
| def init(opts) do
|
| seed = Keyword.get(opts, :seed, :crypto.strong_rand_bytes(32))
|
| algorithm = Keyword.get(opts, :algorithm, :chacha20)
|
|
|
| case BobQuantum.rng_init(seed) do
|
| {:ok, ref} ->
|
| state = %{
|
| ref: ref,
|
| algorithm: algorithm,
|
| seed: seed,
|
| generation_count: 0,
|
| last_reseed: DateTime.utc_now()
|
| }
|
| {:ok, state}
|
|
|
| {:error, reason} ->
|
| {:stop, reason}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:uniform, _from, state) do
|
| case BobQuantum.rng_uniform(state.ref) do
|
| {:ok, value} ->
|
| {:reply, {:ok, value}, update_count(state)}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:normal, mean, stddev}, _from, state) do
|
| case BobQuantum.rng_normal(state.ref, mean, stddev) do
|
| {:ok, value} ->
|
| {:reply, {:ok, value}, update_count(state)}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:integer, min, max}, _from, state) when min <= max do
|
| case BobQuantum.rng_integer(state.ref, min, max) do
|
| {:ok, value} ->
|
| {:reply, {:ok, value}, update_count(state)}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:integer, _min, _max}, _from, state) do
|
| {:reply, {:error, :invalid_range}, state}
|
| end
|
|
|
| @impl true
|
| def handle_call({:reseed, entropy}, _from, state) do
|
| case BobQuantum.rng_reseed(state.ref, entropy) do
|
| :ok ->
|
| {:reply, :ok, %{state | last_reseed: DateTime.utc_now(), generation_count: 0}}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:get_state, _from, state) do
|
| case BobQuantum.rng_get_state(state.ref) do
|
| {:ok, data} ->
|
| {:reply, {:ok, data}, state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:stats, _from, state) do
|
| {:reply, {:ok, format_stats(state)}, state}
|
| end
|
|
|
| @impl true
|
| def handle_cast(_msg, state) do
|
| {:noreply, state}
|
| end
|
|
|
| @impl true
|
| def handle_info(_info, state) do
|
| {:noreply, state}
|
| end
|
|
|
| @impl true
|
| def terminate(_reason, state) do
|
|
|
| :ok
|
| end
|
|
|
| @impl true
|
| def code_change(_old_vsn, state, _extra) do
|
| {:ok, state}
|
| end
|
|
|
|
|
|
|
|
|
|
|
| defp update_count(state) do
|
| %{state | generation_count: state.generation_count + 1}
|
| end
|
|
|
| defp format_stats(state) do
|
| %{
|
| algorithm: state.algorithm,
|
| generation_count: state.generation_count,
|
| last_reseed: state.last_reseed,
|
| uptime: DateTime.diff(DateTime.utc_now(), state.last_reseed, :second)
|
| }
|
| end
|
| end
|
|
|
|
|
|
|
|
|
|
|
| defmodule BobQuantum.Lattice do
|
| @moduledoc """
|
| GenServer wrapper for quantum lattice simulation.
|
| Manages lattice evolution, thermodynamic quantities, and spatial correlations.
|
| """
|
|
|
| use GenServer
|
|
|
| @type state :: %{
|
| ref: BobQuantum.lattice_ref(),
|
| dimensions: {integer(), integer(), integer()},
|
| boundary: atom(),
|
| coupling: float(),
|
| temperature: float(),
|
| step_count: integer(),
|
| last_energy: float(),
|
| last_entropy: float()
|
| }
|
|
|
|
|
|
|
|
|
|
|
| @doc """
|
| Start Lattice GenServer.
|
|
|
| Options:
|
| - :dimensions - {x, y, z} tuple (default: {16, 16, 16})
|
| - :boundary - :periodic | :open | :reflective (default: :periodic)
|
| - :coupling - interaction strength (default: 1.0)
|
| - :temperature - initial temperature (default: 2.0)
|
| - :name - registration name (default: __MODULE__)
|
| """
|
| @spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}
|
| def start_link(opts \\ []) do
|
| name = Keyword.get(opts, :name, __MODULE__)
|
| GenServer.start_link(__MODULE__, opts, name: name)
|
| end
|
|
|
| @doc """
|
| Evolve lattice by n Monte Carlo steps.
|
| """
|
| @spec evolve(steps :: integer()) :: {:ok, {energy(), entropy()}} | {:error, :not_started}
|
| def evolve(steps) do
|
| call(__MODULE__, {:evolve, steps})
|
| end
|
|
|
| @spec evolve(pid(), steps :: integer()) :: {:ok, {energy(), entropy()}} | {:error, :not_started}
|
| def evolve(pid, steps) do
|
| call(pid, {:evolve, steps})
|
| end
|
|
|
| @doc """
|
| Get current system energy.
|
| """
|
| @spec energy() :: {:ok, energy()} | {:error, :not_started}
|
| def energy do
|
| call(__MODULE__, :energy)
|
| end
|
|
|
| @spec energy(pid()) :: {:ok, energy()} | {:error, :not_started}
|
| def energy(pid) do
|
| call(pid, :energy)
|
| end
|
|
|
| @doc """
|
| Get current system entropy.
|
| """
|
| @spec entropy() :: {:ok, entropy()} | {:error, :not_started}
|
| def entropy do
|
| call(__MODULE__, :entropy)
|
| end
|
|
|
| @spec entropy(pid()) :: {:ok, entropy()} | {:error, :not_started}
|
| def entropy(pid) do
|
| call(pid, :entropy)
|
| end
|
|
|
| @doc """
|
| Get correlation function at distance r.
|
| """
|
| @spec correlation(distance :: integer()) :: {:ok, float()} | {:error, :not_started | :invalid_distance}
|
| def correlation(distance) do
|
| call(__MODULE__, {:correlation, distance})
|
| end
|
|
|
| @spec correlation(pid(), distance :: integer()) :: {:ok, float()} | {:error, :not_started | :invalid_distance}
|
| def correlation(pid, distance) do
|
| call(pid, {:correlation, distance})
|
| end
|
|
|
| @doc """
|
| Get magnetization at site.
|
| """
|
| @spec magnetization(site :: {integer(), integer(), integer()}) :: {:ok, float()} | {:error, :not_started | :invalid_site}
|
| def magnetization(site) do
|
| call(__MODULE__, {:magnetization, site})
|
| end
|
|
|
| @spec magnetization(pid(), site :: {integer(), integer(), integer()}) :: {:ok, float()} | {:error, :not_started | :invalid_site}
|
| def magnetization(pid, site) do
|
| call(pid, {:magnetization, site})
|
| end
|
|
|
| @doc """
|
| Apply external field to region.
|
| """
|
| @spec apply_field(min :: {integer(), integer(), integer()}, max :: {integer(), integer(), integer()}, strength :: float()) :: :ok | {:error, :not_started | :invalid_region}
|
| def apply_field(min, max, strength) do
|
| call(__MODULE__, {:apply_field, min, max, strength})
|
| end
|
|
|
| @spec apply_field(pid(), min :: {integer(), integer(), integer()}, max :: {integer(), integer(), integer()}, strength :: float()) :: :ok | {:error, :not_started | :invalid_region}
|
| def apply_field(pid, min, max, strength) do
|
| call(pid, {:apply_field, min, max, strength})
|
| end
|
|
|
| @doc """
|
| Get lattice snapshot for visualization.
|
| """
|
| @spec snapshot() :: {:ok, binary()} | {:error, :not_started}
|
| def snapshot do
|
| call(__MODULE__, :snapshot)
|
| end
|
|
|
| @spec snapshot(pid()) :: {:ok, binary()} | {:error, :not_started}
|
| def snapshot(pid) do
|
| call(pid, :snapshot)
|
| end
|
|
|
| @doc """
|
| Get server statistics.
|
| """
|
| @spec stats() :: {:ok, map()} | {:error, :not_started}
|
| def stats do
|
| call(__MODULE__, :stats)
|
| end
|
|
|
| @spec stats(pid()) :: {:ok, map()} | {:error, :not_started}
|
| def stats(pid) do
|
| call(pid, :stats)
|
| end
|
|
|
|
|
|
|
|
|
|
|
| @impl true
|
| def init(opts) do
|
| dimensions = Keyword.get(opts, :dimensions, {16, 16, 16})
|
| boundary = Keyword.get(opts, :boundary, :periodic)
|
| coupling = Keyword.get(opts, :coupling, 1.0)
|
| temperature = Keyword.get(opts, :temperature, 2.0)
|
|
|
| case BobQuantum.lattice_init(dimensions, boundary, coupling, temperature) do
|
| {:ok, ref} ->
|
| state = %{
|
| ref: ref,
|
| dimensions: dimensions,
|
| boundary: boundary,
|
| coupling: coupling,
|
| temperature: temperature,
|
| step_count: 0,
|
| last_energy: 0.0,
|
| last_entropy: 0.0
|
| }
|
| {:ok, state}
|
|
|
| {:error, reason} ->
|
| {:stop, reason}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:evolve, steps}, _from, state) when steps > 0 do
|
| case BobQuantum.lattice_evolve(state.ref, steps) do
|
| {:ok, {energy, entropy}} ->
|
| new_state = %{
|
| state
|
| | step_count: state.step_count + steps,
|
| last_energy: energy,
|
| last_entropy: entropy
|
| }
|
| {:reply, {:ok, {energy, entropy}}, new_state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:evolve, _steps}, _from, state) do
|
| {:reply, {:error, :invalid_steps}, state}
|
| end
|
|
|
| @impl true
|
| def handle_call(:energy, _from, state) do
|
| case BobQuantum.lattice_energy(state.ref) do
|
| {:ok, energy} ->
|
| {:reply, {:ok, energy}, %{state | last_energy: energy}}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:entropy, _from, state) do
|
| case BobQuantum.lattice_entropy(state.ref) do
|
| {:ok, entropy} ->
|
| {:reply, {:ok, entropy}, %{state | last_entropy: entropy}}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:correlation, distance}, _from, state) do
|
| case BobQuantum.lattice_correlation(state.ref, distance) do
|
| {:ok, value} ->
|
| {:reply, {:ok, value}, state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:magnetization, site}, _from, state) do
|
| case BobQuantum.lattice_magnetization(state.ref, site) do
|
| {:ok, value} ->
|
| {:reply, {:ok, value}, state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call({:apply_field, min, max, strength}, _from, state) do
|
| case BobQuantum.lattice_apply_field(state.ref, min, max, strength) do
|
| :ok ->
|
| {:reply, :ok, state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:snapshot, _from, state) do
|
| case BobQuantum.lattice_snapshot(state.ref) do
|
| {:ok, data} ->
|
| {:reply, {:ok, data}, state}
|
| {:error, reason} ->
|
| {:reply, {:error, reason}, state}
|
| end
|
| end
|
|
|
| @impl true
|
| def handle_call(:stats, _from, state) do
|
| {:reply, {:ok, format |