SiDB Simulation

Timeouts

SiDB timeouts use milliseconds: 2**64 - 1 (default) is unlimited; 0 expires immediately. Nested calls and workers share the enclosing deadline without extending it.

Application

Budget in its parameters

Gate design

operational_params.timeout

Circuit design, including defect-aware placement and routing

timeout

Operational checks and input-pattern analysis

timeout

Operational and critical-temperature domains, physical-validity sweeps

operational_params.timeout

Critical temperature, defect influence, displacement robustness and fabrication probability

operational_params.timeout

Operational-domain ratio

op_domain_params.operational_params.timeout

Population stability and time-to-solution

timeout

Band-bending resilience

assess_population_stability_params.timeout

Expiration throws fiction::utils::timeout_error in C++ or TimeoutError in Python, without publishing partial results or statistics. A completed result is returned even if the last operation or callback overruns the deadline. Circuit design aborts on a gate timeout; only a completed, unsuccessful gate search triggers defect-aware placement-and-routing retries.

Finite budgets support QuickExact, ExGS, and QuickSim. ClusterComplete rejects them with std::invalid_argument (ValueError in Python). QuickSim’s quicksim_params.timeout remains a separate per-attempt limit in time-to-solution measurements.

Cancellation is cooperative: individual operations and callbacks can overrun the budget before the next check, and cleanup callbacks can run afterward. Use a separate process for a hard deadline.

fiction::sidb::simulation::logic::operational_domain_params params{};
params.operational_params.timeout = 5'000;
const auto domain = fiction::sidb::simulation::logic::operational_domain_grid_search(layout, spec, params);
from mnt.pyfiction.sidb.simulation.logic import operational_domain_grid_search, operational_domain_params

params = operational_domain_params()
params.operational_params.timeout = 5_000
domain = operational_domain_grid_search(layout, spec, params)

Simulation Result

A simulation result holds one copy of the simulated layout and one charge_distribution per physically valid charge configuration, together with the algorithm name, runtime, and physical parameters.

Header: fiction/technology/sidb/simulation/result.hpp

struct result

The unified return type of every SiDB simulation algorithm: the name of the algorithm, its runtime, the layout it simulated, the physically valid charge distributions it found over that layout, the physical parameters, and optional algorithm-specific named parameters.

The layout is stored once; each charge distribution holds one charge state per SiDB in the layout’s raster order plus its energy.

Public Functions

inline model::charge_state charge_state(const std::size_t distribution, const lattice_site &s) const

The charge state of an SiDB in one of the charge distributions.

Parameters:
Throws:

std::out_of_range – if the distribution index is out of range.

Returns:

The charge state, charge_state::NONE if s holds no SiDB.

inline std::vector<charge_distribution> groundstates() const

The charge distributions of minimal energy. Distributions with identical charge states count once.

Note

If degenerate states exist in the simulation result, this function returns multiple ground states that all possess the same energy.

Returns:

The charge distributions with the minimal energy.

Public Members

std::string algorithm_name = {}

Name of the algorithm used to determine the charge distributions.

std::chrono::duration<double> simulation_runtime = {}

Total simulation runtime in seconds.

layout lyt = {}

The simulated layout.

std::vector<charge_distribution> charge_distributions = {}

Charge distributions determined by the algorithm.

model::simulation_parameters sim_params = {}

Physical parameters used in the simulation.

std::unordered_map<std::string, std::any> additional_simulation_parameters = {}

Additional named simulation parameters. This is used to store algorithm-dependent parameters that are not part of the sidb::model::simulation_parameters struct.

The key of the map is the name of the parameter, the element is the value of the parameter.

class sidb_simulation_result

The unified return type of every SiDB simulation algorithm: the name of the algorithm, its runtime, the layout it simulated, the physically valid charge distributions it found over that layout, the physical parameters, and optional algorithm-specific named parameters.

The layout is stored once; each charge distribution holds one charge state per SiDB in the layout’s raster order plus its energy.

property additional_simulation_parameters

Additional named simulation parameters. This is used to store algorithm-dependent parameters that are not part of the sidb::model::simulation_parameters struct.

The key of the map is the name of the parameter, the element is the value of the parameter.

