This page documents the public Bernstein-Vazirani family APIs in lindblad::algorithms.
- Header:
include/lindblad/algorithms.hpp - Namespace:
lindblad::algorithms
The family includes five solvers:
BernsteinVaziraniRecursiveBernsteinVaziraniProbabilisticBernsteinVaziraniDistributedBernsteinVaziraniQuditBernsteinVazirani(d-dimensional generalisation, d ≥ 2)
The first four build circuits over qubits with one shared ancilla and interpret
sampled bitstrings as secrets. QuditBernsteinVazirani runs on the qudit layer
(QuditStatevector / QuditGates / QuditSimulator — documented in
docs/api/qudit.md) and recovers an integer-valued secret in Z_d^n.
Signature:
static QuantumCircuit build_circuit(const QuantumCircuit& oracle, int n);Behavior (verified against src/algorithms/bernstein_vazirani.cpp):
- Allocates
n + 1qubits andnclassical bits - Prepares the ancilla as |1> then applies H to all qubits
- Appends the oracle instructions
- Applies H to the query register
- Measures the query register into classical bits 0..n-1
Signature:
static Result solve(
const QuantumCircuit& oracle,
int n,
int shots = 1,
uint64_t seed = 0
);Behavior:
- Builds the circuit via
build_circuit - Runs
StatevectorSimulator::run - Selects the most-frequent bitstring via
std::max_elementwith a count comparator - Reverses the string to convert MSB-first ordering into index order
- Returns the reversed string as the secret
Bitstring handling detail:
- Per-shot execution records only the
nquery qubits (0..n-1) into then-bit classical register; bitstrings have lengthn - No
substrstripping is needed: the ancilla is never written to the classical register - The
n-bit MSB-first string is reversed so thatsecret[i] == '1'means bitiof the hidden string is set
Signature:
static Result solve(
const std::vector<QuantumCircuit>& oracles,
int n,
int shots = 1,
uint64_t seed = 0
);Behavior:
- Executes
BernsteinVazirani::solveonce per oracle - Increments
seedbylevelto keep runs independent - Returns
depth,total_oracle_calls, and asecretsvector
Signature:
static Result solve(
const std::vector<QuantumCircuit>& oracle_pool,
int n,
const std::vector<double>& weights = {},
int shots = 50,
uint64_t seed = 0
);Behavior:
- Builds a discrete distribution from
weights(uniform if empty) - For each shot:
- samples an oracle index
- runs
BernsteinVazirani::solvewithshots = 1 - increments the key count
- Populates
discovered_keysas sorted unique keys
secret: recovered key (index-order string)
secrets: recovered keys per depth leveldepth: number of oracle levelstotal_oracle_calls: total oracle invocations (one per level)
discovered_keys: sorted unique keys foundkey_counts: map from key to countshots_used: total number of shots requested
struct Party {
QuantumCircuit local_oracle; // (n_bits + 1) qubits: 0..n_bits-1 query, n_bits ancilla
int n_bits; // number of bits this party holds
};Signature:
static QuantumCircuit build_circuit(const std::vector<Party>& parties);Behavior:
- Computes
n_total = Σ party.n_bits; allocates(n_total + 1)qubits,n_totalclassical bits - Prepares ancilla as |1⟩ then applies H to all qubits
- For each party, remaps oracle qubit indices:
- local
k < n_bits→offset + k(query slice) - local
k == n_bits→n_total(shared ancilla)
- local
- Appends H to the full query register, then measures it
Signature:
static Result solve(const std::vector<Party>& parties,
int shots = 1, uint64_t seed = 0);Behavior:
- Builds the circuit via
build_circuit - Runs
StatevectorSimulator::run - Selects the most-frequent bitstring, reverses it to index order
- Slices the full secret into per-party portions matching each
party.n_bits
full_secret: complete n-bit recovered secret (index order)party_secrets: per-party slices offull_secret, lengthn_jeachnum_parties: number of parties ttotal_bits: total n = Σ n_jquantum_rounds: always 1classical_rounds: equalsnum_parties
The d-dimensional BV variant runs on qudits rather than qubits. The secret is
an integer vector s ∈ Z_d^n and the oracle computes f(x) = s·x mod d.
Operates on the qudit layer documented in docs/api/qudit.md.
struct Result {
std::vector<int> secret; // recovered s, each element in {0..d-1}
int d; // qudit dimension used
int n; // number of query qudits
};Signature:
static Result solve(
const std::vector<int>& secret,
int d,
int shots = 1,
uint64_t seed = 0
);Behavior (verified against src/algorithms/qudit_bv.cpp):
- Validates inputs (
d >= 2,secretnon-empty, allsecret[i]in[0, d)) - Constructs a
QuditStatevectorof(n + 1)qudits and dimensiond - Prepares the ancilla in
|−⟩_dviaX_d^{d-1}thenF_d - Applies
F_dto each query qudit - For every
iwithsecret[i] != 0, applies the controlled-ADD gateCADD_{s_i}from query quditito the ancilla - Applies
F_d†to each query qudit - Calls
QuditSimulator::run, which measures the full register once - Accumulates per-position votes across
shots(only the firstnqudits; the ancilla is ignored) - Returns the per-position argmax as the recovered secret
For exact statevector simulation, shots = 1 is sufficient — the protocol is
deterministic. Higher shot counts exist for future noise-aware experiments.
Signature:
static std::vector<Complex128> oracle_gate(int d, int s_i);Returns the d²×d² CADD gate matrix for secret component s_i. Equivalent to
qudit_gates::cadd_matrix(d, s_i). Exposed for testing and inspection.
secret: recovereds ∈ Z_d^n, length nd: qudit dimension usedn: number of query qudits
solve() throws std::invalid_argument if:
d < 2secretis empty- any
secret[i]is outside[0, d)
- docs/algorithms/bernstein-vazirani.md
- docs/api/qudit.md — qudit layer API (state vector, gates, simulator)
- docs/APIOverview.md