diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 8f009b738..cef2d5faf 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -9,6 +9,26 @@ add_executable(fiction ${SOURCES}) # Link against the project settings, libfiction and alice target_link_libraries(fiction PRIVATE libfiction alice) +# Compile-time decisions on which flows to compile + +# Logic synthesis flow +option(FICTION_LOGIC_SYNTHESIS_FLOW "Enable the logic synthesis flow for the fiction CLI" ON) +if(FICTION_LOGIC_SYNTHESIS_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_LOGIC_SYNTHESIS_FLOW) +endif() + +# Physical design flow +option(FICTION_PHYSICAL_DESIGN_FLOW "Enable the physical design flow for the fiction CLI" ON) +if(FICTION_PHYSICAL_DESIGN_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_PHYSICAL_DESIGN_FLOW) +endif() + +# Physical simulation flow +option(FICTION_SIMULATION_FLOW "Enable the physical simulation flow for the fiction CLI" ON) +if(FICTION_SIMULATION_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_SIMULATION_FLOW) +endif() + # Strip the executable if we are in Release mode if(CMAKE_BUILD_TYPE STREQUAL "Release") if(CMAKE_STRIP) diff --git a/cli/cmd/io/read.hpp b/cli/cmd/io/read.hpp index e28441c89..18901b79b 100644 --- a/cli/cmd/io/read.hpp +++ b/cli/cmd/io/read.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -23,9 +24,9 @@ namespace alice * * Currently parses Verilog, AIGER, and BLIF using the lorina parsers. * - * Parses FGL and FQCA via custom reader functions. - * * For more information see: https://github.com/hriener/lorina + * + * Parses FGL, SQD, and FQCA via custom reader functions. */ class read_command : public command { @@ -40,54 +41,57 @@ class read_command : public command "which will be put into the respective store. Current supported file types are:\n" "Logic networks: Verilog, AIGER, BLIF.\n" "Gate-level layouts: FGL.\n" - "Cell-level layouts: FQCA.\n" + "Cell-level layouts: SQD, FQCA.\n" "In a directory, only files with extension '.v', '.aig', '.blif' are considered.") { add_option("filename", filename, "Filename or directory")->required(); add_option("topology", topology, "Topology for gate-level layouts. Can be 'cartesian' or of the form " "'__'"); - add_flag("--aig,-a", "Parse file as AIG"); - add_flag("--xag,-x", "Parse file as XAG"); - add_flag("--mig,-m", "Parse file as MIG"); - add_flag("--tec,-t", "Parse file as technology network"); - add_flag("--fgl,-f", "Parse file as fiction gate-level layout"); - add_flag("--qca,-q", "Parse file as QCA cell-level layout"); - add_flag("--sort,-s", sort, "Sort networks in given directory by vertex count prior to storing them"); + add_flag("--aig,-a", "Parse Verilog file as AIG"); + add_flag("--xag,-x", "Parse Verilog file as XAG"); + add_flag("--mig,-m", "Parse Verilog file as MIG"); + add_flag("--tec,-t", "Parse Verilog file as technology network"); + add_flag("--fgl,-f", "Parse FGL file as fiction gate-level layout"); + add_flag("--sqd,-s", "Parse SQD file as SiDB cell-level layout"); + add_flag("--fqca,-q", "Parse FQCA file as QCA cell-level layout"); + add_flag("--sort", sort, "Sort networks in given directory by node count prior to storing them"); } protected: /** - * Function to perform the read call. Reads Verilog and creates a logic_network. + * Function to perform the read call. Reads a network or layout from a file. */ void execute() override { - const auto store_ntks = [&](auto&& reader) - { - for (const auto& ln : reader.get_networks(sort)) - { - store().extend() = ln; - } - }; - - if (!is_set("aig") && !is_set("xag") && !is_set("mig") && !is_set("tec") && !is_set("fgl") && !is_set("qca")) + if (!is_set("aig") && !is_set("xag") && !is_set("mig") && !is_set("tec") && !is_set("fgl") && !is_set("sqd") && + !is_set("fqca")) { env->out() << "[e] at least one network or layout type must be specified" << std::endl; } - else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("fql")) + else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("fgl")) { env->out() << "[e] cannot parse files as both logic networks and gate-level layouts" << std::endl; } - else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("qca")) + else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && + (is_set("sqd") || is_set("fqca"))) { env->out() << "[e] cannot parse files as both logic networks and cell-level layouts" << std::endl; } - else if (is_set("fql") && is_set("qca")) + else if (is_set("fgl") && (is_set("sqd") || is_set("fqca"))) { env->out() << "[e] cannot parse files as both gate-level and cell-level layouts" << std::endl; } else { + const auto store_ntks = [&](auto&& reader) + { + for (const auto& ln : reader.get_networks(sort)) + { + store().extend() = ln; + } + }; + try { if (is_set("aig")) @@ -114,7 +118,7 @@ class read_command : public command store_ntks(reader); } - if (is_set("fgl") || is_set("qca")) + if (is_set("fgl") || is_set("sqd") || is_set("fqca")) { if (std::filesystem::exists(filename)) { @@ -205,7 +209,23 @@ class read_command : public command << std::endl; } } - if (is_set("qca")) + else if (is_set("sqd")) + { + try + { + const auto layout_name = std::filesystem::path{filename}.stem().string(); + + store().extend() = + std::make_shared( + fiction::read_sqd_layout(filename, + layout_name)); + } + catch (const fiction::sqd_parsing_error& e) + { + env->out() << e.what() << std::endl; + } + } + else if (is_set("fqca")) { try { @@ -250,7 +270,7 @@ class read_command : public command } catch (...) { - env->out() << "[e] no networks or layouts were read" << std::endl; + env->out() << "[e] I/O error: no file could be read" << std::endl; } } diff --git a/cli/cmd/simulation/opdom.hpp b/cli/cmd/simulation/opdom.hpp new file mode 100644 index 000000000..d1e2b495a --- /dev/null +++ b/cli/cmd/simulation/opdom.hpp @@ -0,0 +1,341 @@ +// +// Created by marcel on 12.12.23. +// + +#ifndef FICTION_CMD_OPDOM_HPP +#define FICTION_CMD_OPDOM_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class opdom_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit opdom_command(const environment::ptr& e) : + command(e, "Opdom is a quick and exact electrostatic ground state simulation algorithm designed " + "specifically for SiDB layouts. It provides a significant performance advantage of more than " + "three orders of magnitude over ExGS from SiQAD.") + { + add_option("--random_sampling,-r", num_random_samples, + "Use random sampling instead of grid search with this many random samples"); + add_option("--flood_fill,-f", num_random_samples, + "Use flood fill instead of grid search with this many initial random samples"); + add_option("--contour_tracing,-c", num_random_samples, + "Use contour tracing instead of grid search with up to this many random " + "samples"); + + add_option("filename", filename, "CSV filename to write the operational domain to")->required(); + + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + + add_option("--x_sweep", x_sweep, "Sweep parameter of the x dimension [epsilon_r, lambda_tf, mu_minus]", true); + add_option("--y_sweep", y_sweep, "Sweep parameter of the y dimension [epsilon_r, lambda_tf, mu_minus]", true); + + add_option("--x_min", params.x_min, "Minimum value of the x dimension sweep", true); + add_option("--x_max", params.x_max, "Maximum value of the x dimension sweep", true); + add_option("--x_step", params.x_step, "Step size of the x dimension sweep", true); + add_option("--y_min", params.y_min, "Minimum value of the y dimension sweep", true); + add_option("--y_max", params.y_max, "Maximum value of the y dimension sweep", true); + add_option("--y_step", params.y_step, "Step size of the y dimension sweep", true); + } + + protected: + /** + * Function to perform the operational domain call. + */ + void execute() override + { + // reset operational domain and stats + op_domain = {}; + stats = {}; + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + // check for valid x and y parameter bounds + if (params.x_min >= params.x_max) + { + env->out() << "[e] x_min must be smaller than x_max" << std::endl; + reset_params(); + return; + } + if (params.y_min >= params.y_max) + { + env->out() << "[e] y_min must be smaller than y_max" << std::endl; + reset_params(); + return; + } + + // make sure that at most one algorithm is selected + const std::array algorithm_selections = {is_set("random_sampling"), is_set("flood_fill"), + is_set("contour_tracing")}; + if (std::count(algorithm_selections.cbegin(), algorithm_selections.cend(), true) > 1) + { + env->out() << "[e] only one algorithm can be selected at a time" << std::endl; + reset_params(); + return; + } + + auto& cs = store(); + + // error case: empty cell layout store + if (cs.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + auto& ts = store(); + + // error case: empty truth table store + if (ts.empty()) + { + env->out() << "[w] no truth table in store" << std::endl; + reset_params(); + return; + } + + // default sweep parameters + if (x_sweep.empty() && y_sweep.empty()) + { + x_sweep = "epsilon_r"; + y_sweep = "lambda_tf"; + } + else + { + // overwrite x and y sweep with their respective lower-case string representations + std::transform(x_sweep.begin(), x_sweep.end(), x_sweep.begin(), ::tolower); + std::transform(y_sweep.begin(), y_sweep.end(), y_sweep.begin(), ::tolower); + + static constexpr const std::array valid_sweep_params = {"epsilon_r", "lambda_tf", "mu_minus"}; + + // check if x sweep parameter is valid + if (std::find(valid_sweep_params.cbegin(), valid_sweep_params.cend(), x_sweep) == valid_sweep_params.cend()) + { + env->out() << "[e] invalid x sweep parameter \"" << x_sweep + << "\". Has to be one of [epsilon_r, lambda_tf, " + "mu_minus]" + << std::endl; + reset_params(); + return; + } + + // check if y sweep parameter is valid + if (std::find(valid_sweep_params.cbegin(), valid_sweep_params.cend(), y_sweep) == valid_sweep_params.cend()) + { + env->out() << "[e] invalid y sweep parameter \"" << y_sweep + << "\". Has to be one of [epsilon_r, lambda_tf, " + "mu_minus]" + << std::endl; + reset_params(); + return; + } + } + + // assign x sweep parameters + if (x_sweep == "epsilon_r") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::EPSILON_R; + } + else if (x_sweep == "lambda_tf") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::LAMBDA_TF; + } + else if (x_sweep == "mu_minus") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::MU_MINUS; + } + + // assign y sweep parameters + if (y_sweep == "epsilon_r") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::EPSILON_R; + } + else if (y_sweep == "lambda_tf") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::LAMBDA_TF; + } + else if (y_sweep == "mu_minus") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::MU_MINUS; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto opdom = [this, &ts, &get_name](auto&& lyt_ptr) + { + const auto tt_ptr = ts.current(); + + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if (lyt_ptr->num_pis() == 0 || lyt_ptr->num_pos() == 0) + { + env->out() << fmt::format("[e] {} requires primary input and output cells to simulate its " + "Boolean function", + get_name(lyt_ptr)) + << std::endl; + return; + } + + params.sim_params = physical_params; + + if (is_set("random_sampling")) + { + op_domain = fiction::operational_domain_random_sampling(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else if (is_set("flood_fill")) + { + op_domain = fiction::operational_domain_flood_fill(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else if (is_set("contour_tracing")) + { + op_domain = fiction::operational_domain_contour_tracing(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else + { + op_domain = fiction::operational_domain_grid_search(*lyt_ptr, std::vector{*tt_ptr}, + params, &stats); + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(opdom, cs.current()); + + write_op_domain(); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * Operational domain parameters. + */ + fiction::operational_domain_params params{}; + /** + * Operational domain stats. + */ + fiction::operational_domain_stats stats{}; + /** + * Number of random samples. + */ + std::size_t num_random_samples{}; + /** + * User input for the x dimension sweep parameter. + */ + std::string x_sweep{}; + /** + * User input for the y dimension sweep parameter. + */ + std::string y_sweep{}; + /** + * CSV filename to write the operational domain to. + */ + std::string filename{}; + /** + * The operational domain. + */ + fiction::operational_domain op_domain{}; + + /** + * Writes the operational domain to the specified CSV file. + */ + void write_op_domain() const + { + static const fiction::write_operational_domain_params write_opdom_params{"1", "0"}; + try + { + fiction::write_operational_domain(op_domain, filename, write_opdom_params); + } + catch (const std::exception& e) + { + env->out() << fmt::format("[e] {}", e.what()) << std::endl; + } + } + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the operational domain. + */ + [[nodiscard]] nlohmann::json log() const override + { + return nlohmann::json{ + {"Algorithm name", "QuickExact"}, + {"Runtime in seconds", mockturtle::to_seconds(stats.time_total)}, + {"Number of simulator invocations", stats.num_simulator_invocations}, + {"Number of evaluated parameter combinations", stats.num_evaluated_parameter_combinations}, + {"Number of operational parameter combinations", stats.num_operational_parameter_combinations}, + {"Number of non-operational parameter combinations", stats.num_non_operational_parameter_combinations}}; + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + x_sweep = {}; + y_sweep = {}; + filename = {}; + } +}; + +ALICE_ADD_COMMAND(opdom, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_OPDOM_HPP diff --git a/cli/cmd/simulation/quickexact.hpp b/cli/cmd/simulation/quickexact.hpp new file mode 100644 index 000000000..9114f9cdd --- /dev/null +++ b/cli/cmd/simulation/quickexact.hpp @@ -0,0 +1,195 @@ +// +// Created by marcel on 06.12.23. +// + +#ifndef FICTION_CMD_QUICKEXACT_HPP +#define FICTION_CMD_QUICKEXACT_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class quickexact_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit quickexact_command(const environment::ptr& e) : + command(e, "QuickExact is a quick and exact electrostatic ground state simulation algorithm designed " + "specifically for SiDB layouts. It provides a significant performance advantage of more than " + "three orders of magnitude over ExGS from SiQAD.") + { + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + add_option("--global_potential,-g", params.global_potential, + "Global potential applied to the entire layout (unit: V)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset sim result + sim_result = {}; + min_energy = std::numeric_limits::infinity(); + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + auto& s = store(); + + // error case: empty cell layout store + if (s.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto quickexact = [this, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.physical_parameters = physical_params; + + sim_result = fiction::quickexact(*lyt_ptr, params); + + if (sim_result.charge_distributions.empty()) + { + env->out() << fmt::format("[e] ground state of {} could not be determined", get_name(lyt_ptr)) + << std::endl; + } + else + { + const auto min_energy_distr = fiction::minimum_energy_distribution( + sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()); + + min_energy = min_energy_distr->get_system_energy(); + + store().extend() = + std::make_shared(*min_energy_distr); + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(quickexact, s.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * QuickExact parameters. + */ + fiction::quickexact_params params{}; + /** + * Simulation result. + */ + fiction::sidb_simulation_result sim_result{}; + /** + * Minimum energy. + */ + double min_energy{std::numeric_limits::infinity()}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + try + { + return nlohmann::json{ + {"Algorithm name", sim_result.algorithm_name}, + {"Simulation runtime", sim_result.simulation_runtime.count()}, + {"Physical parameters", + {{"base", std::any_cast(sim_result.additional_simulation_parameters.at( + "base_number"))}, // fetch the automatically inferred base number + {"epsilon_r", sim_result.physical_parameters.epsilon_r}, + {"lambda_tf", sim_result.physical_parameters.lambda_tf}, + {"mu_minus", sim_result.physical_parameters.mu_minus}, + {"global_potential", + std::any_cast(sim_result.additional_simulation_parameters.at("global_potential"))}}}, + {"Ground state energy (eV)", min_energy}, + {"Number of stable states", sim_result.charge_distributions.size()}}; + } + catch (...) + { + return nlohmann::json{}; + } + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(quickexact, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_QUICKEXACT_HPP diff --git a/cli/cmd/simulation/quicksim.hpp b/cli/cmd/simulation/quicksim.hpp new file mode 100644 index 000000000..93acb708b --- /dev/null +++ b/cli/cmd/simulation/quicksim.hpp @@ -0,0 +1,203 @@ +// +// Created by marcel on 07.12.23. +// + +#ifndef FICTION_CMD_QUICKSIM_HPP +#define FICTION_CMD_QUICKSIM_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class quicksim_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit quicksim_command(const environment::ptr& e) : + command(e, + "The QuickSim algorithm is a heuristic electrostatic ground state simulation algorithm for SiDB " + "layouts. It determines physically valid charge configurations (with minimal energy). Depending on " + "the simulation parameters, the ground state is found with a certain probability after one run.") + { + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + add_option("--iterations,-i", params.interation_steps, "Number of iterations to run the simulation for", true); + add_option("--alpha,-a", params.alpha, + "alpha parameter (should be reduced if not charge distribution can be determined)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset sim result + sim_result = {}; + min_energy = std::numeric_limits::infinity(); + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + if (params.alpha <= 0) + { + env->out() << "[e] alpha must be positive" << std::endl; + reset_params(); + return; + } + + auto& s = store(); + + // error case: empty cell layout store + if (s.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto quicksim = [this, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.phys_params = physical_params; + + sim_result = fiction::quicksim(*lyt_ptr, params); + + if (sim_result.charge_distributions.empty()) + { + env->out() << fmt::format("[e] no stable charge distribution could be determined for {}", + get_name(lyt_ptr)) + << std::endl; + } + else + { + const auto min_energy_distr = fiction::minimum_energy_distribution( + sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()); + + min_energy = min_energy_distr->get_system_energy(); + + store().extend() = + std::make_shared(*min_energy_distr); + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(quicksim, s.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * QuickSim parameters. + */ + fiction::quicksim_params params{}; + /** + * Simulation result. + */ + fiction::sidb_simulation_result sim_result{}; + /** + * Minimum energy. + */ + double min_energy{std::numeric_limits::infinity()}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + try + { + return nlohmann::json{ + {"Algorithm name", sim_result.algorithm_name}, + {"Simulation runtime", sim_result.simulation_runtime.count()}, + {"Physical parameters", + {{"epsilon_r", sim_result.physical_parameters.epsilon_r}, + {"lambda_tf", sim_result.physical_parameters.lambda_tf}, + {"mu_minus", sim_result.physical_parameters.mu_minus}}}, + {"Lowest state energy (eV)", min_energy}, + {"Number of stable states", sim_result.charge_distributions.size()}, + {"Iteration steps", + std::any_cast(sim_result.additional_simulation_parameters.at("iteration_steps"))}, + {"alpha", std::any_cast(sim_result.additional_simulation_parameters.at("alpha"))}}; + } + catch (...) + { + return nlohmann::json{}; + } + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(quicksim, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_QUICKSIM_HPP diff --git a/cli/cmd/simulation/temp.hpp b/cli/cmd/simulation/temp.hpp new file mode 100644 index 000000000..f19c6294a --- /dev/null +++ b/cli/cmd/simulation/temp.hpp @@ -0,0 +1,234 @@ +// +// Created by marcel on 06.12.23. +// + +#ifndef FICTION_CMD_TEMP_HPP +#define FICTION_CMD_TEMP_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class temp_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit temp_command(const environment::ptr& e) : + command(e, "Temperature-aware simulation of SiDB layouts. Uses QuickExact to determine the critical " + "temperature at which the ground state of the current SiDB layout is populated with a " + "probability below a given confidence level. For gate-based simulation, the probability of " + "erroneous calculations of the gate are applied.") + { + add_option( + "--confidence_level,-c", params.confidence_level, + "Probability threshold for ground state population. The temperature at which the simulation finds the " + "ground state to be populated with a probability of less than the given percentage, is determined to be " + "the critical temperature. For gate-based simulation, this is the probability of erroneous calculations of " + "the gate.", + true); + add_option("--max_temperature,-t", params.max_temperature, + "Maximum simulation temperature beyond which no simulation will be conducted (~ 126 °C by default) " + "(unit: K)", + true); + add_flag("--gate_based,-g", + "Gate-based simulation that matches the current SiDB layout in store against the current truth table " + "in store and considers the probability of erroneous calculations of the gate."); + + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset statistics + stats = {}; + + if (params.confidence_level <= 0 || params.confidence_level > 1) + { + env->out() << "[e] confidence_level must be in (0, 1]" << std::endl; + reset_params(); + return; + } + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + auto& cs = store(); + + // error case: empty cell layout store + if (cs.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + auto& ts = store(); + + // error case: empty truth table store + if (is_set("gate_based")) + { + if (ts.empty()) + { + env->out() << "[w] no truth table in store" << std::endl; + reset_params(); + return; + } + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto temp = [this, &ts, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.physical_parameters = physical_params; + + if (is_set("gate_based")) + { + if (lyt_ptr->num_pis() == 0 || lyt_ptr->num_pos() == 0) + { + env->out() << fmt::format("[e] {} requires primary input and output cells to simulate its " + "Boolean function", + get_name(lyt_ptr)) + << std::endl; + return; + } + + const auto tt_ptr = ts.current(); + + fiction::critical_temperature_gate_based(*lyt_ptr, std::vector{*tt_ptr}, params, + &stats); + } + else + { + fiction::critical_temperature_non_gate_based(*lyt_ptr, params, &stats); + } + + if (stats.num_valid_lyt == 0) + { + env->out() << fmt::format("[e] ground state of {} could not be determined", get_name(lyt_ptr)) + << std::endl; + } + else + { + env->out() << fmt::format("[i] critical temperature of {} is {}{} K", get_name(lyt_ptr), + (stats.critical_temperature == params.max_temperature ? "> " : ""), + stats.critical_temperature) + << std::endl; + + if (stats.num_valid_lyt > 1) + { + env->out() << fmt::format( + "[i] energy between the ground state and the first erroneous is {} meV", + fiction::round_to_n_decimal_places( + stats.energy_between_ground_state_and_first_erroneous, 2)) + << std::endl; + } + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(temp, cs.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * Critical temperature parameters. + */ + fiction::critical_temperature_params params{}; + /** + * Critical temperature statistics. + */ + fiction::critical_temperature_stats stats{}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + return nlohmann::json{{"Algorithm name", stats.algorithm_name}, + {"Physical parameters", + {{"base", stats.physical_parameters.base}, + {"epsilon_r", stats.physical_parameters.epsilon_r}, + {"lambda_tf", stats.physical_parameters.lambda_tf}, + {"mu_minus", stats.physical_parameters.mu_minus}}}, + {"Critical temperature", stats.critical_temperature}, + {"Number of stable states", stats.num_valid_lyt}, + {"Energy difference between ground state and first erroneous state", + stats.energy_between_ground_state_and_first_erroneous}}; + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(temp, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_TEMP_HPP diff --git a/cli/commands.hpp b/cli/commands.hpp index 523d55ab8..8e37a7b96 100644 --- a/cli/commands.hpp +++ b/cli/commands.hpp @@ -5,8 +5,11 @@ #ifndef FICTION_COMMANDS_HPP #define FICTION_COMMANDS_HPP +// general commands #include "cmd/general/clear.hpp" #include "cmd/general/version.hpp" + +// input/output commands #include "cmd/io/blif.hpp" #include "cmd/io/fgl.hpp" #include "cmd/io/fqca.hpp" @@ -17,6 +20,9 @@ #include "cmd/io/sqd.hpp" #include "cmd/io/tt.hpp" #include "cmd/io/verilog.hpp" + +// logic synthesis commands +#ifdef FICTION_LOGIC_SYNTHESIS_FLOW #include "cmd/logic/akers.hpp" #include "cmd/logic/balance.hpp" #include "cmd/logic/fanouts.hpp" @@ -26,6 +32,10 @@ #include "cmd/logic/miginvprop.hpp" #include "cmd/logic/random.hpp" #include "cmd/logic/simulate.hpp" +#endif + +// physical design and validation commands +#ifdef FICTION_PHYSICAL_DESIGN_FLOW #include "cmd/physical_design/exact.hpp" #include "cmd/physical_design/hex.hpp" #include "cmd/physical_design/onepass.hpp" @@ -36,5 +46,14 @@ #include "cmd/technology/energy.hpp" #include "cmd/verification/check.hpp" #include "cmd/verification/equiv.hpp" +#endif + +// physical simulation commands +#ifdef FICTION_SIMULATION_FLOW +#include "cmd/simulation/opdom.hpp" +#include "cmd/simulation/quickexact.hpp" +#include "cmd/simulation/quicksim.hpp" +#include "cmd/simulation/temp.hpp" +#endif #endif // FICTION_COMMANDS_HPP diff --git a/cli/stores.hpp b/cli/stores.hpp index 0817dcfd4..9a0761632 100644 --- a/cli/stores.hpp +++ b/cli/stores.hpp @@ -24,6 +24,7 @@ namespace alice { + /** * Truth tables. * @@ -163,7 +164,7 @@ ALICE_ADD_STORE(fiction::gate_layout_t, "gate_layout", "g", "gate layout", "gate ALICE_PRINT_STORE(fiction::gate_layout_t, os, layout) { - const auto print = [&os](auto&& lyt_ptr) { fiction::print_gate_level_layout(os, *lyt_ptr); }; + const auto print = [&os](auto&& lyt_ptr) { fiction::print_layout(*lyt_ptr, os); }; std::visit(print, layout); } @@ -373,7 +374,7 @@ ALICE_ADD_STORE(fiction::cell_layout_t, "cell_layout", "c", "cell layout", "cell ALICE_PRINT_STORE(fiction::cell_layout_t, os, layout) { - const auto print = [&os](auto&& lyt_ptr) { fiction::print_cell_level_layout(os, *lyt_ptr); }; + const auto print = [&os](auto&& lyt_ptr) { fiction::print_layout(*lyt_ptr, os); }; std::visit(print, layout); } @@ -391,10 +392,10 @@ ALICE_DESCRIBE_STORE(fiction::cell_layout_t, layout) z = lyt_ptr->z() + 1; } - return fmt::format("{} ({}) - {} × {}{}, I/O: {}/{}, cells: {}", lyt_ptr->get_layout_name(), + return fmt::format("{} ({}) - {} × {}{}, I/O: {}/{}, {}: {}", lyt_ptr->get_layout_name(), fiction::tech_impl_name>, lyt_ptr->x() + 1, lyt_ptr->y() + 1, (z ? fmt::format(" × {}", z) : ""), lyt_ptr->num_pis(), lyt_ptr->num_pos(), - lyt_ptr->num_cells()); + fiction::tech_cell_name>, lyt_ptr->num_cells()); }; return std::visit(describe, layout); @@ -413,10 +414,10 @@ ALICE_PRINT_STORE_STATISTICS(fiction::cell_layout_t, os, layout) z = lyt_ptr->z() + 1; } - os << fmt::format("[i] {} ({}) - {} × {}{}, I/O: {}/{}, cells: {}\n", lyt_ptr->get_layout_name(), + os << fmt::format("[i] {} ({}) - {} × {}{}, I/O: {}/{}, {}: {}\n", lyt_ptr->get_layout_name(), fiction::tech_impl_name>, lyt_ptr->x() + 1, lyt_ptr->y() + 1, (z ? fmt::format(" × {}", z) : ""), lyt_ptr->num_pis(), lyt_ptr->num_pos(), - lyt_ptr->num_cells()); + fiction::tech_cell_name>, lyt_ptr->num_cells()); }; std::visit(print_statistics, layout); @@ -432,7 +433,7 @@ ALICE_LOG_STORE_STATISTICS(fiction::cell_layout_t, layout) {"technology", fiction::tech_impl_name>}, {"inputs", lyt_ptr->num_pis()}, {"outputs", lyt_ptr->num_pos()}, - {"cells", lyt_ptr->num_cells()}, + {fiction::tech_cell_name>, lyt_ptr->num_cells()}, {"layout", {{"x-size", lyt_ptr->x() + 1}, {"y-size", lyt_ptr->y() + 1}, diff --git a/cmake/PackageProject.cmake b/cmake/PackageProject.cmake index 6179d9005..24321c536 100644 --- a/cmake/PackageProject.cmake +++ b/cmake/PackageProject.cmake @@ -8,8 +8,8 @@ function(fiction_package_project) cmake_policy(SET CMP0103 NEW) # disallow multiple calls with the same NAME endif() - # if CMake version >= 3.23 - if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.23") + # if CMake version >= 3.24 + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") cmake_policy(SET CMP0135 NEW) # enable new timestamp checking behavior for # fetching content endif() diff --git a/docs/algorithms/sidb_simulation.rst b/docs/algorithms/sidb_simulation.rst index 663995861..657905da1 100644 --- a/docs/algorithms/sidb_simulation.rst +++ b/docs/algorithms/sidb_simulation.rst @@ -71,6 +71,7 @@ Energy Calculation **Header:** ``fiction/algorithms/simulation/sidb/minimum_energy.hpp`` .. doxygenfunction:: fiction::minimum_energy +.. doxygenfunction:: fiction::minimum_energy_distribution **Header:** ``fiction/algorithms/simulation/sidb/is_ground_state.hpp`` @@ -90,7 +91,7 @@ Temperature Behavior .. doxygenfunction:: fiction::critical_temperature_gate_based .. doxygenfunction:: fiction::critical_temperature_non_gate_based -**Header:** ``fiction/algorithms/simulation/sidb/occupation_probability_excited_states.hpp`` +**Header:** ``fiction/algorithms/simulation/sidb/occupation_probability_of_excited_states.hpp`` .. doxygenfunction:: fiction::occupation_probability_gate_based .. doxygenfunction:: fiction::occupation_probability_non_gate_based @@ -129,6 +130,7 @@ Random SiDB Layout Generator **Header:** ``fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp`` .. doxygenstruct:: fiction::generate_random_sidb_layout_params + :members: .. doxygenfunction:: fiction::generate_random_sidb_layout .. doxygenfunction:: fiction::generate_multiple_random_sidb_layouts diff --git a/docs/cli.rst b/docs/cli.rst index f50241ecd..44693edab 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -215,6 +215,7 @@ throughput (TP) and thereby, the amount of clock cycles the PIs need to be stall A ``network`` can also be simulated for comparison by using ``simulate -n``. + Equivalence checking (``equiv``) -------------------------------- @@ -263,6 +264,109 @@ simulators are currently supported: If no filename is given, the stored layout name will be used and the file will be written to the current folder. +Physical Simulation of SiDBs +---------------------------- + +Performing physical simulation of SiDB layouts is crucial for understanding layout behavior and +facilitating rapid prototyping, eliminating the need for expensive and time-intensive fabrication processes. +The command ``read --sqd`` (or ``read -s``) is used to import a SiDB layout from an sqd-file, a format compatible with `SiQAD `_. +The SiDB layout can be visualized using the ``print -c`` command. Currently, *fiction* provides two electrostatic physical simulators: +the exact one *QuickExact* and the scalable one *QuickSim*. + +QuickExact (``quickexact``) +########################### + +*QuickExact* serves as an exact simulator, meticulously determining all physically valid charge distributions. +It enumerates all possible charge distributions. However, by incorporating three techniques, namely +1.) Physically-informed Search Space Pruning, 2.) Partial Solution Caching, and 3.) Effective State Enumeration, it provides +a significant performance advantage of more than three orders of magnitude over ExGS from SiQAD. For additional details, +see `the paper `_. + +Most important parameters: + +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening length :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) + +See ``quickexact -h`` for a full list. + +The simulated ground state charge distribution can be printed with ``print -c``. + + +QuickSim (``quicksim``) +####################### + +*QuickSim* serves as a scalable simulator designed to determine the ground state charge distribution +for a given SiDB layout. To enhance efficiency, effective search space pruning techniques, such as +(`max-min diversity distributions `_), are integrated. +For more in-depth information, refer to `the paper `_. + +Most important parameters: + +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) +- Number of iterations (``-i``) +- :math:`\alpha` value (``-a``) + +The simulated ground state charge distribution can be printed with ``print -c``. + +Critical Temperature (``temp``) +############################### + +The critical temperature of an SiDB layout is the temperature at which the layout's ground state is populated with a +probability larger than a certain threshold. This threshold is specified as a confidence level :math:`1 - \eta`, where +:math:`\eta \in [0,1]`. The simulation can be conducted for gate-based SiDB layouts as well, where the gate is +simulated with respect to the stability of a given Boolean function in form of the current truth table in store. +For more in-depth information, refer to `the paper `_. + +Most important parameters: + +- Confidence level :math:`1 - \eta` (``-c``) +- Maximum temperature in K to explore (``-t``) +- Gate-based simulation toggle (``-g``) +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) + + +Operational Domain (``opdom``) +############################## + +Computes the operational domain of the current SiDB cell-level layout in store. The operational domain is the set of all +parameter combinations for which the layout is logically operational. Logical operation is defined as the layout +implementing the current truth table in store. The input BDL pairs of the layout are assumed to be in the same order as +the inputs of the truth table. +For more information, see `the paper `_. + +The command ``opdom`` writes the operational domain to a CSV file with the given filename from where it can be further +processed by other tools. + +The parameter space to sweep over can be specified by the user via the flags +- ``--x_sweep`` +- ``--y_sweep`` +which have to be either ``epsilon_r``, ``lambda_tf``, or ``mu_minus``. The default is ``epsilon_r`` for ``--x_sweep`` and +``lambda_tf`` for ``--y_sweep``. + +Additionally, min, max, and step size values can be specified for each parameter using the flags +- ``--x_min`` +- ``--x_max`` +- ``--x_step`` +- ``--y_min`` +- ``--y_max`` +- ``--y_step`` +respectively. The default values are 1, 10, and 0.1 on both axis, for min, max, and step, respectively. + +By default, grid search is applied to explore the operational domain. The algorithm can be changed by specifying one of +the following options: +- ``--random_sampling``/``-r`` +- ``--flood_fill``/``-f`` +- ``--contour_tracing``/``-c`` +each of which start from a set of random samples, whose number has to be passed as an argument to the flag. + +See ``opdom -h`` for a full list of arguments. + + Area usage (``area``) --------------------- @@ -365,7 +469,7 @@ Additionally, *fiction* itself can be part of a bash script. Consider the follow for filepath in ../benchmarks/TOY/*.v; do f="${filepath##*/}" - ./fiction -c "read $filepath; ortho; cell; qca ${f%.*}.qca" + ./fiction -c "read $filepath; ortho; cell; qca ${f%.*}.qca" done where the for-loop iterates over all Verilog files in the ``../benchmarks/TOY/`` folder. By using the flag ``-c``, a diff --git a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp index d54934b27..ef2b4c65b 100644 --- a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp +++ b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp @@ -7,23 +7,18 @@ #include "fiction/algorithms/iter/bdl_input_iterator.hpp" #include "fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp" +#include "fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp" #include "fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp" #include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/exhaustive_ground_state_simulation.hpp" -#include "fiction/algorithms/simulation/sidb/is_operational.hpp" #include "fiction/algorithms/simulation/sidb/occupation_probability_of_excited_states.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/algorithms/simulation/sidb/quicksim.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/cell_technologies.hpp" -#include "fiction/technology/charge_distribution_surface.hpp" #include "fiction/technology/physical_constants.hpp" -#include "fiction/technology/sidb_charge_state.hpp" #include "fiction/traits.hpp" -#include "fiction/types.hpp" -#include "fiction/utils/hash.hpp" #include "fiction/utils/math_utils.hpp" -#include "fiction/utils/truth_table_utils.hpp" #include #include @@ -33,10 +28,10 @@ #include #include #include +#include #include #include -#include -#include +#include #include #include @@ -48,29 +43,10 @@ namespace fiction */ struct critical_temperature_params { - /** - * An enumeration of modes to use for the *Critical Temperature* Simulation. - */ - enum class critical_temperature_mode - { - /** - * The *Critical Temperature* is determined by considering the gate logic of the given layout. In this mode, it - * is distinguished between excited charge distributions that produce the correct output (with respect to a - * truth table) and those that do not. - */ - GATE_BASED_SIMULATION, - /** - * The *Critical Temperature* is determined by ignoring the gate logic of the given layout. This mode does not - * distinguish between excited charge distributions that produce the correct output (with respect to a truth - * table) and those that do not. - */ - NON_GATE_BASED_SIMULATION - }; - /** * An enumeration of simulation modes (exact vs. approximate) to use for the *Critical Temperature* Simulation. */ - enum class simulation_engine + enum class simulation_engine : uint8_t { /** * This simulation engine computes *Critical Temperature* values with 100 % accuracy. @@ -83,26 +59,35 @@ struct critical_temperature_params APPROXIMATE }; /** - * All Parameters for physical SiDB simulations. + * All parameters for physical SiDB simulations. */ - quicksim_params simulation_params{}; + sidb_simulation_parameters physical_parameters{}; /** * Simulation mode to determine the *Critical Temperature*. */ simulation_engine engine = simulation_engine::EXACT; /** - * Probability that the ground state is less populated due to temperature. For gate-based simulation, this is the - * probability of erroneous calculations of the gate. + * Probability threshold for ground state population. The temperature at which the simulation finds the ground state + * to be populated with a probability of less than the given percentage, is determined to be the critical + * temperature. For gate-based simulation, this is the probability of erroneous calculations of the gate. */ double confidence_level{0.99}; /** - * Simulation stops at max_temperature (~ 126 °C by default) (unit: K). + * Maximum simulation temperature beyond which no simulation will be conducted (~ 126 °C by default) (unit: K). */ double max_temperature{400}; /** * Parameters for the BDL pair detection algorithms. */ detect_bdl_pairs_params bdl_params{}; + /** + * Number of iteration steps for the *QuickSim* algorithm (only applicable if engine == APPROXIMATE). + */ + uint64_t iteration_steps{80}; + /** + * Alpha parameter for the *QuickSim* algorithm (only applicable if engine == APPROXIMATE). + */ + double alpha{0.7}; }; /** @@ -113,6 +98,10 @@ struct critical_temperature_params template struct critical_temperature_stats { + /** + * All parameters for physical SiDB simulations. + */ + sidb_simulation_parameters physical_parameters{}; /** * Name of the algorithm used to compute the physically valid charge distributions. */ @@ -126,7 +115,7 @@ struct critical_temperature_stats */ uint64_t num_valid_lyt{}; /** - * Energy difference between the ground state and the first (erroneous) excited state (unit: eV). + * Energy difference between the ground state and the first (erroneous) excited state (unit: meV). */ double energy_between_ground_state_and_first_erroneous = std::numeric_limits::infinity(); /** @@ -141,7 +130,7 @@ struct critical_temperature_stats if (num_valid_lyt != 0) { out << fmt::format("'# of physically valid charge configurations': {} | Energy between ground state and " - "first erroneous: {}\n", + "first erroneous in meV: {}\n", num_valid_lyt, energy_between_ground_state_and_first_erroneous); } else @@ -168,9 +157,10 @@ class critical_temperature_impl bii(bdl_input_iterator{layout, params.bdl_params}) { - stats.critical_temperature = params.max_temperature; + stats.physical_parameters = params.physical_parameters; stats.algorithm_name = (params.engine == critical_temperature_params::simulation_engine::EXACT) ? "QuickExact" : "QuickSim"; + stats.critical_temperature = params.max_temperature; } /** @@ -187,7 +177,8 @@ class critical_temperature_impl stats.critical_temperature = 0.0; return; } - else if (layout.num_cells() > 1) + + if (layout.num_cells() > 1) { const auto output_bdl_pairs = detect_bdl_pairs(layout, sidb_technology::cell_type::OUTPUT, params.bdl_params); @@ -196,7 +187,7 @@ class critical_temperature_impl for (auto i = 0u; i < spec.front().num_bits(); ++i, ++bii) { // if positively charged SiDBs can occur, the SiDB layout is considered as non-operational - if (can_positive_charges_occur(*bii, params.simulation_params.phys_params)) + if (can_positive_charges_occur(*bii, params.physical_parameters)) { stats.critical_temperature = 0.0; return; @@ -244,21 +235,23 @@ class critical_temperature_impl void non_gate_based_simulation() noexcept { sidb_simulation_result simulation_results{}; + if (params.engine == critical_temperature_params::simulation_engine::EXACT) { - stats.algorithm_name = "QuickExact"; + const quickexact_params qe_params{params.physical_parameters, + quickexact_params::automatic_base_number_detection::OFF}; + // All physically valid charge configurations are determined for the given layout (`QuickExact` simulation // is used to provide 100 % accuracy for the Critical Temperature). - const quickexact_params qe_params{params.simulation_params.phys_params, - quickexact_params::automatic_base_number_detection::OFF}; simulation_results = quickexact(layout, qe_params); } else { - stats.algorithm_name = "QuickSim"; - // All physically valid charge configurations are determined for the given layout (exhaustive ground state - // simulation is used to provide 100 % accuracy for the Critical Temperature). - simulation_results = quicksim(layout, params.simulation_params); + const quicksim_params qs_params{params.physical_parameters, params.iteration_steps, params.alpha}; + + // All physically valid charge configurations are determined for the given layout (probabilistic ground + // state simulation is used). + simulation_results = quicksim(layout, qs_params); } // The number of physically valid charge configurations is stored. @@ -417,19 +410,20 @@ class critical_temperature_impl [[nodiscard]] sidb_simulation_result physical_simulation_of_layout(const bdl_input_iterator& bdl_iterator) noexcept { - assert(params.simulation_params.phys_params.base == 2 && "base number is set to 3"); + assert(params.physical_parameters.base == 2 && "base number has to be 2"); + if (params.engine == critical_temperature_params::simulation_engine::EXACT) { // perform exact simulation - const quickexact_params quickexact_params{ - params.simulation_params.phys_params, - fiction::quickexact_params::automatic_base_number_detection::OFF}; - return quickexact(*bdl_iterator, quickexact_params); + const quickexact_params qe_params{ + params.physical_parameters, fiction::quickexact_params::automatic_base_number_detection::OFF}; + return quickexact(*bdl_iterator, qe_params); } if (params.engine == critical_temperature_params::simulation_engine::APPROXIMATE) { - return quicksim(*bdl_iterator, params.simulation_params); + const quicksim_params qs_params{params.physical_parameters, params.iteration_steps, params.alpha}; + return quicksim(*bdl_iterator, qs_params); } assert(false && "unsupported simulation engine"); @@ -467,8 +461,9 @@ double critical_temperature_gate_based(const Lyt& lyt, const std::vector& sp assert(!spec.empty()); // all elements in tts must have the same number of variables - assert(std::adjacent_find(spec.begin(), spec.end(), - [](const auto& a, const auto& b) { return a.num_vars() != b.num_vars(); }) == spec.end()); + assert(std::adjacent_find(spec.cbegin(), spec.cend(), + [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); critical_temperature_stats st{}; @@ -501,7 +496,6 @@ double critical_temperature_non_gate_based(const Lyt& lyt, const critical_temper { static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); static_assert(has_sidb_technology_v, "Lyt is not an SiDB layout"); - static_assert(has_siqad_coord_v, "Lyt is not based on SiQAD coordinates"); critical_temperature_stats st{}; diff --git a/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp b/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp index 085ef971e..4c62f11e8 100644 --- a/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp +++ b/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp @@ -38,8 +38,10 @@ template return false; } - const auto min_energy_exact = minimum_energy(exhaustive_results.charge_distributions); - const auto min_energy_new_ap = minimum_energy(quicksim_results.charge_distributions); + const auto min_energy_exact = minimum_energy(exhaustive_results.charge_distributions.cbegin(), + exhaustive_results.charge_distributions.cend()); + const auto min_energy_new_ap = minimum_energy(exhaustive_results.charge_distributions.cbegin(), + exhaustive_results.charge_distributions.cend()); return round_to_n_decimal_places(std::abs(min_energy_exact - min_energy_new_ap), 6) == 0; } diff --git a/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp b/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp index d15001d90..ab258dc39 100644 --- a/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp +++ b/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp @@ -6,6 +6,7 @@ #define FICTION_MAXIMUM_DEFECT_INFLUENCE_POSITION_AND_DISTANCE_HPP #include "fiction/algorithms/simulation/sidb/critical_temperature.hpp" +#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/layouts/bounding_box.hpp" #include "fiction/technology/sidb_defects.hpp" @@ -81,8 +82,10 @@ class maximum_defect_influence_position_and_distance_impl quickexact(layout, quickexact_params{params.physical_params, quickexact_params::automatic_base_number_detection::OFF}); - const auto min_energy = minimum_energy(simulation_results.charge_distributions); - uint64_t charge_index_layout = 0; + const auto min_energy = minimum_energy(simulation_results.charge_distributions.cbegin(), + simulation_results.charge_distributions.cend()); + + uint64_t charge_index_layout = 0; for (auto& lyt_result : simulation_results.charge_distributions) { @@ -109,8 +112,10 @@ class maximum_defect_influence_position_and_distance_impl // conduct simulation with defect auto simulation_result_defect = quickexact(lyt_defect, params_defect); - const auto min_energy_defect = minimum_energy(simulation_result_defect.charge_distributions); - uint64_t charge_index_defect_layout = 0; + const auto min_energy_defect = minimum_energy(simulation_result_defect.charge_distributions.cbegin(), + simulation_result_defect.charge_distributions.cend()); + + uint64_t charge_index_defect_layout = 0; // get the charge index of the ground state for (const auto& lyt_simulation_with_defect : simulation_result_defect.charge_distributions) diff --git a/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp b/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp index e57d797bb..9a37c1147 100644 --- a/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp +++ b/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp @@ -5,30 +5,59 @@ #ifndef FICTION_MINIMUM_ENERGY_HPP #define FICTION_MINIMUM_ENERGY_HPP -#include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/traits.hpp" #include +#include #include -#include namespace fiction { /** - * Computes the minimum energy of a vector of charge_distribution_surface objects. + * Computes the minimum energy of a range of `charge_distribution_surface` objects. If the range is empty, infinity is + * returned. * - * @tparam Lyt Cell-level layout type. - * @param charge_lyts Vector of charge_distribution_surface objects. - * @return Value of the minimum energy found in the input vector (unit: eV). + * @tparam InputIt Must meet the requirements of `LegacyInputIterator`. + * @param first Begin of the range to examime. + * @param last End of the range to examine. + * @return Value of the minimum energy found in the input range (unit: eV), or infinity if the range is empty. */ -template -[[nodiscard]] double minimum_energy(const std::vector>& charge_lyts) noexcept +template +[[nodiscard]] double minimum_energy(const InputIt first, const InputIt last) noexcept { - static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); - static_assert(has_sidb_technology_v, "Lyt is not an SiDB layout"); + static_assert(std::is_base_of_v::iterator_category>, + "InputIt must meet the requirements of LegacyInputIterator"); + static_assert(is_charge_distribution_surface_v::value_type>, + "Range must be of charge_distribution_surface objects"); - return std::accumulate(charge_lyts.cbegin(), charge_lyts.cend(), std::numeric_limits::max(), - [](const double a, const auto& lyt) { return std::min(a, lyt.get_system_energy()); }); + if (first != last) + { + return minimum_energy_distribution(first, last)->get_system_energy(); + } + + return std::numeric_limits::infinity(); +} +/** + * Returns an iterator to the charge distribution of minimum energy contained in a range of + * `charge_distribution_surface` objects. If the range is empty, `last` is returned. + * + * @tparam InputIt Must meet the requirements of `LegacyInputIterator`. + * @param first Begin of the range to examime. + * @param last End of the range to examine. + * @return Iterator to the minimum energy charge distribution found in the input range, or `last` if the range is empty. + */ +template +[[nodiscard]] InputIt minimum_energy_distribution(const InputIt first, const InputIt last) noexcept +{ + static_assert(std::is_base_of_v::iterator_category>, + "InputIt must meet the requirements of LegacyInputIterator"); + static_assert(is_charge_distribution_surface_v::value_type>, + "Range must be of charge_distribution_surface objects"); + + return std::min_element(first, last, + [](const auto& cds1, const auto& cds2) + { return cds1.get_system_energy() < cds2.get_system_energy(); }); } } // namespace fiction diff --git a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp index ee6726dcd..b38d43c79 100644 --- a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp +++ b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp @@ -16,12 +16,12 @@ #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/layouts/cell_level_layout.hpp" +#include "fiction/technology/cell_technologies.hpp" #include "fiction/traits.hpp" #include "fiction/utils/execution_utils.hpp" #include "fiction/utils/hash.hpp" #include "fiction/utils/phmap_utils.hpp" -#include #include #include #include @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/fiction/algorithms/simulation/sidb/quickexact.hpp b/include/fiction/algorithms/simulation/sidb/quickexact.hpp index 138a0e326..a1cb738f8 100644 --- a/include/fiction/algorithms/simulation/sidb/quickexact.hpp +++ b/include/fiction/algorithms/simulation/sidb/quickexact.hpp @@ -6,15 +6,14 @@ #define FICTION_QUICKEXACT_HPP #include "fiction/algorithms/iter/gray_code_iterator.hpp" -#include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/technology/sidb_charge_state.hpp" +#include "fiction/technology/sidb_defects.hpp" #include "fiction/traits.hpp" -#include #include #include @@ -98,6 +97,7 @@ class quickexact_impl { result.algorithm_name = "QuickExact"; result.physical_parameters = params.physical_parameters; + result.additional_simulation_parameters.emplace("global_potential", params.global_potential); mockturtle::stopwatch<>::duration time_counter{}; { @@ -304,13 +304,13 @@ class quickexact_impl if (base_number == required_simulation_base_number::TWO) { - result.additional_simulation_parameters.emplace_back("base_number", uint64_t{2}); + result.additional_simulation_parameters.emplace("base_number", uint64_t{2}); two_state_simulation(charge_layout); } // If positively charged SiDBs can occur in the layout, 3-state simulation is conducted. else { - result.additional_simulation_parameters.emplace_back("base_number", uint64_t{3}); + result.additional_simulation_parameters.emplace("base_number", uint64_t{3}); three_state_simulation(charge_layout); } } diff --git a/include/fiction/algorithms/simulation/sidb/quicksim.hpp b/include/fiction/algorithms/simulation/sidb/quicksim.hpp index 771444da7..41b886ec2 100644 --- a/include/fiction/algorithms/simulation/sidb/quicksim.hpp +++ b/include/fiction/algorithms/simulation/sidb/quicksim.hpp @@ -5,20 +5,17 @@ #ifndef FICTION_QUICKSIM_HPP #define FICTION_QUICKSIM_HPP -#include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/technology/sidb_charge_state.hpp" #include "fiction/traits.hpp" -#include #include #include #include #include -#include -#include #include #include #include @@ -57,7 +54,7 @@ struct quicksim_params * charge distribution layout. Depending on the simulation parameters, the ground state is found with a certain * probability after one run. * - * @tparam Lyt Cell-level layout type. + * @tparam Lyt SiDB cell-level layout type. * @param lyt The layout to simulate. * @param ps Physical parameters. They are material-specific and may vary from experiment to experiment. * @return sidb_simulation_result is returned with all results. @@ -75,8 +72,8 @@ sidb_simulation_result quicksim(const Lyt& lyt, const quicksim_params& ps = sidb_simulation_result st{}; st.algorithm_name = "QuickSim"; - st.additional_simulation_parameters.emplace_back("iteration_steps", ps.interation_steps); - st.additional_simulation_parameters.emplace_back("alpha", ps.alpha); + st.additional_simulation_parameters.emplace("iteration_steps", ps.interation_steps); + st.additional_simulation_parameters.emplace("alpha", ps.alpha); st.physical_parameters = ps.phys_params; st.charge_distributions.reserve(ps.interation_steps); diff --git a/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp b/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp index c90ed5c7f..792c73b7b 100644 --- a/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp +++ b/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include namespace fiction @@ -57,9 +57,9 @@ struct sidb_simulation_result * Additional named simulation parameters. This is used to store algorithm-dependent parameters that are not part of * the `sidb_simulation_parameters` struct. * - * The first element of the pair is the name of the parameter, the second element is the value of the parameter. + * The key of the map is the name of the parameter, the element is the value of the parameter. */ - std::vector> additional_simulation_parameters{}; + std::unordered_map additional_simulation_parameters{}; }; } // namespace fiction diff --git a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp index 3cefa64d4..ac87768ad 100644 --- a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp +++ b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp @@ -7,7 +7,6 @@ #include "fiction/algorithms/simulation/sidb/exhaustive_ground_state_simulation.hpp" #include "fiction/algorithms/simulation/sidb/is_ground_state.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/algorithms/simulation/sidb/quicksim.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" diff --git a/include/fiction/io/print_layout.hpp b/include/fiction/io/print_layout.hpp index 7e9dfffb9..517323ac1 100644 --- a/include/fiction/io/print_layout.hpp +++ b/include/fiction/io/print_layout.hpp @@ -533,7 +533,14 @@ void print_layout(const Lyt& lyt, std::ostream& os = std::cout) { if constexpr (has_sidb_technology_v) { - print_sidb_layout(os, lyt); + if constexpr (has_siqad_coord_v) + { + print_sidb_layout(os, lyt); + } + else + { + print_sidb_layout(os, convert_to_siqad_coordinates(lyt)); + } } else { diff --git a/include/fiction/io/write_location_and_ground_state.hpp b/include/fiction/io/write_location_and_ground_state.hpp index ed6a1a2f0..6af35875a 100644 --- a/include/fiction/io/write_location_and_ground_state.hpp +++ b/include/fiction/io/write_location_and_ground_state.hpp @@ -39,8 +39,8 @@ class write_location_and_ground_state_impl void run() { // this part searches for the ground state(s) among all physically valid charge distributions - const auto min_energy = - round_to_n_decimal_places(minimum_energy(sim_result.charge_distributions), 6); + const auto min_energy = round_to_n_decimal_places( + minimum_energy(sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()), 6); std::vector> ground_state_layouts{}; for (const auto& valid_layout : sim_result.charge_distributions) diff --git a/include/fiction/io/write_operational_domain.hpp b/include/fiction/io/write_operational_domain.hpp index 2c9244204..8cd2f07ae 100644 --- a/include/fiction/io/write_operational_domain.hpp +++ b/include/fiction/io/write_operational_domain.hpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace fiction @@ -40,7 +41,8 @@ namespace detail * @param param The sweep parameter to be converted. * @return The string representation of the sweep parameter. */ -static inline std::string sweep_parameter_to_string(const operational_domain::sweep_parameter& param) noexcept +[[nodiscard]] static inline std::string +sweep_parameter_to_string(const operational_domain::sweep_parameter& param) noexcept { switch (param) { diff --git a/include/fiction/layouts/cell_level_layout.hpp b/include/fiction/layouts/cell_level_layout.hpp index aea701153..0fb1b99a5 100644 --- a/include/fiction/layouts/cell_level_layout.hpp +++ b/include/fiction/layouts/cell_level_layout.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -418,7 +419,7 @@ class cell_level_layout : public ClockedLayout using iterator_type = decltype(strg->cell_type_map.cbegin()); mockturtle::detail::foreach_element_transform( strg->cell_type_map.cbegin(), strg->cell_type_map.cend(), - [](const auto& ct) { return static_cast(ct.first); }, fn); + [](const auto& ct) { return static_cast(ct.first); }, std::forward(fn)); } /** * Applies a function to all cell positions in the layout, even empty ones. This function, thereby, renames @@ -445,7 +446,8 @@ class cell_level_layout : public ClockedLayout { using iterator_type = decltype(strg->inputs.cbegin()); mockturtle::detail::foreach_element_transform( - strg->inputs.cbegin(), strg->inputs.cend(), [](const auto& i) { return static_cast(i); }, fn); + strg->inputs.cbegin(), strg->inputs.cend(), [](const auto& i) { return static_cast(i); }, + std::forward(fn)); } /** * Applies a function to all primary output cells in the layout. @@ -459,7 +461,8 @@ class cell_level_layout : public ClockedLayout { using iterator_type = decltype(strg->outputs.cbegin()); mockturtle::detail::foreach_element_transform( - strg->outputs.cbegin(), strg->outputs.end(), [](const auto& o) { return static_cast(o); }, fn); + strg->outputs.cbegin(), strg->outputs.end(), [](const auto& o) { return static_cast(o); }, + std::forward(fn)); } #pragma endregion diff --git a/include/fiction/technology/charge_distribution_surface.hpp b/include/fiction/technology/charge_distribution_surface.hpp index 2035f0088..eaf86c938 100644 --- a/include/fiction/technology/charge_distribution_surface.hpp +++ b/include/fiction/technology/charge_distribution_surface.hpp @@ -14,7 +14,6 @@ #include "fiction/technology/sidb_defects.hpp" #include "fiction/technology/sidb_nm_position.hpp" #include "fiction/traits.hpp" -#include "fiction/types.hpp" #include #include @@ -24,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +129,7 @@ class charge_distribution_surface : public Lyt */ using distance_matrix = std::vector>; /** - * The potential matrix is a vector of vectors storing the chargless electrostatic potentials in Volt (V). + * The potential matrix is a vector of vectors storing the charge-less electrostatic potentials in Volt (V). */ using potential_matrix = std::vector>; /** @@ -287,11 +287,11 @@ class charge_distribution_surface : public Lyt /** * Copy constructor. * - * @param lyt charge_distribution_surface + * @param cds Other `charge_distribution_surface`. */ - explicit charge_distribution_surface(const charge_distribution_surface& lyt) : - Lyt(lyt), - strg{std::make_shared(*lyt.strg)} + charge_distribution_surface(const charge_distribution_surface& cds) : + Lyt(cds), + strg{std::make_shared(*cds.strg)} {} /** * Copy assignment operator. diff --git a/include/fiction/types.hpp b/include/fiction/types.hpp index c1e5415fe..5f5cd7966 100644 --- a/include/fiction/types.hpp +++ b/include/fiction/types.hpp @@ -15,6 +15,7 @@ #include "fiction/layouts/tile_based_layout.hpp" #include "fiction/networks/technology_network.hpp" #include "fiction/technology/cell_technologies.hpp" +#include "fiction/technology/charge_distribution_surface.hpp" #include "fiction/technology/sidb_surface.hpp" #include @@ -134,6 +135,16 @@ inline constexpr const char* tech_impl_name = std::is_same_v, std::is_same_v, sidb_technology> ? sidb_name : "?"; +constexpr const char* qca_cell_name = "cells"; +constexpr const char* inml_cell_name = "magnets"; +constexpr const char* sidb_cell_name = "dots"; + +template +inline constexpr const char* tech_cell_name = std::is_same_v, qca_technology> ? qca_cell_name : + std::is_same_v, inml_technology> ? inml_cell_name : + std::is_same_v, sidb_technology> ? sidb_cell_name : + "?"; + /** * FCN cell-level layouts. */ @@ -152,14 +163,18 @@ using inml_cell_clk_lyt_ptr = std::shared_ptr; using sidb_cell_clk_lyt = cell_level_layout>>; using sidb_cell_clk_lyt_ptr = std::shared_ptr; +using cds_sidb_cell_clk_lyt = + charge_distribution_surface>>>; +using cds_sidb_cell_clk_lyt_ptr = std::shared_ptr; + using sidb_cell_clk_lyt_siqad = cell_level_layout>>; using sidb_cell_clk_lyt_siqad_ptr = std::shared_ptr; using sidb_defect_cell_clk_lyt_siqad = sidb_surface; using sidb_defect_cell_clk_lyt_siqad_ptr = std::shared_ptr; -using cell_layout_t = - std::variant; +using cell_layout_t = std::variant; } // namespace fiction diff --git a/include/fiction/utils/layout_utils.hpp b/include/fiction/utils/layout_utils.hpp index 5c371908d..4a631528b 100644 --- a/include/fiction/utils/layout_utils.hpp +++ b/include/fiction/utils/layout_utils.hpp @@ -287,11 +287,12 @@ Lyt normalize_layout_coordinates(const Lyt& lyt) noexcept * coordinates is returned. * * @tparam Lyt Cell-level layout type based on fiction coordinates, e.g., `offset::ucoord_t` or `cube::coord_t`. + * @tparam TargetLyt Cell-level layout type based on SiQAD coordinates, i.e., `siqad::coord_t`. * @param lyt The layout that is to be converted to a new layout based on SiQAD coordinates. * @return A new equivalent layout based on SiQAD coordinates. */ template -sidb_cell_clk_lyt_siqad convert_to_siqad_coordinates(const Lyt& lyt) noexcept +auto convert_to_siqad_coordinates(const Lyt& lyt) noexcept { static_assert(is_cartesian_layout_v, "Lyt is not a Cartesian layout"); static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); @@ -310,7 +311,22 @@ sidb_cell_clk_lyt_siqad convert_to_siqad_coordinates(const Lyt& lyt) noexcept lyt_new.assign_cell_name(siqad::to_siqad_coord>(c), lyt.get_cell_name(c)); }); - return lyt_new; + if constexpr (is_charge_distribution_surface_v) + { + charge_distribution_surface lyt_new_cds{lyt_new}; + + lyt.foreach_cell( + [&lyt_new_cds, &lyt](const auto& c) + { lyt_new_cds.assign_charge_state(siqad::to_siqad_coord>(c), lyt.get_charge_state(c), false); }); + + lyt_new_cds.assign_physical_parameters(lyt.get_phys_params()); + + return lyt_new_cds; + } + else + { + return lyt_new; + } } /** diff --git a/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp b/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp index 1fd7822d1..9fe28d9aa 100644 --- a/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp +++ b/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include using namespace fiction; diff --git a/test/algorithms/simulation/sidb/critical_temperature.cpp b/test/algorithms/simulation/sidb/critical_temperature.cpp index bfa509d31..6ef56d35f 100644 --- a/test/algorithms/simulation/sidb/critical_temperature.cpp +++ b/test/algorithms/simulation/sidb/critical_temperature.cpp @@ -6,17 +6,19 @@ #include #include -#include -#include +#include #include #include #include -#include +#include #include +#include #include +#include #include #include +#include using namespace fiction; @@ -25,40 +27,50 @@ TEMPLATE_TEST_CASE( (cell_level_layout>>), (charge_distribution_surface>>>)) { + TestType lyt{}; + + critical_temperature_params params{}; + sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + + critical_temperature_stats critical_stats{}; + SECTION("No physically valid charge distribution could be found") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({6, 1, 0}, sidb_technology::cell_type::OUTPUT); lyt.assign_cell_type({8, 1, 0}, sidb_technology::cell_type::OUTPUT); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 0, 0.0}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); - CHECK(criticalstats.num_valid_lyt == 0); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 0; + params.alpha = 0.0; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 0); + CHECK(critical_stats.critical_temperature == 0.0); } - SECTION("One SiDB") + SECTION("No SiDB") { - TestType lyt{}; - - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{tt{}}, params, &criticalstats); - CHECK(criticalstats.num_valid_lyt == 0); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{tt{}}, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 0); + CHECK(critical_stats.critical_temperature == 0.0); } - SECTION("Not working diagonal Wire where positively charged SiDBs can occur") + SECTION("Not working diagonal wire where positively charged SiDBs can occur") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -80,37 +92,43 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); + + CHECK(critical_stats.critical_temperature == 0.0); } SECTION("four SiDBs with two valid charge distributions, QuickExact") { - TestType lyt{}; lyt.assign_cell_type({0, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({4, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 2}, TestType::cell_type::NORMAL); - critical_temperature_stats criticalstats_non_gate_based{}; - const critical_temperature_params params_non_gate_based{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, - 0.99, 350}; - critical_temperature_non_gate_based(lyt, params_non_gate_based, &criticalstats_non_gate_based); - CHECK(criticalstats_non_gate_based.num_valid_lyt == 2); - CHECK_THAT(std::abs(criticalstats_non_gate_based.energy_between_ground_state_and_first_erroneous), + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_non_gate_based(lyt, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 2); + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.01)); - CHECK(criticalstats_non_gate_based.critical_temperature == 350); + CHECK(critical_stats.critical_temperature == 350); } SECTION("Y-shape SiDB AND gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 1}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 1}, sidb_technology::cell_type::INPUT); @@ -128,20 +146,24 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({10, 9, 1}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.28}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); + physical_params.mu_minus = -0.28; + + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.01)); - CHECK(criticalstats.critical_temperature == 350); + CHECK(critical_stats.critical_temperature == 350); } SECTION("Bestagon AND gate, QuickExact") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -171,19 +193,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(26.02, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 59.19), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 59.19), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("Bestagon AND gate, QuickSim") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -213,18 +238,20 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 500, 0.6}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); - CHECK(criticalstats.critical_temperature > 0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 500; + params.alpha = 0.6; + + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK(critical_stats.critical_temperature > 0); } SECTION("Bestagon FO2 gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -253,20 +280,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({2, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_fan_out_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous - 0.56), + critical_temperature_gate_based(lyt, std::vector{create_fan_out_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous - 0.56), Catch::Matchers::WithinAbs(0.00, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 1.46), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 1.46), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("Bestagon CX gate") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -307,20 +336,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({2, 19, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_crossing_wire_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::fabs(criticalstats.energy_between_ground_state_and_first_erroneous - 0.32), + critical_temperature_gate_based(lyt, std::vector{create_crossing_wire_tt()}, params, &critical_stats); + + CHECK_THAT(std::fabs(critical_stats.energy_between_ground_state_and_first_erroneous - 0.32), Catch::Matchers::WithinAbs(0.00, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 0.85), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 0.85), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("OR gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({26, 0, 0}, sidb_technology::cell_type::INPUT); @@ -343,18 +374,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({24, 15, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.25}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_or_tt()}, params, &criticalstats); + physical_params.mu_minus = -0.25; + + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_or_tt()}, params, &critical_stats); - CHECK(criticalstats.critical_temperature < 350); + CHECK(critical_stats.critical_temperature < 350); } SECTION("Not working diagonal Wire") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -374,22 +409,24 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); - CHECK(criticalstats.algorithm_name == "QuickExact"); + CHECK(critical_stats.algorithm_name == "QuickExact"); - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(305.95, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("nine SiDBs, QuickSim, non-gate-based") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({3, 0, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::NORMAL); @@ -401,14 +438,17 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({9, 1, 1}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({12, 1, 1}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 500, 0.6}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 750}; - critical_temperature_non_gate_based(lyt, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 750; + params.iteration_steps = 500; + params.alpha = 0.6; + + critical_temperature_non_gate_based(lyt, params, &critical_stats); - CHECK(criticalstats.algorithm_name == "QuickSim"); + CHECK(critical_stats.algorithm_name == "QuickSim"); - CHECK_THAT(std::abs(criticalstats.critical_temperature), Catch::Matchers::WithinAbs(11.55, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature), Catch::Matchers::WithinAbs(11.55, 0.01)); } } diff --git a/test/algorithms/simulation/sidb/minimum_energy.cpp b/test/algorithms/simulation/sidb/minimum_energy.cpp index 937055ecc..904c94f52 100644 --- a/test/algorithms/simulation/sidb/minimum_energy.cpp +++ b/test/algorithms/simulation/sidb/minimum_energy.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include @@ -32,11 +32,12 @@ TEMPLATE_TEST_CASE( const charge_distribution_surface charge_layout{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.begin(), all_lyts.end()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); all_lyts.push_back(charge_layout); - CHECK(std::abs(minimum_energy(all_lyts) - 0) < 0.00000001); + CHECK(std::abs(minimum_energy(all_lyts.begin(), all_lyts.end()) - 0) < 0.00000001); } SECTION("layout with one SiDB placed") @@ -46,11 +47,12 @@ TEMPLATE_TEST_CASE( const charge_distribution_surface charge_layout{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); all_lyts.push_back(charge_layout); - CHECK(std::abs(minimum_energy(all_lyts) - 0) < 0.00000001); + CHECK(std::abs(minimum_energy(all_lyts.cbegin(), all_lyts.cend()) - 0) < 0.00000001); } SECTION("layout with three SiDBs placed") @@ -62,7 +64,8 @@ TEMPLATE_TEST_CASE( charge_distribution_surface charge_layout_first{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); charge_layout_first.assign_charge_state({0, 0}, sidb_charge_state::NEUTRAL); @@ -79,6 +82,6 @@ TEMPLATE_TEST_CASE( charge_layout_second.recompute_system_energy(); all_lyts.push_back(charge_layout_second); - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(0.0, 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), Catch::Matchers::WithinAbs(0.0, 0.00001)); } } diff --git a/test/algorithms/simulation/sidb/quickexact.cpp b/test/algorithms/simulation/sidb/quickexact.cpp index 77517b5fa..9b5f3f9d7 100644 --- a/test/algorithms/simulation/sidb/quickexact.cpp +++ b/test/algorithms/simulation/sidb/quickexact.cpp @@ -1,5 +1,6 @@ // // Created by Jan Drewniok on 18.12.22. +// #include #include @@ -28,9 +29,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); CHECK(simulation_results.charge_distributions.empty()); - CHECK(simulation_results.additional_simulation_parameters.empty()); CHECK(simulation_results.algorithm_name == "QuickExact"); - CHECK(simulation_results.additional_simulation_parameters.empty()); } TEMPLATE_TEST_CASE( @@ -252,6 +251,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); REQUIRE(simulation_results.charge_distributions.size() == 1); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("global_potential")) == -0.26); CHECK(simulation_results.charge_distributions.front().get_charge_state_by_index(0) == sidb_charge_state::NEUTRAL); } @@ -1150,8 +1150,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); REQUIRE(!simulation_results.additional_simulation_parameters.empty()); - CHECK(simulation_results.additional_simulation_parameters[0].first == "base_number"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[0].second) == 3); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("base_number")) == 3); const quickexact_params params_new{sidb_simulation_parameters{2, -0.32}, quickexact_params::automatic_base_number_detection::OFF}; @@ -1159,8 +1158,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results_new = quickexact(lyt, params_new); REQUIRE(!simulation_results_new.additional_simulation_parameters.empty()); - CHECK(simulation_results_new.additional_simulation_parameters[0].first == "base_number"); - CHECK(std::any_cast(simulation_results_new.additional_simulation_parameters[0].second) == 2); + CHECK(std::any_cast(simulation_results_new.additional_simulation_parameters.at("base_number")) == 2); } TEMPLATE_TEST_CASE( diff --git a/test/algorithms/simulation/sidb/quicksim.cpp b/test/algorithms/simulation/sidb/quicksim.cpp index b8de465b7..6da36cd06 100644 --- a/test/algorithms/simulation/sidb/quicksim.cpp +++ b/test/algorithms/simulation/sidb/quicksim.cpp @@ -5,12 +5,19 @@ #include #include +#include #include +#include #include #include #include +#include #include +#include #include +#include + +#include using namespace fiction; @@ -46,10 +53,8 @@ TEMPLATE_TEST_CASE( CHECK(simulation_results.charge_distributions.empty()); REQUIRE(!simulation_results.additional_simulation_parameters.empty()); CHECK(simulation_results.algorithm_name == "QuickSim"); - CHECK(simulation_results.additional_simulation_parameters[0].first == "iteration_steps"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[0].second) == 80); - CHECK(simulation_results.additional_simulation_parameters[1].first == "alpha"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[1].second) == 0.7); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("iteration_steps")) == 80); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("alpha")) == 0.7); CHECK(simulation_results.charge_distributions.empty()); } @@ -373,8 +378,8 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({10, 8, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({16, 1, 0}, TestType::cell_type::NORMAL); - sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.28}; + const sidb_simulation_result quicksim_stats{}; + const sidb_simulation_parameters params{2, -0.28}; quicksim_params quicksim_params{params}; @@ -459,20 +464,19 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({6, 2, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({8, 2, 0}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.25}; + const sidb_simulation_parameters params{2, -0.25}; quicksim_params quicksim_params{params}; REQUIRE(quicksim_params.phys_params.mu_minus == -0.25); - const auto check_charge_configuration = [](const sidb_simulation_result& stats) noexcept + const auto check_charge_configuration = [](const sidb_simulation_result& result) noexcept { - REQUIRE(!stats.charge_distributions.empty()); + REQUIRE(!result.charge_distributions.empty()); - REQUIRE(!energy_distribution(stats.charge_distributions).empty()); + REQUIRE(!energy_distribution(result.charge_distributions).empty()); - const auto& charge_lyt_first = stats.charge_distributions.front(); + const auto& charge_lyt_first = result.charge_distributions.front(); CHECK((((charge_lyt_first.get_charge_state({6, 2, 0}) == sidb_charge_state::NEGATIVE) && (charge_lyt_first.get_charge_state({8, 2, 0}) == sidb_charge_state::NEUTRAL)) || @@ -559,8 +563,7 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({8, 10, 1}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.32}; + const sidb_simulation_parameters params{2, -0.32}; quicksim_params quicksim_params{params}; @@ -668,7 +671,7 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({30, 15, 0}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; + const sidb_simulation_result quicksim_stats{}; const sidb_simulation_parameters params{2, -0.32}; quicksim_params quicksim_params{params}; diff --git a/test/io/write_sqd_sim_result.cpp b/test/io/write_sqd_sim_result.cpp index f05a5b4cd..629a65b85 100644 --- a/test/io/write_sqd_sim_result.cpp +++ b/test/io/write_sqd_sim_result.cpp @@ -191,7 +191,7 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") CHECK(simulation_stream.str() == sim_result_str); } - SECTION("with additional parameters") + SECTION("with additional parameter (string)") { const std::string sim_result_str = fmt::format( "\n" @@ -209,9 +209,41 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") " {}\n" " {}\n" " value1\n" - " 2\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + FICTION_VERSION, FICTION_REPO, fmt::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(std::time(nullptr))), + sim_result.physical_parameters.lambda_tf, sim_result.physical_parameters.epsilon_r, + sim_result.physical_parameters.mu_minus); + + sim_result.additional_simulation_parameters.emplace("param1", "value1"); + + write_sqd_sim_result(sim_result, simulation_stream); + + CHECK(simulation_stream.str() == sim_result_str); + } + + SECTION("with additional parameters (double)") + { + const std::string sim_result_str = fmt::format( + "\n" + "\n" + " \n" + " TestSim\n" + " {}\n" + " {}\n" + " 0\n" + " {}\n" + " 42.0\n" + " \n" + " \n" + " {}\n" + " {}\n" + " {}\n" " 3.140000\n" - " c\n" " \n" " \n" " \n" @@ -222,10 +254,7 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") sim_result.physical_parameters.lambda_tf, sim_result.physical_parameters.epsilon_r, sim_result.physical_parameters.mu_minus); - sim_result.additional_simulation_parameters.emplace_back("param1", "value1"); - sim_result.additional_simulation_parameters.emplace_back("param2", 2); - sim_result.additional_simulation_parameters.emplace_back("param3", 3.14); - sim_result.additional_simulation_parameters.emplace_back("param4", 'c'); + sim_result.additional_simulation_parameters.emplace("param3", 3.14); write_sqd_sim_result(sim_result, simulation_stream);