property algorithm_name

Name of the algorithm used to determine the charge distributions.

property charge_distributions

Charge distributions determined by the algorithm.

charge_state(distribution: int, site: lattice_site) → sidb_charge_state

The charge state of an SiDB in one of the charge distributions.

Parameters:
  • distribution – Index into charge_distributions.

  • s – Site of the SiDB.

Returns:

The charge state, charge_state::NONE if s holds no SiDB.

Raises:

std::out_of_range – if the distribution index is out of range.

groundstates() → list[charge_distribution]

The charge distributions of minimal energy. Distributions with identical charge states count once.

Returns:

The charge distributions with the minimal energy.

Note

If degenerate states exist in the simulation result, this function returns multiple ground states that all possess the same energy.

property layout

The simulated layout.

property simulation_parameters

Physical parameters used in the simulation.

property simulation_runtime

Total simulation runtime in seconds.

Potential Landscape

The potential landscape stores the static electrostatics of one layout and parameter set: SiDB pair distances and potentials, defect and external potentials, and charge transition thresholds. Simulations share the immutable landscape across worker threads; it evaluates local potentials, energy, and physical validity for a charge_distribution.

Header: fiction/technology/sidb/simulation/potential_landscape.hpp

enum class fiction::sidb::simulation::charge_transition_threshold_bounds : uint8_t

The bounds a local potential is tested against to decide whether a charge state is population stable.

Values:

enumerator NEGATIVE_UPPER_BOUND

The upper bound check against mu_minus to validate DB-.

enumerator POSITIVE_LOWER_BOUND

The lower bound check against mu_plus to validate DB+.

enumerator NEUTRAL_LOWER_BOUND

The lower bound check against mu_minus to validate DB0.

enumerator NEUTRAL_UPPER_BOUND

The upper bound check against mu_plus to validate DB0.

class potential_landscape

The static electrostatics of an SiDB layout under one set of physical parameters: the screened Coulomb potential every SiDB exerts on every other, the potential the charged surface defects and the external sources exert on every SiDB, and the resulting charge transition thresholds. Nothing in it depends on the charge states, so one landscape serves every charge distribution of the layout and can be shared read-only by worker threads.

Distances are stored in nm, potentials in V, energies in eV.

Public Functions

inline explicit potential_landscape(const layout &lyt, const model::simulation_parameters &params = model::simulation_parameters{}, const std::unordered_map<lattice_site, double> &local_external_potential = {}, const double global_external_potential = 0.0)

Builds the landscape of a layout.

Parameters:
  • lyt – Layout; its charged surface defects enter the landscape.

  • params – Physical parameters.

  • local_external_potential – External potential per site (unit: V). A site holding a defect shifts the potential at that defect; a site holding an SiDB shifts the potential at that SiDB.

  • global_external_potential – External potential applied to every SiDB (unit: V).

Throws:

std::out_of_range – if a SiDB or defect site has an invalid lattice basis index.

inline const layout &get_layout() const noexcept

The layout the landscape describes.

Returns:

The layout.

inline const model::simulation_parameters &params() const noexcept

The physical parameters.

Returns:

The parameters.

inline std::size_t num_sidbs() const noexcept

Number of SiDBs.

Returns:

Number of SiDBs.

inline const charge_distribution::site_list &sites() const noexcept

The SiDB sites in raster order, shared with the charge distributions built over this landscape.

Returns:

The shared site list.

inline const std::vector<std::pair<lattice_site, model::defect>> &defects() const noexcept

The charged surface defects that enter the landscape.

Returns:

Site-defect pairs.

inline double nm_distance(const std::size_t i, const std::size_t j) const

Distance between two SiDBs.

Parameters:
  • i – Index of the first SiDB.

  • j – Index of the second SiDB.

Throws:

std::out_of_range – if an SiDB index is out of range.

Returns:

Distance (unit: nm).

inline double chargeless_potential(const std::size_t i, const std::size_t j) const

The chargeless potential one SiDB exerts on another, i.e., the potential of a unit charge at the distance of the two.

Parameters:
  • i – Index of the SiDB the potential acts on.

  • j – Index of the SiDB that exerts it.

Throws:

std::out_of_range – if an SiDB index is out of range.

