This page documents the public lindblad::Statevector class.
- Header:
include/lindblad/statevector.hpp - Namespace:
lindblad
Statevector stores a quantum state in a structure-of-arrays layout with
separate aligned real and imaginary buffers. Qubit index q corresponds to
bit position q in the amplitude index (qubit 0 is the least significant bit).
The class is move-only; copy construction and copy assignment are disabled.
explicit Statevector(int n_qubits);Behavior:
- Accepts
n_qubitsin [1, 30] - Throws
std::invalid_argumentoutside that range - Allocates aligned buffers and initializes to |0...0>
Initialization helpers:
initialize()resets to |0...0>initialize_basis(k)sets |k> and throws ifk >= dim
amplitude(index)returns the complex amplitude atindexamplitudes()returns a full vector copy of amplitudesprobability(index)andprobabilities()compute |amp|^2
void set_amplitudes(const double* real, const double* imag, size_t count,
ValidationOptions validation = {});
void set_amplitudes(const std::vector<Complex128>& amplitudes,
ValidationOptions validation = {});This is the one point at which a caller hands a whole state over, so it is where
normalization is judged. The default policy is Throw, matching every other
physical-validity check.
Throw(default): an unnormalized hand-over raisesstd::invalid_argumentnaming the residual and the toleranceFix: the amplitudes are accepted and renormalizedWarn: reported through the warning handler, then accepted unchangedIgnore: no check, at the cost of one branch
sv.set_amplitudes(amps); // must already be normalized
sv.set_amplitudes(amps, {Validation::Fix}); // normalize on the way in
sv.set_amplitudes(amps, {Validation::Ignore}); // deliberately unnormalizedIgnore is the right choice when the amplitudes are not meant to be a physical
state, for instance when probing index arithmetic with a deliberately arbitrary
vector.
The policy is judged against the caller's buffer before anything is written, so a hand-over that is refused leaves the object holding whatever it held before, not the amplitudes that were just rejected.
norm_sq()andnorm()compute the squared norm and norm. Both use an OpenMP reduction, so their last bits depend on the thread count. They are for computation, not for comparing against a tolerance.normalize()scales to unit norm. It throwsstd::runtime_errorwhen there is no norm to divide out, which is a zero or non-finite state. It does not return an unnormalized state quietly, because a caller who asked for normalization and received none has been told nothing.is_normalized(atol)is a predicate: it answers, and neither repairs nor throws. A non-finite state answers false. Defaults toDEFAULT_PHYSICAL_ATOL.check_normalized(validation)applies a policy to the state as it stands, with the same four behaviours asset_amplitudesabove.
is_normalized and check_normalized measure through a summation that does not
depend on thread count or vector width, which is why they do not simply call
norm_sq(). A verdict that moved with the number of free cores would not be a
verdict.
Complex128 inner_product(const Statevector& other) const;- Throws
std::invalid_argumentif dimensions differ - Computes sum_i conj(this_i) * other_i
std::string measure_once(uint64_t seed = 0) const;
std::unordered_map<std::string, int> sample_counts(int shots, uint64_t seed = 0) const;Behavior:
seed == 0usesstd::random_deviceto seed the RNGmeasure_oncedoes a linear scan of cumulative probabilitysample_countsprecomputes cumulative probabilities and useslower_bound- Bitstrings are returned MSB-first (leftmost char is the most significant bit)
clone()returns a deep copy of the statevectorto_string(precision)prints up to 32 non-zero basis states with a ket-style label and probability annotation
#include "lindblad/statevector.hpp"
#include "lindblad/gates.hpp"
using namespace lindblad;
int main() {
Statevector sv(2);
gates::apply_h(sv, 0);
auto counts = sv.sample_counts(1000, 42);
return counts.empty() ? 1 : 0;
}