Returns:

Chargeless potential (unit: V).

inline double local_external_potential(const std::size_t i) const

The external potential at an SiDB: its local external potential plus the global one.

Parameters:

i – Index of the SiDB.

Throws:

std::out_of_range – if an SiDB index is out of range.

Returns:

External potential (unit: V).

inline double local_potential_caused_by_defects(const std::size_t i) const

The potential the charged surface defects exert on an SiDB.

Parameters:

i – Index of the SiDB.

Throws:

std::out_of_range – if an SiDB index is out of range.

Returns:

Defect potential (unit: V).

inline const std::array<double, 4> &effective_charge_transition_thresholds(const std::size_t i) const

The charge transition thresholds of an SiDB, indexed by charge_transition_threshold_bounds.

Parameters:

i – Index of the SiDB.

Throws:

std::out_of_range – if an SiDB index is out of range.

Returns:

The four thresholds (unit: V).

inline double chargeless_potential_at_distance(const double distance) const noexcept

The chargeless potential of a unit charge at a distance under the landscape’s parameters.

Parameters:

distance – Distance (unit: nm).

Returns:

Chargeless potential (unit: V); 0 at distance 0.

inline double chargeless_potential_of_defect_at_distance(const double distance, const model::defect &defect) const noexcept

The chargeless potential a defect exerts at a distance, screened by the defect’s own permittivity and screening length.

Parameters:
  • distance – Distance (unit: nm).

  • defect – Defect.

Returns:

Chargeless potential (unit: V); 0 at distance 0.

inline std::vector<double> local_internal_potentials(const charge_distribution &cd) const

The local internal potentials of a charge distribution: at every SiDB, the potential of the charged SiDBs plus the potential of the charged defects. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Throws:

std::invalid_argument – if the distribution sites differ from the landscape.

Returns:

Local internal potential per SiDB (unit: V).

inline std::vector<double> local_potentials(const charge_distribution &cd) const

The local potentials of a charge distribution: internal plus external potential per SiDB. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Throws:

std::invalid_argument – if the distribution sites differ from the landscape.

Returns:

Local potential per SiDB (unit: V).

inline double energy(const charge_distribution &cd, const std::vector<double> &local_internal_potential) const

The electrostatic potential energy of a charge distribution given its local internal potentials, i.e., the energy of the SiDB charges in the potential of the other SiDBs, the defects, and the external sources, plus the energy of the defect charges. O(N + D·N) for D charged defects.

Parameters:
  • cd – Charge distribution over this landscape’s SiDBs.

  • local_internal_potential – Local internal potential per SiDB, as local_internal_potentials computes it.

Throws:
  • std::invalid_argument – if the distribution sites differ from the landscape.

  • std::invalid_argument – if the potential vector does not contain one value per SiDB.

Returns:

Energy (unit: eV).

inline double energy(const charge_distribution &cd) const

The electrostatic potential energy of a charge distribution. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Throws:

std::invalid_argument – if the distribution sites differ from the landscape.

Returns:

Energy (unit: eV).

inline bool is_population_stable(const charge_distribution &cd, const std::vector<double> &local_internal_potential) const

Whether every SiDB’s charge state is population stable under its local potential: a negative SiDB below the (0/-) transition, a positive one above the (+/0) transition, a neutral one in between.

Parameters:
  • cd – Charge distribution over this landscape’s SiDBs.

  • local_internal_potential – Local internal potential per SiDB.

Throws:
  • std::invalid_argument – if the distribution sites differ from the landscape.

  • std::invalid_argument – if the potential vector does not contain one value per SiDB.

Returns:

true iff the population stability holds for every SiDB.

inline bool is_configuration_stable(const charge_distribution &cd, const std::vector<double> &local_internal_potential) const

Whether no charge hop between two SiDBs lowers the energy, i.e., the distribution is configuration stable.

Parameters:
  • cd – Charge distribution over this landscape’s SiDBs.

  • local_internal_potential – Local internal potential per SiDB.

Throws:
  • std::invalid_argument – if the distribution sites differ from the landscape.

  • std::invalid_argument – if the potential vector does not contain one value per SiDB.

Returns:

true iff no energetically favored hop exists.

inline bool is_physically_valid(const charge_distribution &cd, const std::vector<double> &local_internal_potential) const

Whether a charge distribution is physically valid: population stable and configuration stable.

Parameters:
  • cd – Charge distribution over this landscape’s SiDBs.

  • local_internal_potential – Local internal potential per SiDB.

Throws:
  • std::invalid_argument – if the distribution sites differ from the landscape.

  • std::invalid_argument – if the potential vector does not contain one value per SiDB.

Returns:

true iff both stability criteria hold.

inline bool is_physically_valid(const charge_distribution &cd) const

Whether a charge distribution is physically valid. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Throws:

std::invalid_argument – if the distribution sites differ from the landscape.

Returns:

true iff both stability criteria hold.

inline charge_distribution evaluate(charge_distribution cd) const

Evaluates a charge distribution: returns a copy with its energy set. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Throws:

std::invalid_argument – if the distribution sites differ from the landscape.

Returns:

cd with its energy assigned.

class charge_transition_threshold_bounds(*values)

The bounds a local potential is tested against to decide whether a charge state is population stable.

NEGATIVE_UPPER_BOUND = 0
POSITIVE_LOWER_BOUND = 1
NEUTRAL_LOWER_BOUND = 2
NEUTRAL_UPPER_BOUND = 3
class potential_landscape(layout: sidb_layout, params: sidb_simulation_parameters = <mnt.pyfiction.sidb.model.sidb_simulation_parameters object at 0x579c36d842c0>, local_external_potential: collections.abc.Mapping[mnt.pyfiction.sidb.lattice_site, float]={}, global_external_potential: float = 0.0)

The static electrostatics of an SiDB layout under one set of physical parameters: the screened Coulomb potential every SiDB exerts on every other, the potential the charged surface defects and the external sources exert on every SiDB, and the resulting charge transition thresholds. Nothing in it depends on the charge states, so one landscape serves every charge distribution of the layout and can be shared read-only by worker threads.

Distances are stored in nm, potentials in V, energies in eV.

chargeless_potential(i: int, j: int) → float

The chargeless potential one SiDB exerts on another, i.e., the potential of a unit charge at the distance of the two.

Parameters:
  • i – Index of the SiDB the potential acts on.

  • j – Index of the SiDB that exerts it.

Returns:

V).

Return type:

Chargeless potential (unit

Raises:

std::out_of_range – if an SiDB index is out of range.

chargeless_potential_at_distance(distance: float) → float

The chargeless potential of a unit charge at a distance under the landscape’s parameters.

Parameters:

distance – Distance (unit: nm).

Returns:

V); 0 at distance 0.

Return type:

Chargeless potential (unit

defects() → list[tuple[lattice_site, sidb_defect]]

The charged surface defects that enter the landscape.

Returns:

Site-defect pairs.

effective_charge_transition_thresholds(i: int) → list[float]

The charge transition thresholds of an SiDB, indexed by charge_transition_threshold_bounds.

Parameters:

i – Index of the SiDB.

Returns:

V).

Return type:

The four thresholds (unit

Raises:

std::out_of_range – if an SiDB index is out of range.

energy(cd: charge_distribution) → float

The electrostatic potential energy of a charge distribution. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Returns:

eV).

Return type:

Energy (unit

Raises:

std::invalid_argument – if the distribution sites differ from the landscape.

evaluate(cd: charge_distribution) → charge_distribution

Evaluates a charge distribution: returns a copy with its energy set. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Returns:

cd with its energy assigned.

Raises:

std::invalid_argument – if the distribution sites differ from the landscape.

get_layout() → sidb_layout

The layout the landscape describes.

Returns:

The layout.

is_physically_valid(cd: charge_distribution) → bool

Whether a charge distribution is physically valid. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Returns:

true iff both stability criteria hold.

Raises:

std::invalid_argument – if the distribution sites differ from the landscape.

local_external_potential(i: int) → float

The external potential at an SiDB: its local external potential plus the global one.

Parameters:

i – Index of the SiDB.

Returns:

V).

Return type:

External potential (unit

Raises:

std::out_of_range – if an SiDB index is out of range.

local_internal_potentials(cd: charge_distribution) → list[float]

The local internal potentials of a charge distribution: at every SiDB, the potential of the charged SiDBs plus the potential of the charged defects. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Returns:

V).

Return type:

Local internal potential per SiDB (unit

Raises:

std::invalid_argument – if the distribution sites differ from the landscape.

local_potential_caused_by_defects(i: int) → float

The potential the charged surface defects exert on an SiDB.

Parameters:

i – Index of the SiDB.

Returns:

V).

Return type:

Defect potential (unit

Raises:

std::out_of_range – if an SiDB index is out of range.

local_potentials(cd: charge_distribution) → list[float]

The local potentials of a charge distribution: internal plus external potential per SiDB. O(N²).

Parameters:

cd – Charge distribution over this landscape’s SiDBs.

Returns:

V).

Return type:

Local potential per SiDB (unit

Raises:

std::invalid_argument – if the distribution sites differ from the landscape.

nm_distance(i: int, j: int) → float

Distance between two SiDBs.

Parameters:
  • i – Index of the first SiDB.

  • j – Index of the second SiDB.

Returns:

nm).

Return type:

Distance (unit

Raises:

std::out_of_range – if an SiDB index is out of range.

num_sidbs() → int

Number of SiDBs.

Returns:

Number of SiDBs.

params() → sidb_simulation_parameters

The physical parameters.

Returns:

The parameters.

sites() → list[lattice_site]

The SiDB sites in raster order, shared with the charge distributions built over this landscape.

Returns:

The shared site list.

Engine Selectors

Header: fiction/technology/sidb/simulation/engine.hpp

enum class fiction::sidb::simulation::engine : uint8_t

Selector for the available SiDB simulation engines.

Values:

enumerator EXGS

Exhaustive Ground State Search (EXGS) is an exact simulation engine that always has exponential runtime.

enumerator QUICKSIM

QuickSim is a heuristic simulation engine that only requires polynomial runtime.

enumerator QUICKEXACT

QuickExact is also an exact simulation engine that requires exponential runtime, but it scales a lot better than ExGS due to its effective search-space pruning.

enumerator CLUSTERCOMPLETE

ClusterComplete is a novel exact simulation engine that requires exponential runtime, though, depending on the simulation problem, it effectively reduces the base number by a real number, thus allowing problem sizes that were previously considered astronomical in size. Inherent to the simulation methodology that does not depend on the simulation base, it simulates very effectively for either base number (2 or 3).

enum class fiction::sidb::simulation::exact_engine : uint8_t

Selector exclusively for exact SiDB simulation engines.

Values:

enumerator EXGS

Exhaustive Ground State Search (EXGS) is an exact simulation engine that always has exponential runtime.

enumerator QUICKEXACT

QuickExact is also an exact simulation engine that requires exponential runtime, but it scales a lot better than ExGS due to its effective search-space pruning.

enumerator CLUSTERCOMPLETE

ClusterComplete is a novel exact simulation engine that requires exponential runtime, though, depending on the simulation problem, it effectively reduces the base number by a real number, thus allowing problem sizes that were previously considered astronomical. Inherent to the simulation methodology that does not depend on the simulation base, it simulates very effectively for either base number (2 or 3).

enum class fiction::sidb::simulation::heuristic_engine : uint8_t

Selector exclusively for heuristic SiDB simulation engines.

Values:

enumerator QUICKSIM

QuickSim is a heuristic simulation engine that only requires polynomial runtime.

template<typename EngineType>
std::string_view fiction::sidb::simulation::engine_name(const EngineType &eng) noexcept

Returns the name of the given simulation engine.

Template Parameters:

EngineType – The type of the SiDB simulation engine (exhaustive/heuristic/generic).

Parameters:

eng – An SiDB simulation engine.

Returns:

The name of the simulation engine.

class sidb_simulation_engine(*values)

Selector for the available SiDB simulation engines.

EXGS = 0
QUICKSIM = 1
QUICKEXACT = 2
CLUSTERCOMPLETE = 3
class exact_sidb_simulation_engine(*values)

Selector exclusively for exact SiDB simulation engines.

EXGS = 0
QUICKEXACT = 1
CLUSTERCOMPLETE = 2
class heuristic_sidb_simulation_engine(*values)

Selector exclusively for heuristic SiDB simulation engines.

QUICKSIM = 0
sidb_simulation_engine_name(engine: sidb_simulation_engine) → str
sidb_simulation_engine_name(engine: exact_sidb_simulation_engine) → str
sidb_simulation_engine_name(engine: heuristic_sidb_simulation_engine) → str

Returns the name of the given simulation engine.

Parameters:

eng – An SiDB simulation engine.

Template Args:

EngineType – The type of the SiDB simulation engine (exhaustive/heuristic/generic).

Returns:

The name of the simulation engine.

Simulation Equivalence Checking

Header: fiction/technology/sidb/simulation/check_simulation_results_for_equivalence.hpp

inline bool fiction::sidb::simulation::check_simulation_results_for_equivalence(result result1, result result2)

Whether two simulation results of the same layout are equivalent: the same number of distinct charge distributions, and, sorted by charge states, pairwise the same charge states and energies within ERROR_MARGIN.

Parameters:
  • result1 – First result.

  • result2 – Second result.

Returns:

true iff both results hold the same charge distributions.

check_simulation_results_for_equivalence(result1: sidb_simulation_result, result2: sidb_simulation_result) → bool

Whether two simulation results of the same layout are equivalent: the same number of distinct charge distributions, and, sorted by charge states, pairwise the same charge states and energies within ERROR_MARGIN.

Parameters:
  • result1 – First result.

  • result2 – Second result.

Returns:

true iff both results hold the same charge distributions.

Ground State Check

Header: fiction/technology/sidb/simulation/is_ground_state.hpp

inline bool fiction::sidb::simulation::is_ground_state(const result &heuristic_results, const result &exact_results) noexcept

Whether a heuristic simulation found every ground state an exact simulation found: the two results have the same number of ground states and every exact ground state appears among the heuristic ones.

Parameters:
  • heuristic_results – Result of a heuristic simulation.

  • exact_results – Result of an exact simulation of the same layout.

Returns:

true iff the heuristic result contains every ground state of the exact one.

is_ground_state(heuristic_results: sidb_simulation_result, exhaustive_results: sidb_simulation_result) → bool

Whether a heuristic simulation found every ground state an exact simulation found: the two results have the same number of ground states and every exact ground state appears among the heuristic ones.

Parameters:
  • heuristic_results – Result of a heuristic simulation.

  • exact_results – Result of an exact simulation of the same layout.

Returns:

true iff the heuristic result contains every ground state of the exact one.

Simulation Domain

Header: fiction/technology/sidb/simulation/domain.hpp

template<typename Key, typename ...MappedTypes>
class domain

The domain is designed to represent a generic simulation domain where keys are associated with values stored as tuples. It uses a locked_parallel_flat_hash_map to ensure thread-safe access to the stored data. All methods of this class are thread-safe.

Template Parameters:
  • Key – The type of the key used to identify entries in the domain.

  • MappedTypes – Value types stored in the tuple.

Public Functions

inline domain()

Constructs a new domain instance.

inline void add_value(const Key &key, const std::tuple<MappedTypes...> &value)

Adds a value to the operational domain. This function is thread-safe and uses the try_emplace method.

Parameters:
  • key – The key to associate with the value.

  • value – The value to add, which must be a tuple.

inline std::size_t size() const

Counts the number of key-value pairs in the operational domain.

Returns:

The size of the operational domain.

inline bool empty() const noexcept

Checks whether the operational domain is empty.

Returns:

true if the operational domain is empty, false otherwise.

template<typename Fn>
inline void for_each(Fn &&fn) const

Applies a callable to all key-value pairs in the container. For thread-safety, this function operates on a copy of the underlying map created at the time of the function call.

Template Parameters:

Fn – Functor type.

Parameters:

fn – Functor to apply to each key-value pair.

inline std::optional<std::tuple<MappedTypes...>> contains(const Key &key) const

Checks whether a specified key exists in the given map and retrieves its associated value if present. This function utilizes the if_contains method of the map to ensure thread-safe access.

Parameters:

key – The key to search for in the map.

Returns:

The value associated with the key if it exists, std::nullopt otherwise.