diff --git a/.clang-format b/.clang-format index 5a4805e25..77513b04a 100644 --- a/.clang-format +++ b/.clang-format @@ -55,7 +55,7 @@ IncludeCategories: Priority: 2 - Regex: '$' Priority: 3 - - Regex: '<(alice|lorina|mockturtle|kitty|bill|nlohmann|fmt|z3|phmap|tinyxml2|units)(\/[a-zA-Z0-9_])*.+>$' + - Regex: '<(alice|lorina|mockturtle|kitty|bill|nlohmann|fmt|z3|phmap|btree|tinyxml2|units)(\/[a-zA-Z0-9_])*.+>$' Priority: 4 - Regex: '$' Priority: 5 diff --git a/docs/algorithms/iterators.rst b/docs/algorithms/iterators.rst index b836370fe..7536af9a3 100644 --- a/docs/algorithms/iterators.rst +++ b/docs/algorithms/iterators.rst @@ -14,3 +14,12 @@ Gray Code Iterator .. doxygenclass:: fiction::gray_code_iterator :members: + + +BDL Input Iterator +------------------ + +**Header:** ``fiction/algorithms/iter/bdl_input_iterator.hpp`` + +.. doxygenclass:: fiction::bdl_input_iterator + :members: diff --git a/docs/algorithms/sidb_simulation.rst b/docs/algorithms/sidb_simulation.rst index e9632ac0e..05c0af0ea 100644 --- a/docs/algorithms/sidb_simulation.rst +++ b/docs/algorithms/sidb_simulation.rst @@ -40,7 +40,6 @@ Exhaustive Ground State Simulation **Header:** ``fiction/algorithms/simulation/sidb/quickexact.hpp`` -.. doxygenenum:: fiction::automatic_base_number_detection .. doxygenstruct:: fiction::quickexact_params :members: .. doxygenfunction:: fiction::quickexact @@ -50,6 +49,15 @@ Exhaustive Ground State Simulation .. doxygenfunction:: fiction::exhaustive_ground_state_simulation +Engine Selectors +################ + +**Header:** ``fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp`` + +.. doxygenenum:: fiction::sidb_simulation_engine +.. doxygenenum:: fiction::exhaustive_sidb_simulation_engine + + Energy Calculation ################## @@ -76,8 +84,6 @@ Temperature Behavior **Header:** ``fiction/algorithms/simulation/sidb/critical_temperature.hpp`` -.. doxygenenum:: fiction::critical_temperature_mode -.. doxygenenum:: fiction::simulation_engine .. doxygenstruct:: fiction::critical_temperature_params :members: .. doxygenfunction:: fiction::critical_temperature @@ -108,7 +114,6 @@ Time-to-Solution (TTS) Statistics **Header:** ``fiction/algorithms/simulation/sidb/time_to_solution.hpp`` -.. doxygenenum:: fiction::exhaustive_algorithm .. doxygenstruct:: fiction::time_to_solution_params :members: .. doxygenfunction:: fiction::sim_acc_tts @@ -119,7 +124,41 @@ Random SiDB Layout Generator **Header:** ``fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp`` -.. doxygenenum:: fiction::positive_charges .. doxygenstruct:: fiction::generate_random_sidb_layout_params .. doxygenfunction:: fiction::generate_random_sidb_layout .. doxygenfunction:: fiction::generate_multiple_random_sidb_layouts + + +Operational Domain Computation +############################## + +**Header:** ``fiction/algorithms/simulation/sidb/operational_domain.hpp`` + +.. doxygenstruct:: fiction::operational_domain + :members: + +.. doxygenstruct:: fiction::operational_domain_params + :members: +.. doxygenstruct:: fiction::operational_domain_stats + :members: + +.. doxygenfunction:: fiction::operational_domain_grid_search +.. doxygenfunction:: fiction::operational_domain_random_sampling +.. doxygenfunction:: fiction::operational_domain_flood_fill +.. doxygenfunction:: fiction::operational_domain_contour_tracing + + +Utility Functions +################# + + +Binary-dot Logic (BDL) Pair Detection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Header:** ``fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp`` + +.. doxygenstruct:: fiction::bdl_pair + :members: +.. doxygenstruct:: fiction::detect_bdl_pairs_params + :members: +.. doxygenfunction:: fiction::detect_bdl_pairs diff --git a/docs/index.rst b/docs/index.rst index ac2ec9dd7..10b03793d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -62,6 +62,7 @@ Let there be a *fiction* io/input.rst io/visualization.rst io/physical_simulation.rst + io/utility.rst .. toctree:: :maxdepth: 2 diff --git a/docs/io/physical_simulation.rst b/docs/io/physical_simulation.rst index aa6633723..6f3af3f4d 100644 --- a/docs/io/physical_simulation.rst +++ b/docs/io/physical_simulation.rst @@ -77,3 +77,13 @@ SiQAD .. doxygenfunction:: fiction::read_sqd_layout(Lyt& lyt, const std::string_view& filename) .. doxygenclass:: fiction::sqd_parsing_error + +SiDB Operational Domain +####################### + +**Header:** ``fiction/io/write_operational_domain.hpp`` + +.. doxygenstruct:: fiction::write_operational_domain_params + :members: +.. doxygenfunction:: fiction::write_operational_domain(const operational_domain& opdom, std::ostream& os, const write_operational_domain_params& params = {}) +.. doxygenfunction:: fiction::write_operational_domain(const operational_domain& opdom, const std::string_view& filename, const write_operational_domain_params& params = {}) diff --git a/docs/io/utility.rst b/docs/io/utility.rst new file mode 100644 index 000000000..13f317fe4 --- /dev/null +++ b/docs/io/utility.rst @@ -0,0 +1,9 @@ +Utility +------- + +Utility code for I/O. + + +**Header:** ``fiction/io/csv_writer.hpp`` + +.. doxygenclass:: fiction::csv_writer diff --git a/docs/utils/utils.rst b/docs/utils/utils.rst index e99a02f12..163936878 100644 --- a/docs/utils/utils.rst +++ b/docs/utils/utils.rst @@ -180,3 +180,11 @@ Hashing This header defines implementations for ``std::hash`` for several data types. .. doxygenfunction:: fiction::hash_combine + + +`phmap` +------- + +**Header:** ``fiction/utils/phmap_utils.hpp`` + +.. doxygentypedef:: fiction::locked_parallel_flat_hash_map \ No newline at end of file diff --git a/experiments/operational_domain/operational_domain_siqad.cpp b/experiments/operational_domain/operational_domain_siqad.cpp new file mode 100644 index 000000000..ce9a07c35 --- /dev/null +++ b/experiments/operational_domain/operational_domain_siqad.cpp @@ -0,0 +1,188 @@ +// +// Created by marcel on 08.08.23. +// + +#include "fiction/algorithms/simulation/sidb/operational_domain.hpp" // operational domain computation algorithms +#include "fiction/io/read_sqd_layout.hpp" // reader for SiDB layouts +#include "fiction/io/write_operational_domain.hpp" // writer for operational domains +#include "fiction/types.hpp" // pre-defined types suitable for the FCN domain +#include "fiction/utils/truth_table_utils.hpp" // truth tables helper functions +#include "fiction_experiments.hpp" // experiment class + +#include // string formatting +#include // truth tables + +#include +#include +#include +#include +#include +#include +#include + +using namespace fiction; + +int main() // NOLINT +{ + experiments::experiment + opdomain_exp{ + "Operational Domain", "Name", "#SiDBs", // Benchmark + "#Samples (GS)", "op. (GS)", "sim calls (GS)", "t in s (GS)", // Grid Search + "#Samples (RS)", "op. (RS)", "sim calls (RS)", "t in s (RS)", // Random Sampling + "#Samples (FF)", "op. (FF)", "sim calls (FF)", "t in s (FF)", // Flood Fill + "#Samples (CT)", "op. (CT)", "sim calls (CT)", "t in s (CT)" // Contour Tracing + }; + + // simulation parameters + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + + // operational domain parameters + operational_domain_params op_domain_params{}; + op_domain_params.sim_params = sim_params; + op_domain_params.sim_engine = sidb_simulation_engine::QUICKEXACT; + op_domain_params.x_dimension = operational_domain::sweep_parameter::EPSILON_R; + op_domain_params.x_min = 1.0; + op_domain_params.x_max = 10.0; + op_domain_params.x_step = 0.05; + op_domain_params.y_dimension = operational_domain::sweep_parameter::LAMBDA_TF; + op_domain_params.y_min = 1.0; + op_domain_params.y_max = 10.0; + op_domain_params.y_step = 0.05; + + // write operational domain parameters + static const write_operational_domain_params write_op_domain_params{"1", "0"}; + + static const std::string folder = fmt::format("{}siqad_gates_type_tags/", EXPERIMENTS_PATH); + + static const std::array, 5> gates = { + std::make_pair("and", create_and_tt()), std::make_pair("nand", create_nand_tt()), + std::make_pair("xnor", create_xnor_tt()), std::make_pair("xor", create_xor_tt()), + std::make_pair("or", create_or_tt())}; + + // total number of samples + static std::size_t total_samples_gs = 0; + static std::size_t total_samples_rs = 0; + static std::size_t total_samples_ff = 0; + static std::size_t total_samples_ct = 0; + + // total number of simulator calls + static std::size_t total_sim_calls_gs = 0; + static std::size_t total_sim_calls_rs = 0; + static std::size_t total_sim_calls_ff = 0; + static std::size_t total_sim_calls_ct = 0; + + for (const auto& [gate, truth_table] : gates) + { + for (const auto& file : std::filesystem::directory_iterator(fmt::format("{}{}", folder, gate))) + { + const auto& benchmark = file.path(); + + std::cout << benchmark << std::endl; + + const auto lyt = read_sqd_layout(benchmark.string(), gate); + + // operational domain stats + operational_domain_stats op_domain_stats_gs{}; + operational_domain_stats op_domain_stats_rs{}; + operational_domain_stats op_domain_stats_ff{}; + operational_domain_stats op_domain_stats_ct{}; + + // compute the operational domains + const auto op_domain_gs = + operational_domain_grid_search(lyt, truth_table, op_domain_params, &op_domain_stats_gs); + const auto op_domain_rs = + operational_domain_random_sampling(lyt, truth_table, 2500, op_domain_params, &op_domain_stats_rs); + const auto op_domain_ff = + operational_domain_flood_fill(lyt, truth_table, 250, op_domain_params, &op_domain_stats_ff); + const auto op_domain_ct = + operational_domain_contour_tracing(lyt, truth_table, 100, op_domain_params, &op_domain_stats_ct); + + // write the operational domains to a CSV file + write_operational_domain(op_domain_gs, + fmt::format("{}operational_domain_grid_search_siqad_{}.csv", folder, gate), + write_op_domain_params); + write_operational_domain(op_domain_rs, + fmt::format("{}operational_domain_random_sampling_siqad_{}.csv", folder, gate), + write_op_domain_params); + write_operational_domain(op_domain_ff, + fmt::format("{}operational_domain_flood_fill_siqad_{}.csv", folder, gate), + write_op_domain_params); + write_operational_domain(op_domain_ct, + fmt::format("{}operational_domain_contour_tracing_siqad_{}.csv", folder, gate), + write_op_domain_params); + + // update the total number of samples + total_samples_gs += op_domain_stats_gs.num_evaluated_parameter_combinations; + total_samples_rs += op_domain_stats_rs.num_evaluated_parameter_combinations; + total_samples_ff += op_domain_stats_ff.num_evaluated_parameter_combinations; + total_samples_ct += op_domain_stats_ct.num_evaluated_parameter_combinations; + + // update the total number of simulator calls + total_sim_calls_gs += op_domain_stats_gs.num_simulator_invocations; + total_sim_calls_rs += op_domain_stats_rs.num_simulator_invocations; + total_sim_calls_ff += op_domain_stats_ff.num_simulator_invocations; + total_sim_calls_ct += op_domain_stats_ct.num_simulator_invocations; + + // compute the operational percentages + const auto operational_percentage_gs = + static_cast(op_domain_stats_gs.num_operational_parameter_combinations) / + static_cast(op_domain_stats_gs.num_evaluated_parameter_combinations); + const auto operational_percentage_rs = + static_cast(op_domain_stats_rs.num_operational_parameter_combinations) / + static_cast(op_domain_stats_rs.num_evaluated_parameter_combinations); + const auto operational_percentage_ff = + static_cast(op_domain_stats_ff.num_operational_parameter_combinations) / + static_cast(op_domain_stats_ff.num_evaluated_parameter_combinations); + const auto operational_percentage_ct = + static_cast(op_domain_stats_ct.num_operational_parameter_combinations) / + static_cast(op_domain_stats_ct.num_evaluated_parameter_combinations); + + opdomain_exp( + // Benchmark + benchmark.string(), lyt.num_cells(), + + // Grid Search + op_domain_stats_gs.num_evaluated_parameter_combinations, operational_percentage_gs, + op_domain_stats_gs.num_simulator_invocations, mockturtle::to_seconds(op_domain_stats_gs.time_total), + + // Random Sampling + op_domain_stats_rs.num_evaluated_parameter_combinations, operational_percentage_rs, + op_domain_stats_rs.num_simulator_invocations, mockturtle::to_seconds(op_domain_stats_rs.time_total), + + // Flood Fill + op_domain_stats_ff.num_evaluated_parameter_combinations, operational_percentage_ff, + op_domain_stats_ff.num_simulator_invocations, mockturtle::to_seconds(op_domain_stats_ff.time_total), + + // Contour Tracing + op_domain_stats_ct.num_evaluated_parameter_combinations, operational_percentage_ct, + op_domain_stats_ct.num_simulator_invocations, mockturtle::to_seconds(op_domain_stats_ct.time_total)); + } + + opdomain_exp.save(); + opdomain_exp.table(); + } + + // log the total number of samples and simulator calls + opdomain_exp( + // Benchmark + "Total", 0, + + // Grid Search + total_samples_gs, 0.0, total_sim_calls_gs, 0.0, + + // Random Sampling + total_samples_rs, 0.0, total_sim_calls_rs, 0.0, + + // Flood Fill + total_samples_ff, 0.0, total_sim_calls_ff, 0.0, + + // Contour Tracing + total_samples_ct, 0.0, total_sim_calls_ct, 0.0); + + opdomain_exp.save(); + opdomain_exp.table(); + + return EXIT_SUCCESS; +} diff --git a/experiments/random_sidb_layout_generation/random_sidb_layout_generation.cpp b/experiments/random_sidb_layout_generation/random_sidb_layout_generation.cpp index 635600c1a..9a48b5fd9 100644 --- a/experiments/random_sidb_layout_generation/random_sidb_layout_generation.cpp +++ b/experiments/random_sidb_layout_generation/random_sidb_layout_generation.cpp @@ -96,8 +96,9 @@ int main(int argc, const char* argv[]) // NOLINT // specifies whether positively charged SiDBs are allowed ("ALLOWED") or forbidden ("FORBIDDEN") const std::string charges_str = options["--positive_charges"]; // specifies whether positively charged SiDBs are allowed ("ALLOWED") or forbidden ("FORBIDDEN") - const positive_charges charges = - (charges_str == "ALLOWED") ? positive_charges::ALLOWED : positive_charges::FORBIDDEN; + const generate_random_sidb_layout_params::positive_charges charges = + (charges_str == "ALLOWED") ? generate_random_sidb_layout_params::positive_charges::ALLOWED : + generate_random_sidb_layout_params::positive_charges::FORBIDDEN; // sets the number of SiDBs for the first bunch of layouts const uint64_t lower_limit = std::stoull(options["--lower"]); // sets the number of SiDBs for the last bunch of layouts @@ -118,8 +119,6 @@ int main(int argc, const char* argv[]) // NOLINT std::cout << "step: " << step << std::endl; // generates random SiDB layouts as .sqd file - using cell_level_layout = cell_level_layout>>; - try { std::filesystem::path folder_path(EXPERIMENTS_PATH); @@ -165,11 +164,11 @@ int main(int argc, const char* argv[]) // NOLINT std::cout << "Folder already exists." << std::endl; } - const generate_random_sidb_layout_params params{ + const generate_random_sidb_layout_params params{ {{nw_x, nw_y}, {se_x, se_y}}, number_of_placed_sidbs, charges, 2, static_cast(10E6), number_of_layouts}; const auto unique_lyts = - generate_multiple_random_sidb_layouts(cell_level_layout{}, params); + generate_multiple_random_sidb_layouts(sidb_cell_clk_lyt{}, params); for (auto i = 0u; i < unique_lyts.size(); i++) { write_sqd_layout(unique_lyts[i], fmt::format("{}/layout_{}.sqd", dir_path_sqd.string(), i)); diff --git a/include/fiction/algorithms/iter/aspect_ratio_iterator.hpp b/include/fiction/algorithms/iter/aspect_ratio_iterator.hpp index 87e88fdaa..44c6352ba 100644 --- a/include/fiction/algorithms/iter/aspect_ratio_iterator.hpp +++ b/include/fiction/algorithms/iter/aspect_ratio_iterator.hpp @@ -5,9 +5,9 @@ #ifndef FICTION_ASPECT_RATIO_ITERATOR_HPP #define FICTION_ASPECT_RATIO_ITERATOR_HPP -#include #include #include +#include #include namespace fiction @@ -75,47 +75,47 @@ class aspect_ratio_iterator return result; } - AspectRatio operator*() const + [[nodiscard]] AspectRatio operator*() const { return *it; } - bool operator==(const uint64_t m) const noexcept + [[nodiscard]] bool operator==(const uint64_t m) const noexcept { return num == m; } - bool operator==(const aspect_ratio_iterator& other) const + [[nodiscard]] bool operator==(const aspect_ratio_iterator& other) const { return (num == other.num) && (*it == *(other.it)); } - bool operator!=(const uint64_t m) const noexcept + [[nodiscard]] bool operator!=(const uint64_t m) const noexcept { return num != m; } - bool operator!=(const aspect_ratio_iterator& other) const + [[nodiscard]] bool operator!=(const aspect_ratio_iterator& other) const { return !(*this == other); } - bool operator<(const uint64_t m) const noexcept + [[nodiscard]] bool operator<(const uint64_t m) const noexcept { return num < m; } - bool operator<(const aspect_ratio_iterator& other) const + [[nodiscard]] bool operator<(const aspect_ratio_iterator& other) const { return (num < other.num) || (num == other.num && *it < *(other.it)); } - bool operator<=(const uint64_t m) const noexcept + [[nodiscard]] bool operator<=(const uint64_t m) const noexcept { return num <= m; } - bool operator<=(const aspect_ratio_iterator& other) const + [[nodiscard]] bool operator<=(const aspect_ratio_iterator& other) const { return (num <= other.num) || (num == other.num && *it <= *(other.it)); } diff --git a/include/fiction/algorithms/iter/bdl_input_iterator.hpp b/include/fiction/algorithms/iter/bdl_input_iterator.hpp new file mode 100644 index 000000000..600a90eb9 --- /dev/null +++ b/include/fiction/algorithms/iter/bdl_input_iterator.hpp @@ -0,0 +1,330 @@ +// +// Created by marcel on 24.07.23. +// + +#ifndef FICTION_BDL_INPUT_ITERATOR_HPP +#define FICTION_BDL_INPUT_ITERATOR_HPP + +#include "fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp" +#include "fiction/types.hpp" + +#include +#include +#include + +namespace fiction +{ + +/** + * Iterator that iterates over all possible input states of a BDL layout. There are \f$ 2^n \f$ possible input states + * for an \f$n\f$-input BDL layout, each with a unique input index. The input index is interpreted as a binary number, + * where the \f$i\f$-th bit represents the input state of the \f$i\f$-th input BDL pair. If the bit is `1`, the lower + * BDL dot is set and the upper BDL dot removed. If the bit is `0`, the upper BDL dot is removed and the lower BDL dot + * set. The iterator creates and stores a deep-copy of the given layout. The state enumeration wraps around, i.e. after + * the last possible input state, the first input state is set again. + * + * The iterator satisfies the requirements of `LegacyRandomAccessIterator` and can be used in iterator-based `for` + * loops. + * + * @tparam Lyt SiDB cell-level layout type. + */ +template +class bdl_input_iterator +{ + public: + /** + * Standard constructor. It alters the layout to set the first input state, which assigns binary `0` to all input + * BDL pairs. + * + * @param lyt The SiDB BDL layout to iterate over. + * @param params Parameters for the BDL pair detection. + */ + explicit bdl_input_iterator(const Lyt& lyt, const detect_bdl_pairs_params& params = {}) noexcept : + layout{lyt.clone()}, + input_pairs{detect_bdl_pairs(layout, sidb_technology::cell_type::INPUT, params)}, + num_inputs{static_cast(input_pairs.size())} + { + 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"); + + set_all_inputs(); + } + /** + * Dereference operator. Returns a reference to the layout with the current input state. + * + * @return Reference to the current layout. + */ + [[nodiscard]] const Lyt& operator*() const noexcept + { + return layout; + } + /** + * Prefix increment operator. Sets the next input state. + * + * @return Reference to `this`. + */ + bdl_input_iterator& operator++() noexcept + { + ++current_input_index; + + set_all_inputs(); + + return *this; + } + /** + * Postfix increment operator. Sets the next input state. + * + * @return Copy of `this` before incrementing. + */ + bdl_input_iterator operator++(int) noexcept + { + auto result{*this}; + + ++(*this); + + return result; + } + /** + * Addition operator. Computes the input state of the current iterator plus the given integer. + * + * @param m The amount of input states to skip. + * @return The input state of the current iterator plus the given integer. + */ + [[nodiscard]] bdl_input_iterator operator+(const int m) const noexcept + { + auto result{*this}; + + result += m; + + return result; + } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" + /** + * Addition assignment operator. Sets a next input state. + * + * @param m The amount of input states to skip. + * @return Reference to `this`. + */ + bdl_input_iterator& operator+=(const int m) noexcept + { + current_input_index += m; + + set_all_inputs(); + + return *this; + } +#pragma GCC diagnostic pop + /** + * Subtraction operator. Computes the input state of the current iterator minus the given integer. + * + * @param m The amount of input states to skip. + * @return The input state of the current iterator minus the given integer. + */ + [[nodiscard]] bdl_input_iterator operator-(const int m) const noexcept + { + auto result{*this}; + + result -= m; + + return result; + } + /** + * Prefix decrement operator. Sets the previous input state. + * + * @return Reference to `this`. + */ + bdl_input_iterator& operator--() noexcept + { + --current_input_index; + + set_all_inputs(); + + return *this; + } + /** + * Postfix decrement operator. Sets the previous input state. + * + * @return Copy of `this` before decrementing. + */ + bdl_input_iterator operator--(int) noexcept + { + auto result{*this}; + + --(*this); + + return result; + } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" + /** + * Subtraction assignment operator. Sets a previous input state. + * + * @param m The amount of input states to skip. + * @return Reference to `this`. + */ + bdl_input_iterator& operator-=(const int m) noexcept + { + current_input_index -= m; + + set_all_inputs(); + + return *this; + } +#pragma GCC diagnostic pop + /** + * Assignment operator. Sets the input state to the given integer. + * + * @param m The input state to set. + */ + bdl_input_iterator& operator=(const uint64_t m) noexcept + { + current_input_index = m; + + set_all_inputs(); + + return *this; + } + /** + * Subscript operator. Computes the input state of the current iterator plus the given integer. + * + * @param m The amount of input states to skip. + * @return The input state of the current iterator plus the given integer. + */ + [[nodiscard]] bdl_input_iterator operator[](const int m) const noexcept + { + return (*this + m); + } + /** + * Subtraction operator. Computes the difference between the current input index and the given iterator ones. + * + * @param other Iterator to compute the difference with. + * @return The difference between the current input index and the given iterator ones. + */ + [[nodiscard]] int64_t operator-(const bdl_input_iterator& other) const noexcept + { + return static_cast(current_input_index) - static_cast(other.current_input_index); + } + /** + * Equality operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is equal to `m`, `false` otherwise. + */ + [[nodiscard]] bool operator==(const uint64_t m) const noexcept + { + return current_input_index == m; + } + /** + * Inequality operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is not equal to `m`, `false` otherwise. + */ + [[nodiscard]] bool operator!=(const uint64_t m) const noexcept + { + return current_input_index != m; + } + /** + * Less-than operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is less than `m`, `false` otherwise. + */ + [[nodiscard]] bool operator<(const uint64_t m) const noexcept + { + return current_input_index < m; + } + /** + * Less-or-equal-than operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is less than or equal to `m`, `false` otherwise. + */ + [[nodiscard]] bool operator<=(const uint64_t m) const noexcept + { + return current_input_index <= m; + } + /** + * Greater-than operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is greater than `m`, `false` otherwise. + */ + [[nodiscard]] bool operator>(const uint64_t m) const noexcept + { + return current_input_index > m; + } + /** + * Greater-or-equal-than operator. Compares the current input index with the given integer. + * + * @param m Integer to compare with. + * @return `true` if the current input index is greater than or equal to `m`, `false` otherwise. + */ + [[nodiscard]] bool operator>=(const uint64_t m) const noexcept + { + return current_input_index >= m; + } + + private: + /** + * The layout to iterate over. + */ + Lyt layout; + /** + * The detected input BDL pairs. + */ + const std::vector> input_pairs; + /** + * The amount of input BDL pairs. + */ + const uint8_t num_inputs; + /** + * The current input index. There are \f$ 2^n \f$ possible input states for an \f$n\f$-input BDL layout. + */ + uint64_t current_input_index{0ull}; + + /** + * Sets all input cells of the layout according to the current input index. The input index is interpreted as a + * binary number, where the \f$i\f$-th bit represents the input state of the \f$i\f$-th input BDL pair. If the bit + * is `1`, the lower BDL dot is set and the upper BDL dot removed. If the bit is `0`, the upper BDL dot is removed + * and the lower BDL dot set. + */ + void set_all_inputs() noexcept + { + for (uint8_t i = 0; i < num_inputs; ++i) + { + const auto& input_i = input_pairs[i]; + + if ((current_input_index & (uint64_t{1ull} << i)) != 0ull) + { + // set input i to 1 + layout.assign_cell_type(input_i.upper, technology::cell_type::EMPTY); + layout.assign_cell_type(input_i.lower, technology::cell_type::INPUT); + } + else + { + // set input i to 0 + layout.assign_cell_type(input_i.upper, technology::cell_type::INPUT); + layout.assign_cell_type(input_i.lower, technology::cell_type::EMPTY); + } + } + } +}; + +} // namespace fiction + +// make `bdl_input_iterator` compatible with STL iterator categories +namespace std +{ +template +struct iterator_traits> +{ + using iterator_category = std::random_access_iterator_tag; + using difference_type = int64_t; + using value_type = Lyt; +}; +} // namespace std + +#endif // FICTION_BDL_INPUT_ITERATOR_HPP diff --git a/include/fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp b/include/fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp index d876b28ad..6523da71a 100644 --- a/include/fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp +++ b/include/fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp @@ -13,6 +13,7 @@ namespace fiction { + /** * This algorithm determines if positively charged SiDBs can occur in a given SiDB cell-level layout due to strong * electrostatic interaction. @@ -22,29 +23,32 @@ namespace fiction * @param sim_params Physical parameters used to determine whether positively charged SiDBs can occur. */ template -bool can_positive_charges_occur(const Lyt& lyt, const sidb_simulation_parameters& sim_params) +[[nodiscard]] bool can_positive_charges_occur(const Lyt& lyt, const sidb_simulation_parameters& sim_params) 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"); - bool result = false; const auto mu_plus = sim_params.mu_plus(); + bool result = false; + // The charge layout is initialized with negatively charged SiDBs. Therefore, the local electrostatic potentials are // maximal. In this extreme case, if the banding is not sufficient for any SiDB to be positively charged, it will // not be for any other charge distribution. Therefore, no positively charged SiDBs can occur. const charge_distribution_surface charge_lyt{lyt, sim_params, sidb_charge_state::NEGATIVE}; charge_lyt.foreach_cell( - [&result, &mu_plus, charge_lyt](const auto& c) + [&result, &mu_plus, charge_lyt](const auto& c) noexcept { if (const auto local_pot = charge_lyt.get_local_potential(c); local_pot.has_value()) { if ((-(*local_pot) + mu_plus) > -physical_constants::POP_STABILITY_ERR) { result = true; + return false; // break } } + return true; }); diff --git a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp index 56d07f518..1ef6e4acc 100644 --- a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp +++ b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp @@ -38,46 +38,47 @@ namespace fiction { -/** - * 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. + * This struct stores the parameters for the `critical_temperature` algorithm. */ -enum class simulation_engine +struct critical_temperature_params { /** - * This simulation engine computes Critical Temperature values with 100 % accuracy. + * An enumeration of modes to use for the Critical Temperature Simulation. */ - EXACT, + 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 + }; + /** - * This simulation engine quickly calculates the Critical Temperature. However, there may be deviations from the - * exact Critical Temperature. This mode is recommended for larger layouts (> 40 SiDBs). + * An enumeration of simulation modes (exact vs. approximate) to use for the Critical Temperature Simulation. */ - APPROXIMATE -}; + enum class simulation_engine + { + /** + * This simulation engine computes Critical Temperature values with 100 % accuracy. + */ + EXACT, + /** + * This simulation engine quickly calculates the Critical Temperature. However, there may be deviations from the + * exact Critical Temperature. This mode is recommended for larger layouts (> 40 SiDBs). + */ + APPROXIMATE + }; -/** - * This struct stores the parameters for the `critical_temperature` algorithm. - */ -struct critical_temperature_params -{ /** * Simulation mode to determine the Critical Temperature. */ @@ -179,13 +180,13 @@ class critical_temperature_impl } sidb_simulation_result simulation_results{}; - if (parameter.engine == simulation_engine::EXACT) + if (parameter.engine == critical_temperature_params::simulation_engine::EXACT) { temperature_stats.algorithm_name = "QuickExact"; // 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 params{parameter.simulation_params.phys_params, - automatic_base_number_detection::OFF}; + quickexact_params::automatic_base_number_detection::OFF}; simulation_results = quickexact(layout, params); } else @@ -309,13 +310,13 @@ class critical_temperature_impl bool non_gate_based_simulation() { sidb_simulation_result simulation_results{}; - if (parameter.engine == simulation_engine::EXACT) + if (parameter.engine == critical_temperature_params::simulation_engine::EXACT) { temperature_stats.algorithm_name = "QuickExact"; // 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 params{parameter.simulation_params.phys_params, - automatic_base_number_detection::OFF}; + quickexact_params::automatic_base_number_detection::OFF}; simulation_results = quickexact(layout, params); } else @@ -490,7 +491,7 @@ bool critical_temperature(const Lyt& lyt, const critical_temperature_params& par bool result = false; - if (params.temperature_mode == critical_temperature_mode::GATE_BASED_SIMULATION) + if (params.temperature_mode == critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION) { result = p.gate_based_simulation(); } diff --git a/include/fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp b/include/fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp new file mode 100644 index 000000000..5169a91ce --- /dev/null +++ b/include/fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp @@ -0,0 +1,267 @@ +// +// Created by marcel on 21.07.23. +// + +#ifndef FICTION_DETECT_BDL_PAIRS_HPP +#define FICTION_DETECT_BDL_PAIRS_HPP + +#include "fiction/algorithms/path_finding/distance.hpp" +#include "fiction/technology/cell_technologies.hpp" +#include "fiction/traits.hpp" + +#include +#include +#include +#include + +namespace fiction +{ + +/** + * A Binary-dot Logic (BDL) pair is a pair of SiDBs that are close to each other and, thus, most likely share a charge. + * + * @tparam Lyt SiDB cell-level layout type. + */ +template +struct bdl_pair +{ + /** + * The type of the SiDBs in the pair. BDL SiDBs must be of the same type. They can either be normal, input, or + * output SiDBs. + */ + const sidb_technology::cell_type type{}; + /** + * The upper SiDB of the pair. Upper and lower are defined relative to each other via the `operator<` overload. + */ + const cell upper{}; + /** + * The lower SiDB of the pair. Upper and lower are defined relative to each other via the `operator<` overload. + */ + const cell lower{}; + /** + * Standard constructor for empty BDL pairs. + */ + bdl_pair() = default; + /** + * Constructor for BDL pairs. + * + * @param t Type of the SiDBs in the pair. + * @param u The upper SiDB of the pair. + * @param l The lower SiDB of the pair. + */ + bdl_pair(const sidb_technology::cell_type t, const cell& u, const cell& l) noexcept : + type{t}, + upper{u}, + lower{l} + { + 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"); + } +}; + +/** + * Parameters for the BDL pair detection algorithms. + */ +struct detect_bdl_pairs_params +{ + /** + * The minimum distance between two dots to be considered a BDL pair. This is useful to prevent, e.g., SiDBs of + * atomic wires to be considered BDL pairs. (unit: nm). + */ + double minimum_distance{0.75}; + /** + * The maximum distance between two dots to be considered a BDL pair. This is useful to prevent unlikely pairings + * of SiDBs that are far apart and to improve performance of the matching algorithm. (unit: nm). + */ + double maximum_distance{1.5}; +}; + +/** + * This algorithm detects BDL pairs in an SiDB layout. It does so by first collecting all dots of the given type and + * then uniquely pairing them up based on their distance. Lower and upper distance thresholds can be defined (defaults = + * 0.75 nm and 1.5 nm, respectively) to narrow down the range in which SiDBs could be considered a BDL pair. The + * distance between two dots is computed using the `sidb_nanometer_distance` function. The algorithm returns a vector of + * BDL pairs. + * + * @tparam Lyt SiDB cell-level layout type. + * @param lyt The layout to detect BDL pairs in. + * @param type The type of the SiDBs to detect BDL pairs for, e.g., `INPUT`, `OUTPUT`, `NORMAL`. + * @param params Parameters for the BDL pair detection algorithm. + * @return A vector of BDL pairs. + */ +template +std::vector> detect_bdl_pairs(const Lyt& lyt, const typename technology::cell_type type, + const detect_bdl_pairs_params& params = {}) 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(has_siqad_coord_v, "Lyt is not based on SiQAD coordinates"); + + // sanity check for parameter settings + assert(params.minimum_distance <= params.maximum_distance); + + /** + * Pairs up dots based on their distance. It does so by first computing the pairwise distances between all dots and + * then sorting them. The smallest distances are then used to pair up the dots. The function takes a vector of dots + * as input. + */ + const auto pair_up_dots = [&lyt, &type, + ¶ms](const std::vector>& dots) noexcept -> std::vector> + { + /** + * Container for pairwise dot distances used in the pairing algorithm. + */ + struct pairwise_dot_distance + { + /** + * First dot. + */ + cell sidb1{}; + /** + * Second dot. + */ + cell sidb2{}; + /** + * Distance between the two dots. (unit: nm). + */ + double distance{}; + /** + * Standard constructor for empty pairwise dot distances. + */ + pairwise_dot_distance() = default; + /** + * Constructor for pairwise dot distances. + * + * @param s1 The first dot. + * @param s2 The second dot. + * @param d The distance between the two dots. (unit: nm). + */ + pairwise_dot_distance(const cell& s1, const cell& s2, const double d) noexcept : + sidb1{s1}, + sidb2{s2}, + distance{d} + {} + }; + /** + * Computes the pairwise distances between all dots in the input vector. + */ + const auto compute_pairwise_dot_distances = [&lyt, &dots]() noexcept -> std::vector + { + std::vector pairwise_distances{}; + pairwise_distances.reserve((dots.size() * (dots.size() - 1)) / 2); + + for (auto i = 0u; i < dots.size(); ++i) + { + for (auto j = i + 1; j < dots.size(); ++j) + { + pairwise_distances.emplace_back(dots[i], dots[j], sidb_nanometer_distance(lyt, dots[i], dots[j])); + } + } + + return pairwise_distances; + }; + /** + * Comparator for pairwise dot distances. Used in the sorting algorithm. + */ + const auto dot_distance_comparator = [](const auto& lhs, const auto& rhs) noexcept -> bool + { return lhs.distance < rhs.distance; }; + + // container for the detected BDL pairs + std::vector> bdl_pairs{}; + bdl_pairs.reserve(dots.size() / 2); + + // compute pairwise distances + auto pairwise_distances = compute_pairwise_dot_distances(); + // sort pairwise distances + std::sort(pairwise_distances.begin(), pairwise_distances.end(), dot_distance_comparator); + // pair unique dots with the smallest distance + std::unordered_set> paired_dots{}; + paired_dots.reserve(dots.size()); + /** + * Checks whether a dot has already been paired up. + */ + const auto already_paired_up = [&paired_dots](const auto& dot) noexcept -> bool + { return paired_dots.find(dot) != paired_dots.cend(); }; + + for (const auto& potential_bdl_pair : pairwise_distances) + { + // if the distance is smaller than the lower bound threshold, we can continue to the next pairing; this + // prevents the pairing of dots that are too close to each other, e.g., in an atomic wire + if (potential_bdl_pair.distance < params.minimum_distance) + { + continue; + } + // if the distance is larger than the upper bound threshold, we can break the loop because the remaining + // distances must be larger as well due to the prior sorting; this prevents unlikely pairings and helps + // performance + if (potential_bdl_pair.distance > params.maximum_distance) + { + break; + } + + // if either dot has already been matched, skip + if (already_paired_up(potential_bdl_pair.sidb1) || already_paired_up(potential_bdl_pair.sidb2)) + { + continue; + } + + // a BDL pair has been detected (swap SiDBs if necessary) + if (potential_bdl_pair.sidb1 > potential_bdl_pair.sidb2) + { + bdl_pairs.emplace_back(type, potential_bdl_pair.sidb2, potential_bdl_pair.sidb1); + } + else + { + bdl_pairs.emplace_back(type, potential_bdl_pair.sidb1, potential_bdl_pair.sidb2); + } + + // mark the dots as paired + paired_dots.insert(potential_bdl_pair.sidb1); + paired_dots.insert(potential_bdl_pair.sidb2); + } + + return bdl_pairs; + }; + + // collect all dots of the given type + std::vector> dots_of_given_type{}; + switch (type) + { + case (technology::cell_type::INPUT): + { + dots_of_given_type.reserve(lyt.num_pis()); + lyt.foreach_pi([&dots_of_given_type](const auto& pi) { dots_of_given_type.push_back(pi); }); + + break; + } + case (technology::cell_type::OUTPUT): + { + dots_of_given_type.reserve(lyt.num_pos()); + lyt.foreach_po([&dots_of_given_type](const auto& po) { dots_of_given_type.push_back(po); }); + + break; + } + default: + { + dots_of_given_type.reserve(lyt.num_cells()); + lyt.foreach_cell( + [&lyt, &type, &dots_of_given_type](const auto& c) + { + if (lyt.get_cell_type(c) == type) + { + dots_of_given_type.push_back(c); + } + }); + + break; + } + } + + // pair up dots and return the detected BDL pairs + return pair_up_dots(dots_of_given_type); +} + +} // namespace fiction + +#endif // FICTION_DETECT_BDL_PAIRS_HPP diff --git a/include/fiction/algorithms/simulation/sidb/energy_distribution.hpp b/include/fiction/algorithms/simulation/sidb/energy_distribution.hpp index 19b7059a2..06e1d1461 100644 --- a/include/fiction/algorithms/simulation/sidb/energy_distribution.hpp +++ b/include/fiction/algorithms/simulation/sidb/energy_distribution.hpp @@ -18,16 +18,16 @@ namespace fiction /** * Data type to collect electrostatic potential energies (unit: eV) of charge distributions with corresponding - * degeneracy (i.e. how often a certain energy value occurs). + * degeneracy (i.e., how often a certain energy value occurs). */ -using sidb_energy_distribution = std::map; // unit: (eV, unitless) +using sidb_energy_distribution = std::map; // unit: (eV, unit-less) /** - * This function takes in a vector of charge_distribution_surface objects and returns a map containing the system energy - * and the number of occurrences of that energy in the input vector. + * This function takes in a vector of `charge_distribution_surface` objects and returns a map containing the system + * energy and the number of occurrences of that energy in the input vector. * * @tparam Lyt Cell-level layout type. - * @param input_vec A vector of charge_distribution_surface objects for which statistics are to be computed. + * @param input_vec A vector of `charge_distribution_surface` objects for which statistics are to be computed. * @return A map containing the system energy as the key and the number of occurrences of that energy in the input * vector as the value. */ @@ -38,11 +38,11 @@ energy_distribution(const std::vector>& input_v 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"); - std::map distribution{}; // unit: (eV, unitless) + sidb_energy_distribution distribution{}; for (const auto& lyt : input_vec) { - const auto energy = round_to_n_decimal_places(lyt.get_system_energy(), 6); // rounding to 6 decimal places. + const auto energy = round_to_n_decimal_places(lyt.get_system_energy(), 6); // rounding to 6 decimal places distribution[energy]++; } diff --git a/include/fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp b/include/fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp deleted file mode 100644 index b3e75f232..000000000 --- a/include/fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Jan Drewniok on 06.06.23. -// - -#ifndef FICTION_ENUM_CLASS_EXHAUSTIVE_ALGORITHM_HPP -#define FICTION_ENUM_CLASS_EXHAUSTIVE_ALGORITHM_HPP - -namespace fiction -{ - -/** - * An enumeration of exact algorithms for the TTS-simulation. - */ -enum class exhaustive_algorithm -{ - /** - * ExGS - */ - EXGS, - /** - * QuickExact - */ - QUICKEXACT -}; - -} // namespace fiction - -#endif // FICTION_ENUM_CLASS_EXHAUSTIVE_ALGORITHM_HPP 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 35194369f..c6d2e0217 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 @@ -72,14 +72,15 @@ class maximum_defect_influence_position_and_distance_impl std::pair run() noexcept { - const quickexact_params> params_defect{params.physical_params, - automatic_base_number_detection::OFF}; + const quickexact_params> params_defect{ + params.physical_params, quickexact_params>::automatic_base_number_detection::OFF}; double avoidance_distance{0}; coordinate max_defect_position{}; const auto simulation_results = - quickexact(layout, quickexact_params{params.physical_params, automatic_base_number_detection::OFF}); + 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; diff --git a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp new file mode 100644 index 000000000..b05ace1a7 --- /dev/null +++ b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp @@ -0,0 +1,1258 @@ +// +// Created by marcel on 21.07.23. +// + +#ifndef FICTION_OPERATIONAL_DOMAIN_HPP +#define FICTION_OPERATIONAL_DOMAIN_HPP + +#include "fiction/algorithms/iter/bdl_input_iterator.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/quickexact.hpp" +#include "fiction/algorithms/simulation/sidb/quicksim.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fiction +{ + +/** + * An operational domain is a set of simulation parameter values for which a given SiDB layout is logically operational. + * This means that a layout is deemed operational if the layout's ground state corresponds with a given Boolean function + * at the layout's outputs for all possible input combinations. In this implementation, \f$ n \f$ BDL input wires and a + * single BDL output wire are assumed for a given layout. Any operational domain computation algorithm toggles through + * all \f$ 2^n \f$ input combinations and evaluates the layout's output behavior in accordance with the given Boolean + * function. The layout is only considered operational for a certain parameter combination, if the output behavior is + * correct for all input combinations. The operational domain can be computed by sweeping over specified simulation + * parameters and checking the operational status of the layout for each parameter combination. The operational domain + * is then defined as the set of all parameter combinations for which the layout is operational. Different techniques + * for performing these sweep are implemented. + */ +struct operational_domain +{ + /** + * Possible sweep parameters for the operational domain computation. + */ + enum class sweep_parameter + { + /** + * The relative permittivity of the dielectric material. + */ + EPSILON_R, + /** + * The Thomas-Fermi screening length. + */ + LAMBDA_TF, + /** + * The energy transition level. + */ + MU_MINUS + }; + /** + * X dimension sweep parameter. + */ + sweep_parameter x_dimension{operational_domain::sweep_parameter::EPSILON_R}; + /** + * Y dimension sweep parameter. + */ + sweep_parameter y_dimension{operational_domain::sweep_parameter::LAMBDA_TF}; + /** + * The parameter point holds parameter values in the x and y dimension. + */ + struct parameter_point + { + /** + * Standard default constructor. + */ + parameter_point() = default; + /** + * Standard constructor. + * + * @param x_val X dimension parameter value. + * @param y_val Y dimension parameter value. + */ + parameter_point(const double x_val, const double y_val) : x{x_val}, y{y_val} {} + /** + * X dimension parameter value. + */ + double x; + /** + * Y dimension parameter value. + */ + double y; + /** + * Equality operator. + * + * @param other Other parameter point to compare with. + * @return `true` iff the parameter points are equal. + */ + [[nodiscard]] bool operator==(const parameter_point& other) const noexcept + { + return x == other.x && y == other.y; + } + /** + * Inequality operator. + * + * @param other Other parameter point to compare with. + * @return `true` iff the parameter points are not equal. + */ + [[nodiscard]] bool operator!=(const parameter_point& other) const noexcept + { + return !(*this == other); + } + /** + * Support for structured bindings. + * + * @tparam I Index of the parameter value to be returned. + * @return The parameter value at the specified index. + */ + template + auto get() const noexcept + { + static_assert(I < 2, "Index out of bounds for parameter_point"); + + if constexpr (I == 0) + { + return x; + } + else // I == 1 + { + return y; + } + } + }; + /** + * Possible operational status of a layout. + */ + enum class operational_status + { + /** + * The layout is operational. + */ + OPERATIONAL, + /** + * The layout is non-operational. + */ + NON_OPERATIONAL + }; + /** + * The operational status of the layout for each specified parameter combination. This constitutes the operational + * domain. The key of the map is the parameter point, which holds the parameter values in the x and y dimension. + * The operational status is stored as the value of the map. + */ + locked_parallel_flat_hash_map operational_values{}; +}; + +/** + * Parameters for the operational domain computation. The parameters are used across the different operational domain + * computation algorithms. + */ +struct operational_domain_params +{ + /** + * The simulation parameters for the operational domain computation. Most parameters will be kept constant across + * sweeps, but the sweep parameters are adjusted in each simulation step and thus overwritten in this object. + */ + sidb_simulation_parameters sim_params{}; + /** + * The simulation engine to be used for the operational domain computation. + */ + sidb_simulation_engine sim_engine{sidb_simulation_engine::QUICKEXACT}; + /** + * The sweep parameter for the x dimension. + */ + operational_domain::sweep_parameter x_dimension{operational_domain::sweep_parameter::EPSILON_R}; + /** + * The minimum value of the x dimension sweep. + */ + double x_min{1.0}; + /** + * The maximum value of the x dimension sweep. + */ + double x_max{10.0}; + /** + * The step size of the x dimension sweep. + */ + double x_step{0.1}; + /** + * The sweep parameter for the y dimension. + */ + operational_domain::sweep_parameter y_dimension{operational_domain::sweep_parameter::LAMBDA_TF}; + /** + * The minimum value of the y dimension sweep. + */ + double y_min{1.0}; + /** + * The maximum value of the y dimension sweep. + */ + double y_max{10.0}; + /** + * The step size of the y dimension sweep. + */ + double y_step{0.1}; + /** + * The parameters for the BDL pair detection, which is necessary during the operational domain computation to + * detect input and output BDL pairs. + */ + detect_bdl_pairs_params bdl_params{}; +}; +/** + * Statistics for the operational domain computation. The statistics are used across the different operational domain + * computation algorithms. + */ +struct operational_domain_stats +{ + /** + * The total runtime of the operational domain computation. + */ + mockturtle::stopwatch<>::duration time_total{0}; + /** + * Number of simulator invocations. + */ + std::size_t num_simulator_invocations{0}; + /** + * Number of evaluated parameter combinations. + */ + std::size_t num_evaluated_parameter_combinations{0}; + /** + * Number of parameter combinations, for which the layout is operational. + */ + std::size_t num_operational_parameter_combinations{0}; + /** + * Number of parameter combinations, for which the layout is non-operational. + */ + std::size_t num_non_operational_parameter_combinations{0}; +}; + +namespace detail +{ + +template +class operational_domain_impl +{ + public: + /** + * Standard constructor. Initializes the layout, the truth table, the parameters and the statistics. Also detects + * the output BDL pair, which is necessary for the operational domain computation. The layout must have exactly + * one output BDL pair. + * + * @param lyt SiDB cell-level layout to be evaluated. + * @param tt Truth table of the Boolean function, which the layout should implement. + * @param ps Parameters for the operational domain computation. + * @param st Statistics of the process. + */ + operational_domain_impl(const Lyt& lyt, const TT& tt, const operational_domain_params& ps, + operational_domain_stats& st) noexcept : + layout{lyt}, + truth_table{tt}, + params{ps}, + stats{st}, + output_bdl_pairs{detect_bdl_pairs(layout, sidb_technology::cell_type::OUTPUT, params.bdl_params)}, + x_indices(num_x_steps()), // pre-allocate the x dimension indices + y_indices(num_y_steps()), // pre-allocate the y dimension indices + x_values(num_x_steps()), // pre-allocate the x dimension values + y_values(num_y_steps()) // pre-allocate the y dimension values + { + assert(output_bdl_pairs.size() == 1 && "The layout must have exactly one output BDL pair"); + + op_domain.x_dimension = params.x_dimension; + op_domain.y_dimension = params.y_dimension; + + std::iota(x_indices.begin(), x_indices.end(), 0ul); + std::iota(y_indices.begin(), y_indices.end(), 0ul); + + // generate the x dimension values + std::generate(x_values.begin(), x_values.end(), + [x = 0, this]() mutable + { + const double x_val = params.x_min + x * params.x_step; + ++x; + return x_val; + }); + + // generate the y dimension values + std::generate(y_values.begin(), y_values.end(), + [y = 0, this]() mutable + { + const double y_val = params.y_min + y * params.y_step; + ++y; + return y_val; + }); + } + /** + * Performs a grid search over the specified parameter ranges with the specified step sizes. The grid search always + * has quadratic complexity. The operational status is computed for each parameter combination. + * + * @return The operational domain of the layout. + */ + [[nodiscard]] operational_domain grid_search() noexcept + { + mockturtle::stopwatch stop{stats.time_total}; + + // for each x value in parallel + std::for_each(FICTION_EXECUTION_POLICY_PAR_UNSEQ x_indices.cbegin(), x_indices.cend(), + [this](const auto x) + { + // for each y value in parallel + std::for_each(FICTION_EXECUTION_POLICY_PAR_UNSEQ y_indices.cbegin(), y_indices.cend(), + [this, x](const auto y) { + is_operational({x, y}); + }); + }); + + log_stats(); + + return op_domain; + } + /** + * Performs a random sampling of the specified number of samples within the specified parameter range. The + * operational status is computed for each sample point. + * + * @param samples Number of random samples to be taken. + * @return The (partial) operational domain of the layout. + */ + [[nodiscard]] operational_domain random_sampling(const std::size_t samples) noexcept + { + mockturtle::stopwatch stop{stats.time_total}; + + const auto step_point_samples = generate_random_step_points(samples); + + // for each sample point in parallel + std::for_each(FICTION_EXECUTION_POLICY_PAR_UNSEQ step_point_samples.cbegin(), step_point_samples.cend(), + [this](const auto& sp) { is_operational(sp); }); + + log_stats(); + + return op_domain; + } + /** + * Performs flood fill to determine the operational domain. The algorithm first performs a random sampling of the + * specified number of samples. From each operational point found in this way, it starts the flood fill. The + * operational domain will finally only contain up to `samples` random non-operational points as well as all + * operational points that are reachable via flood fill from the found operational points plus a one pixel wide + * border around the domain. + * + * @param samples Maximum number of random samples to be taken before flood fill. + * @return The (partial) operational domain of the layout. + */ + [[nodiscard]] operational_domain flood_fill(const std::size_t samples) noexcept + { + mockturtle::stopwatch stop{stats.time_total}; + + const auto step_point_samples = generate_random_step_points(samples); + + // for each sample point in parallel + std::for_each(FICTION_EXECUTION_POLICY_PAR_UNSEQ step_point_samples.cbegin(), step_point_samples.cend(), + [this](const auto& sp) { is_operational(sp); }); + + // a queue of (x, y) dimension step points to be evaluated + std::queue queue{}; + + // a utility function that adds the adjacent points to the queue for further evaluation + const auto queue_next_points = [this, &queue](const step_point& sp) + { + for (const auto& m : moore_neighborhood(sp)) + { + if (!has_already_been_sampled(m)) + { + queue.push(m); + } + } + }; + + // add the neighbors of each operational point to the queue + for (const auto& [param_point, status] : op_domain.operational_values) + { + if (status == operational_domain::operational_status::OPERATIONAL) + { + queue_next_points(to_step_point(param_point)); + } + } + + // for each point in the queue + while (!queue.empty()) + { + // fetch the step point and remove it from the queue + const auto sp = queue.front(); + queue.pop(); + + // if the point has already been sampled, continue with the next + if (has_already_been_sampled(sp)) + { + continue; + } + + // check if the point is operational + const auto operational_status = is_operational(sp); + + // if the point is operational, add its eight neighbors to the queue + if (operational_status == operational_domain::operational_status::OPERATIONAL) + { + queue_next_points(sp); + } + } + + log_stats(); + + return op_domain; + } + /** + * Performs contour tracing to determine the operational domain. The algorithm first performs a random sampling of + * up to the specified number of samples. It stops random sampling once it finds a single operational point, from + * which it moves straight outwards until it encounters the counter of the operational domain. From this point, it + * traces the contour until it reaches the initial contour point again. The operational domain will finally only + * contain up to `samples` random non-operational points as well as the contour of the found operational domain plus + * a one pixel wide border around it. + * + * @param samples Maximum number of random samples to be taken before contour tracing. + * @return The (partial) operational domain of the layout. + */ + [[nodiscard]] operational_domain contour_tracing(const std::size_t samples) noexcept + { + mockturtle::stopwatch stop{stats.time_total}; + + // first, perform random sampling to find an operational starting point + const auto starting_point = find_operational_step_point_via_random_sampling(samples); + + // if no operational point was found within the specified number of samples, return + if (!starting_point.has_value()) + { + return op_domain; + } + + const auto next_clockwise_point = [](std::vector& neighborhood, + const step_point& backtrack) noexcept -> step_point + { + assert(std::find(neighborhood.cbegin(), neighborhood.cend(), backtrack) != neighborhood.cend() && + "The backtrack point must be part of the neighborhood"); + + while (neighborhood.back() != backtrack) + { + std::rotate(neighborhood.begin(), neighborhood.begin() + 1, neighborhood.end()); + } + + return neighborhood.front(); + }; + + // find an operational point on the contour starting from the randomly determined starting point + const auto contour_starting_point = find_operational_contour_step_point(*starting_point); + + auto current_contour_point = contour_starting_point; + auto backtrack_point = current_contour_point.x == 0 ? + current_contour_point : + step_point{current_contour_point.x - 1, current_contour_point.y}; + + auto current_neighborhood = moore_neighborhood(current_contour_point); + auto next_point = current_contour_point == backtrack_point ? + current_neighborhood.front() : + next_clockwise_point(current_neighborhood, backtrack_point); + + while (next_point != contour_starting_point) + { + const auto operational_status = is_operational(next_point); + + if (operational_status == operational_domain::operational_status::OPERATIONAL) + { + backtrack_point = current_contour_point; + current_contour_point = next_point; + } + else + { + backtrack_point = next_point; + } + + current_neighborhood = moore_neighborhood(current_contour_point); + next_point = next_clockwise_point(current_neighborhood, backtrack_point); + } + + log_stats(); + + return op_domain; + } + + private: + /** + * The SiDB cell-level layout to investigate. + */ + const Lyt& layout; + /** + * The specification of the layout. + */ + const TT& truth_table; // TODO implement the matching of multi-input truth table inputs and BDL pair ordering + /** + * The parameters for the operational domain computation. + */ + const operational_domain_params& params; + /** + * The statistics of the operational domain computation. + */ + operational_domain_stats& stats; + /** + * The output BDL pair of the layout. + */ + const std::vector> output_bdl_pairs; + /** + * X dimension steps. + */ + std::vector x_indices; + /** + * Y dimension steps. + */ + std::vector y_indices; + /** + * All x dimension values. + */ + std::vector x_values; + /** + * All y dimension values. + */ + std::vector y_values; + /** + * The operational domain of the layout. + */ + operational_domain op_domain{}; + /** + * Number of simulator invocations. + */ + std::atomic num_simulator_invocations{0}; + /** + * Number of evaluated parameter combinations. + */ + std::atomic num_evaluated_parameter_combinations{0}; + /** + * A step point represents a point in the x and y dimension from 0 to the maximum number of steps. A step point does + * not hold the actual parameter values, but the step values in the x and y dimension, respectively. + * + * See `operational_domain::parameter_point` for a point that holds the actual parameter values. + */ + struct step_point + { + /** + * Standard default constructor. + */ + step_point() = default; + /** + * Standard constructor. + * + * @param x_step X dimension step value. + * @param y_step Y dimension step value. + */ + step_point(const std::size_t x_step, const std::size_t y_step) : x{x_step}, y{y_step} {} + /** + * X dimension step value. + */ + std::size_t x; + /** + * Y dimension step value. + */ + std::size_t y; + /** + * Equality operator. + * + * @param other Other step point to compare with. + * @return `true` iff the step points are equal. + */ + [[nodiscard]] bool operator==(const step_point& other) const noexcept + { + return x == other.x && y == other.y; + } + /** + * Inequality operator. + * + * @param other Other step point to compare with. + * @return `true` iff the step points are not equal. + */ + [[nodiscard]] bool operator!=(const step_point& other) const noexcept + { + return !(*this == other); + } + }; + /** + * Converts a step point to a parameter point. + * + * @param sp Step point to convert. + * @return The parameter point corresponding to the step point `sp`. + */ + [[nodiscard]] operational_domain::parameter_point to_parameter_point(const step_point& sp) const noexcept + { + return {x_values[sp.x], y_values[sp.y]}; + } + /** + * Converts a parameter point to a step point. + * + * @param pp Parameter point to convert. + * @return The step point corresponding to the parameter point `pp`. + */ + [[nodiscard]] step_point to_step_point(const operational_domain::parameter_point& pp) const noexcept + { + return {static_cast((pp.x - params.x_min) / params.x_step), + static_cast((pp.y - params.y_min) / params.y_step)}; + } + /** + * Calculates the number of steps in the x dimension based on the provided parameters. + * + * @return The number of steps in the x dimension. + */ + [[nodiscard]] inline std::size_t num_x_steps() const noexcept + { + return static_cast((params.x_max - params.x_min) / params.x_step); + } + /** + * Calculates the number of steps in the y dimension based on the provided parameters. + * + * @return The number of steps in the y dimension. + */ + [[nodiscard]] inline std::size_t num_y_steps() const noexcept + { + return static_cast((params.y_max - params.y_min) / params.y_step); + } + /** + * Potential sweep dimensions. + */ + enum class sweep_dimension : uint8_t + { + /** + * Sweep dimension X. + */ + X, + /** + * Sweep dimension Y. + */ + Y + }; + /** + * Helper function that sets the value of a sweep dimension in the simulation parameters. + * + * @param sim_parameters Simulation parameter object to set the sweep dimension `dim` to value `val`. + * @param val Value to set the dimension `dim` to. + * @param dim Sweep dimension to set the value `val` to. + */ + inline void set_dimension_value(sidb_simulation_parameters& sim_parameters, const double val, + const sweep_dimension dim) const noexcept + { + operational_domain::sweep_parameter sweep_parameter = + dim == sweep_dimension::X ? params.x_dimension : params.y_dimension; + + switch (sweep_parameter) + { + case operational_domain::sweep_parameter::EPSILON_R: + { + sim_parameters.epsilon_r = val; + break; + } + case operational_domain::sweep_parameter::LAMBDA_TF: + { + sim_parameters.lambda_tf = val; + break; + } + case operational_domain::sweep_parameter::MU_MINUS: + { + sim_parameters.mu_minus = val; + break; + } + default: + { + assert(false && "Unknown sweep parameter"); + } + } + } + /** + * Helper function that sets the value of the x dimension in the simulation parameters. + * + * @param sim_params Simulation parameter object to set the x dimension value of. + * @param val Value to set the x dimension to. + */ + inline void set_x_dimension_value(sidb_simulation_parameters& sim_params, const double val) const noexcept + { + set_dimension_value(sim_params, val, sweep_dimension::X); + } + /** + * Helper function that sets the value of the y dimension in the simulation parameters. + * + * @param sim_params Simulation parameter object to set the y dimension value of. + * @param val Value to set the y dimension to. + */ + inline void set_y_dimension_value(sidb_simulation_parameters& sim_params, const double val) const noexcept + { + set_dimension_value(sim_params, val, sweep_dimension::Y); + } + /** + * Determines whether the point at step position `(x, y)` has already been sampled and returns the operational value + * at `(x, y)` if it already exists. Here, `x` and `y` represent steps in the x and y dimension, respectively, not + * the actual values of the parameters. + * + * @param sp Step point to check. + * @return The operational status of the point at step position `sp = (x, y)` or `std::nullopt` if `(x, y)` has not + * been sampled yet. + */ + [[nodiscard]] inline std::optional + has_already_been_sampled(const step_point& sp) const noexcept + { + if (const auto it = op_domain.operational_values.find(to_parameter_point(sp)); + it != op_domain.operational_values.cend()) + { + return it->second; + } + + return std::nullopt; + } + /** + * Logs and returns the operational status at the given point `sp = (x, y)`. If the point has already been sampled, + * it returns the cached value. Otherwise, a ground state simulation is performed for all input combinations of the + * stored layout using the given simulation parameters. It terminates as soon as a non-operational state is found. + * In the worst case, the function performs \f$ 2^n \f$ simulations, where \f$ n \f$ is the number of inputs of the + * layout. This function is used by all operational domain computation techniques. + * + * Any investigated point is added to the stored `op_domain`, regardless of its operational status. + * + * @param sp Step point to be investigated. + * @return The operational status of the layout under the given simulation parameters. + */ + operational_domain::operational_status is_operational(const step_point& sp) noexcept + { + // if the point has already been sampled, return the stored operational status + if (const auto op_value = has_already_been_sampled(sp); op_value.has_value()) + { + return *op_value; + } + + // fetch the x and y dimension values + const auto param_point = to_parameter_point(sp); + + const auto operational = [this, ¶m_point]() + { + op_domain.operational_values[param_point] = operational_domain::operational_status::OPERATIONAL; + + return operational_domain::operational_status::OPERATIONAL; + }; + + const auto non_operational = [this, ¶m_point]() + { + op_domain.operational_values[param_point] = operational_domain::operational_status::NON_OPERATIONAL; + + return operational_domain::operational_status::NON_OPERATIONAL; + }; + + // increment the number of evaluated parameter combinations + ++num_evaluated_parameter_combinations; + + // take the first (and only) output BDL pair + const auto& output_bdl_pair = output_bdl_pairs.front(); + + // initialize a BDL input iterator + bdl_input_iterator bii{layout, params.bdl_params}; + + sidb_simulation_parameters sim_params = params.sim_params; + set_x_dimension_value(sim_params, param_point.x); + set_y_dimension_value(sim_params, param_point.y); + + // for each input combination + for (auto i = 0u; i < truth_table.num_bits(); ++i, ++bii) + { + // the expected output of the layout is the i-th bit of the truth table + const auto expected_output = kitty::get_bit(truth_table, i); + + ++num_simulator_invocations; + + if (can_positive_charges_occur(*bii, sim_params)) + { + return non_operational(); + } + + sidb_simulation_result sim_result{}; + + if (params.sim_engine == sidb_simulation_engine::EXGS) + { + // perform an exhaustive ground state simulation + sim_result = exhaustive_ground_state_simulation(*bii, sim_params); + } + else if (params.sim_engine == sidb_simulation_engine::QUICKSIM) + { + // perform a heuristic simulation + const quicksim_params qs_params{sim_params, 500, 0.6}; + sim_result = quicksim(*bii, qs_params); + } + else if (params.sim_engine == sidb_simulation_engine::QUICKEXACT) + { + // perform fast exact ground state simulation + const quickexact_params qe_params{sim_params}; + sim_result = quickexact(*bii, qe_params); + } + else + { + assert(false && "unsupported simulation engine"); + } + + // if no physically-valid charge distributions were found, the layout is non-operational + if (sim_result.charge_distributions.empty()) + { + return non_operational(); + } + + // if the ground state is degenerate, the layout is non-operational + if (const auto energy_distr = energy_distribution(sim_result.charge_distributions); + energy_distr.begin()->second > 1) + { + return non_operational(); + } + + // find the ground state, which is the charge distribution with the lowest energy + const auto ground_state = std::min_element( + sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend(), + [](const auto& lhs, const auto& rhs) { return lhs.get_system_energy() < rhs.get_system_energy(); }); + + // fetch the charge states of the output BDL pair + const auto charge_state_output_upper = ground_state->get_charge_state(output_bdl_pair.upper); + const auto charge_state_output_lower = ground_state->get_charge_state(output_bdl_pair.lower); + + // if the output charge states are equal, the layout is not operational + if (charge_state_output_lower == charge_state_output_upper) + { + return non_operational(); + } + // if the expected output is 1, the expected charge states are (upper, lower) = (0, -1) + if (expected_output) + { + if (charge_state_output_upper != sidb_charge_state::NEUTRAL || + charge_state_output_lower != sidb_charge_state::NEGATIVE) + { + return non_operational(); + } + } + // if the expected output is 0, the expected charge states are (upper, lower) = (-1, 0) + else + { + if (charge_state_output_upper != sidb_charge_state::NEGATIVE || + charge_state_output_lower != sidb_charge_state::NEUTRAL) + { + return non_operational(); + } + } + } + + // if we made it here, the layout is operational + return operational(); + } + /** + * Generates (potentially repeating) random `step_points` in the stored parameter range. The number of generated + * points is exactly equal to `samples`. + * + * @param samples Number of random `step_point`s to generate. + * @return A vector of random `step_point`s in the stored parameter range. + */ + [[nodiscard]] std::vector generate_random_step_points(const std::size_t samples) noexcept + { + static std::mt19937_64 generator{std::random_device{}()}; + + // instantiate distributions + std::uniform_int_distribution x_distribution{0, x_indices.size() - 1}; + std::uniform_int_distribution y_distribution{0, y_indices.size() - 1}; + + // container for the random samples + std::vector step_point_samples{}; + step_point_samples.reserve(samples); + + for (std::size_t i = 0; i < samples; ++i) + { + // sample x and y dimension + step_point_samples.emplace_back(x_distribution(generator), y_distribution(generator)); + } + + return step_point_samples; + } + /** + * Performs random sampling to find any operational parameter combination. This function is useful if a single + * starting point is required within the domain to expand from. This function returns the step in x and y dimension + * of the first operational point found. If no operational parameter combination can be found within the given + * number of samples, the function returns `std::nullopt`. + * + * This function adds any sampled points to the `op_domain` member variables. + * + * @param samples Maximum number of samples to take. Works as a timeout. + * @return The first operational step point, if any could be found, `std::nullopt` otherwise. + */ + [[nodiscard]] std::optional + find_operational_step_point_via_random_sampling(const std::size_t samples) noexcept + { + for (const auto& sample_step_point : generate_random_step_points(samples)) + { + // determine the operational status + const auto operational_value = is_operational(sample_step_point); + + // if the parameter combination is operational, return its step values in x and y dimension + if (operational_value == operational_domain::operational_status::OPERATIONAL) + { + return sample_step_point; + } + } + + return std::nullopt; + } + /** + * Finds a boundary starting point for the contour tracing algorithm. This function starts at the given starting + * point and moves towards the left edge of the parameter range. It returns the last operational point it + * encounters before it reaches the edge. If no non-operational point is found, the operational area extends outside + * the parameter range and the function returns the last operational point that was investigated, i.e., a point at + * the border of the parameter range. + * + * @param starting_point Starting step point for the boundary search. + * @return An operational step point at the edge of the operational domain `starting_point` is located in. + */ + [[nodiscard]] step_point find_operational_contour_step_point(const step_point& starting_point) noexcept + { + auto latest_operational_point = starting_point; + + // move towards the left border of the parameter range + for (std::size_t x = starting_point.x; x > 0; --x) + { + const auto left_step = step_point{x, starting_point.y}; + + const auto operational_status = is_operational(left_step); + + if (operational_status == operational_domain::operational_status::OPERATIONAL) + { + latest_operational_point = left_step; + } + else + { + return latest_operational_point; + } + } + + // if no boundary point was found, the operational area extends outside the parameter range + // return the latest operational point + return latest_operational_point; + } + /** + * Returns the Moore neighborhood of the step point at `sp = (x, y)`. The Moore neighborhood is the set of all + * points that are adjacent to `(x, y)` including the diagonals. Thereby, the Moore neighborhood contains up to 8 + * points as points outside of the parameter range are not gathered. The points are returned in clockwise order + * starting from the right neighbor. + * + * @param sp Step point to get the Moore neighborhood of. + * @return The Moore neighborhood of the step point at `sp = (x, y)`. + */ + [[nodiscard]] std::vector moore_neighborhood(const step_point& sp) const noexcept + { + std::vector neighbors{}; + neighbors.reserve(8); + + const auto& [x, y] = sp; + + const auto decr_x = (x > 0) ? x - 1 : x; + const auto incr_x = (x + 1 < x_indices.size()) ? x + 1 : x; + const auto decr_y = (y > 0) ? y - 1 : y; + const auto incr_y = (y + 1 < x_indices.size()) ? y + 1 : y; + + // add neighbors in clockwise direction + + // right + if (x != incr_x) + { + neighbors.emplace_back(incr_x, y); + } + // lower-right + if (x != incr_x && y != decr_y) + { + neighbors.emplace_back(incr_x, decr_y); + } + // down + if (y != decr_y) + { + neighbors.emplace_back(x, decr_y); + } + // lower-left + if (x != decr_x && y != decr_y) + { + neighbors.emplace_back(decr_x, decr_y); + } + // left + if (x != decr_x) + { + neighbors.emplace_back(decr_x, y); + } + // upper-left + if (x != decr_x && y != incr_y) + { + neighbors.emplace_back(decr_x, incr_y); + } + // up + if (y != incr_y) + { + neighbors.emplace_back(x, incr_y); + } + // upper-right + if (x != incr_x && y != incr_y) + { + neighbors.emplace_back(incr_x, incr_y); + } + + return neighbors; + }; + /** + * Helper function that writes the the statistics of the operational domain computation to the statistics object. + * Due to data races that can occur during the computation, each value is temporarily held in an atomic variable and + * written to the statistics object only after the computation has finished. + */ + void log_stats() const noexcept + { + stats.num_simulator_invocations = num_simulator_invocations; + stats.num_evaluated_parameter_combinations = num_evaluated_parameter_combinations; + + for (const auto& [param_point, status] : op_domain.operational_values) + { + if (status == operational_domain::operational_status::OPERATIONAL) + { + ++stats.num_operational_parameter_combinations; + } + else + { + ++stats.num_non_operational_parameter_combinations; + } + } + } +}; + +} // namespace detail + +/** + * Computes the operational domain of the given SiDB cell-level layout. 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 given truth table. The input BDL pairs of the layout are assumed to be in the same order as the + * inputs of the truth table. + * + * This algorithm uses a grid search to find the operational domain. The grid search is performed by exhaustively + * sweeping the parameter space in the x and y dimensions. Since grid search is exhaustive, the algorithm is guaranteed + * to find the operational domain, if it exists within the parameter range. However, the algorithm performs a quadratic + * number of operational checks on the layout, where each operational check consists of up to \f$ 2^n \f$ exact ground + * state simulations, where \f$ n \f$ is the number of inputs of the layout. Each exact ground state simulation has + * exponential complexity in of itself. Therefore, the algorithm is only feasible for small layouts with few inputs. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Truth table type. + * @param lyt Layout to compute the operational domain for. + * @param spec Expected truth table of the layout. + * @param params Operational domain computation parameters. + * @param stats Operational domain computation statistics. + * @return The operational domain of the layout. + */ +template +operational_domain operational_domain_grid_search(const Lyt& lyt, const TT& spec, + const operational_domain_params& params = {}, + operational_domain_stats* stats = nullptr) +{ + 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"); + static_assert(kitty::is_truth_table::value, "TT is not a truth table"); + + operational_domain_stats st{}; + detail::operational_domain_impl p{lyt, spec, params, st}; + + const auto result = p.grid_search(); + + if (stats) + { + *stats = st; + } + + return result; +} +/** + * Computes the operational domain of the given SiDB cell-level layout. 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 given truth table. The input BDL pairs of the layout are assumed to be in the same order as the + * inputs of the truth table. + * + * This algorithm uses random sampling to find a part of the operational domain that might not be complete. It performs + * a total of `samples` uniformly-distributed random samples within the parameter range. For each sample, the algorithm + * performs one operational check on the layout, where each operational check consists of up to \f$ 2^n \f$ exact + * ground state simulations, where \f$ n \f$ is the number of inputs of the layout. Each exact ground state simulation + * has exponential complexity in of itself. Therefore, the algorithm is only feasible for small layouts with few inputs. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Truth table type. + * @param lyt Layout to compute the operational domain for. + * @param spec Expected truth table of the layout. + * @param samples Number of samples to perform. + * @param params Operational domain computation parameters. + * @param stats Operational domain computation statistics. + * @return The (partial) operational domain of the layout. + */ +template +operational_domain operational_domain_random_sampling(const Lyt& lyt, const TT& spec, const std::size_t samples, + const operational_domain_params& params = {}, + operational_domain_stats* stats = nullptr) +{ + 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"); + static_assert(kitty::is_truth_table::value, "TT is not a truth table"); + + operational_domain_stats st{}; + detail::operational_domain_impl p{lyt, spec, params, st}; + + const auto result = p.random_sampling(samples); + + if (stats) + { + *stats = st; + } + + return result; +} +/** + * Computes the operational domain of the given SiDB cell-level layout. 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 given truth table. The input BDL pairs of the layout are assumed to be in the same order as the + * inputs of the truth table. + * + * This algorithm first uses random sampling to find several operational points within the parameter range. From there, + * it employs the "flood fill" algorithm to explore the operational domain. The algorithm is guaranteed to find all + * operational areas in their entirety if the initial random sampling found at least one operational point within them. + * Thereby, this algorithm works for disconnected operational domains. + * + * It performs `samples` uniformly-distributed random samples within the parameter range. From there, it performs + * another number of samples equal to the number of points within the operational domain plus the first non-operational + * point in each direction. For each sample, the algorithm performs one operational check on the layout, where each + * operational check consists of up to \f$ 2^n \f$ exact ground state simulations, where \f$ n \f$ is the number of + * inputs of the layout. Each exact ground state simulation has exponential complexity in of itself. Therefore, the + * algorithm is only feasible for small layouts with few inputs. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Truth table type. + * @param lyt Layout to compute the operational domain for. + * @param spec Expected truth table of the layout. + * @param samples Number of samples to perform. + * @param params Operational domain computation parameters. + * @param stats Operational domain computation statistics. + * @return The (partial) operational domain of the layout. + */ +template +operational_domain operational_domain_flood_fill(const Lyt& lyt, const TT& spec, const std::size_t samples, + const operational_domain_params& params = {}, + operational_domain_stats* stats = nullptr) +{ + 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"); + static_assert(kitty::is_truth_table::value, "TT is not a truth table"); + + operational_domain_stats st{}; + detail::operational_domain_impl p{lyt, spec, params, st}; + + const auto result = p.flood_fill(samples); + + if (stats) + { + *stats = st; + } + + return result; +} +/** + * Computes the operational domain of the given SiDB cell-level layout. 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 given truth table. The input BDL pairs of the layout are assumed to be in the same order as the + * inputs of the truth table. + * + * This algorithm first uses random sampling to find a single operational point within the parameter range. From there, + * it traverses outwards to find the edge of the operational area and performs Moore neighborhood contour tracing to + * explore the contour of the operational domain. If the operational domain is connected, the algorithm is guaranteed to + * find the contours of the entire operational domain within the parameter range if the initial random sampling found an + * operational point. + * + * It performs up to `samples` uniformly-distributed random samples within the parameter range until an operational + * point is found. From there, it performs another number of samples equal to the distance to an edge of the operational + * area. Finally, it performs up to 8 samples for each contour point (however, the actual number is usually much lower). + * For each sample, the algorithm performs one operational check on the layout, where each operational check consists of + * up to \f$ 2^n \f$ exact ground state simulations, where \f$ n \f$ is the number of inputs of the layout. Each exact + * ground state simulation has exponential complexity in of itself. Therefore, the algorithm is only feasible for small + * layouts with few inputs. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Truth table type. + * @param lyt Layout to compute the operational domain for. + * @param spec Expected truth table of the layout. + * @param samples Number of samples to perform. + * @param params Operational domain computation parameters. + * @param stats Operational domain computation statistics. + * @return The (partial) operational domain of the layout. + */ +template +operational_domain operational_domain_contour_tracing(const Lyt& lyt, const TT& spec, const std::size_t samples, + const operational_domain_params& params = {}, + operational_domain_stats* stats = nullptr) +{ + 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"); + static_assert(kitty::is_truth_table::value, "TT is not a truth table"); + + operational_domain_stats st{}; + detail::operational_domain_impl p{lyt, spec, params, st}; + + const auto result = p.contour_tracing(samples); + + if (stats) + { + *stats = st; + } + + return result; +} + +} // namespace fiction + +namespace std +{ +// make `operational_domain::parameter_point` compatible with `std::integral_constant` +template <> +struct tuple_size : std::integral_constant +{}; +// make `operational_domain::parameter_point` compatible with `std::tuple_element` +template +struct tuple_element +{ + using type = double; +}; +// make `operational_domain::parameter_point` compatible with `std::hash` +template <> +struct hash +{ + size_t operator()(const fiction::operational_domain::parameter_point& p) const noexcept + { + size_t h = 0; + fiction::hash_combine(h, p.x, p.y); + + return h; + } +}; +} // namespace std + +#endif // FICTION_OPERATIONAL_DOMAIN_HPP diff --git a/include/fiction/algorithms/simulation/sidb/quickexact.hpp b/include/fiction/algorithms/simulation/sidb/quickexact.hpp index 5cd8c87a1..f712a90e1 100644 --- a/include/fiction/algorithms/simulation/sidb/quickexact.hpp +++ b/include/fiction/algorithms/simulation/sidb/quickexact.hpp @@ -7,11 +7,12 @@ #include "fiction/algorithms/iter/gray_code_iterator.hpp" #include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.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/traits.hpp" #include #include @@ -23,28 +24,28 @@ namespace fiction { - -/** - * Modes to use for the `QuickExact` algorithm. - */ -enum class automatic_base_number_detection -{ - /** - * Simulation is conducted with the required base number (i.e, if positively charged SiDBs can occur, three state - * simulation is conducted). - */ - ON, - /** - * The base number from the physical parameter is used for the simulation. - */ - OFF -}; /** * This struct stores the parameters for the `QuickExact` algorithm. */ template struct quickexact_params { + /** + * Modes to use for the `QuickExact` algorithm. + */ + enum class automatic_base_number_detection + { + /** + * Simulation is conducted with the required base number (i.e, if positively charged SiDBs can occur, three + * state simulation is conducted). + */ + ON, + /** + * The base number from the physical parameter is used for the simulation. + */ + OFF + }; + /** * All parameters for physical SiDB simulations. */ @@ -57,7 +58,7 @@ struct quickexact_params /** * Local external electrostatic potentials (e.g locally applied electrodes). */ - std::unordered_map local_external_potential = {}; + std::unordered_map, double> local_external_potential = {}; /** * Global external electrostatic potential. Value is applied on each cell in the layout. */ @@ -71,7 +72,7 @@ template class quickexact_impl { public: - quickexact_impl(Lyt& lyt, const quickexact_params& parameter) : + quickexact_impl(const Lyt& lyt, const quickexact_params& parameter) : layout{lyt}, charge_lyt{lyt, parameter.physical_parameters, sidb_charge_state::NEGATIVE}, params{parameter} @@ -90,9 +91,9 @@ class quickexact_impl // Determine if three state simulation (i.e., positively charged SiDBs can occur) is required. const bool three_state_simulation_required = - (params.base_number_detection == automatic_base_number_detection::ON && + (params.base_number_detection == quickexact_params::automatic_base_number_detection::ON && charge_lyt.is_three_state_simulation_required()) || - (params.base_number_detection == automatic_base_number_detection::OFF && + (params.base_number_detection == quickexact_params::automatic_base_number_detection::OFF && params.physical_parameters.base == 3); // If layout has at least two SiDBs, all SiDBs that have to be negatively charged are erased from the @@ -360,8 +361,9 @@ class quickexact_impl charge_layout.increase_charge_index_of_sub_layout_by_one( dependent_cell_mode::VARIABLE, energy_calculation::KEEP_OLD_ENERGY_VALUE, charge_distribution_history::CONSIDER, - exhaustive_algorithm::QUICKEXACT); // "false" allows that the charge state of the dependent cell is - // automatically changed based on the new charge distribution. + exhaustive_sidb_simulation_engine::QUICKEXACT); // "false" allows that the charge state of the + // dependent cell is automatically changed based on + // the new charge distribution. } if (charge_layout.is_physically_valid()) @@ -393,8 +395,9 @@ class quickexact_impl charge_layout.increase_charge_index_by_one( dependent_cell_mode::VARIABLE, energy_calculation::KEEP_OLD_ENERGY_VALUE, charge_distribution_history::CONSIDER, - exhaustive_algorithm::QUICKEXACT); // "false" allows that the charge state of the dependent cell is - // automatically changed based on the new charge distribution. + exhaustive_sidb_simulation_engine::QUICKEXACT); // "false" allows that the charge state of the + // dependent cell is automatically changed based on the + // new charge distribution. } // charge configurations of the sublayout are iterated @@ -424,7 +427,7 @@ class quickexact_impl charge_layout.increase_charge_index_of_sub_layout_by_one( dependent_cell_mode::VARIABLE, energy_calculation::KEEP_OLD_ENERGY_VALUE, - charge_distribution_history::CONSIDER, exhaustive_algorithm::QUICKEXACT); + charge_distribution_history::CONSIDER, exhaustive_sidb_simulation_engine::QUICKEXACT); } if (charge_layout.is_physically_valid()) @@ -556,7 +559,7 @@ class quickexact_impl * @return Simulation result. */ template -[[nodiscard]] sidb_simulation_result quickexact(Lyt& lyt, const quickexact_params& params = {}) noexcept +[[nodiscard]] sidb_simulation_result quickexact(const Lyt& lyt, const quickexact_params& params = {}) 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"); diff --git a/include/fiction/algorithms/simulation/sidb/quicksim.hpp b/include/fiction/algorithms/simulation/sidb/quicksim.hpp index 0ae9ace0c..05abb35cb 100644 --- a/include/fiction/algorithms/simulation/sidb/quicksim.hpp +++ b/include/fiction/algorithms/simulation/sidb/quicksim.hpp @@ -6,7 +6,6 @@ #define FICTION_QUICKSIM_HPP #include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp" #include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" diff --git a/include/fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp b/include/fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp index f7b36beb5..2ef64ec21 100644 --- a/include/fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp +++ b/include/fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp @@ -27,28 +27,29 @@ namespace fiction { -/** - * An enumeration of modes to use for the generation of random SiDB layouts to control control the appearance of - * positive charges. - */ -enum class positive_charges -{ - /** - * Positive charges can occur (i.e. SiDBs can be placed right next to each other). - */ - ALLOWED, - /** - * Positive charges are not allowed to occur (i.e. SiDBs need to be seperated by a few lattice points). - */ - FORBIDDEN -}; - /** * This struct stores the parameters for the `generate_random_sidb_layout` algorithm. */ template struct generate_random_sidb_layout_params { + + /** + * An enumeration of modes to use for the generation of random SiDB layouts to control control the appearance of + * positive charges. + */ + enum class positive_charges + { + /** + * Positive charges can occur (i.e. SiDBs can be placed right next to each other). + */ + ALLOWED, + /** + * Positive charges are not allowed to occur (i.e. SiDBs need to be seperated by a few lattice points). + */ + FORBIDDEN + }; + /** * Two coordinates that span the region where SiDBs may be placed (order is not important). The first coordinate is * the upper left corner and the second coordinate is the lower right corner of the area. @@ -122,7 +123,7 @@ Lyt generate_random_sidb_layout(const Lyt& lyt_skeleton, const generate_random_s bool constraint_violation_positive_sidbs = false; - if (params.positive_sidbs == positive_charges::FORBIDDEN) + if (params.positive_sidbs == generate_random_sidb_layout_params::positive_charges::FORBIDDEN) { // checks if the new coordinate is not closer than 2 cells (Euclidean distance) from an already // placed SiDB diff --git a/include/fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp b/include/fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp new file mode 100644 index 000000000..860899b95 --- /dev/null +++ b/include/fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp @@ -0,0 +1,49 @@ +// +// Created by marcel on 11.08.23. +// + +#ifndef FICTION_SIDB_SIMULATION_ENGINE_HPP +#define FICTION_SIDB_SIMULATION_ENGINE_HPP + +namespace fiction +{ + +/** + * Selector for the available SiDB simulation engines. + */ +enum class sidb_simulation_engine +{ + /** + * Exhaustive Ground State Search (EXGS) is an exact simulation engine that always has exponential runtime. + */ + EXGS, + /** + * QuickSim is a heuristic simulation engine that only requires polynomial runtime. + */ + QUICKSIM, + /** + * QuickExact is also an exact simulation engine that requires exponential runtime, but it scales a lot better than + * ExGS due to its effective search-space pruning. + */ + QUICKEXACT +}; + +/** + * Selector exclusively for exhaustive SiDB simulation engines. + */ +enum class exhaustive_sidb_simulation_engine +{ + /** + * Exhaustive Ground State Search (EXGS) is an exact simulation engine that always has exponential runtime. + */ + EXGS, + /** + * QuickExact is also an exact simulation engine that requires exponential runtime, but it scales a lot better than + * ExGS due to its effective search-space pruning. + */ + QUICKEXACT +}; + +} // namespace fiction + +#endif // FICTION_SIDB_SIMULATION_ENGINE_HPP diff --git a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp index 7552ce475..0306006d0 100644 --- a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp +++ b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp @@ -5,12 +5,12 @@ #ifndef FICTION_TIME_TO_SOLUTION_HPP #define FICTION_TIME_TO_SOLUTION_HPP -#include "fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp" #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" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" #include "fiction/traits.hpp" @@ -32,7 +32,7 @@ struct time_to_solution_params /** * Exhaustive simulation algorithm used to simulate the ground state as reference. */ - exhaustive_algorithm engine = exhaustive_algorithm::QUICKEXACT; + exhaustive_sidb_simulation_engine engine = exhaustive_sidb_simulation_engine::QUICKEXACT; /** * Number of iterations of the heuristic algorithm used to determine the simulation accuracy (`repetitions = 100` * means that accuracy is precise to 1%). @@ -103,7 +103,7 @@ void sim_acc_tts(Lyt& lyt, const quicksim_params& quicksim_params, const time_to time_to_solution_stats st{}; sidb_simulation_result simulation_result{}; - if (tts_params.engine == exhaustive_algorithm::EXGS) + if (tts_params.engine == exhaustive_sidb_simulation_engine::EXGS) { st.algorithm = "ExGS"; simulation_result = exhaustive_ground_state_simulation(lyt, quicksim_params.phys_params); diff --git a/include/fiction/io/csv_writer.hpp b/include/fiction/io/csv_writer.hpp index 5da8003ca..44bd70ae1 100644 --- a/include/fiction/io/csv_writer.hpp +++ b/include/fiction/io/csv_writer.hpp @@ -5,58 +5,86 @@ #ifndef FICTION_CSV_WRITER_HPP #define FICTION_CSV_WRITER_HPP -#include -#include +#include +#include namespace fiction { +/** + * Utility class for writing delimited (e.g. CSV) data into an output stream. It provides a variadic member function, + * `write_line`, that can take an arbitrary number of arguments and write them to the provided output stream in a line + * separated by a specified delimiter. + * + * The csv_writer follows some behavior principles: + * - Any standard data type can be written to the output stream. + * - Data arguments written will be separated by the specified delimiter. + * - A newline is written at the end of each line. + * - If `write_line` receives no arguments, it only writes a newline. + * - The last value written in a line is not followed by a delimiter. + * - No checks for escape characters are performed. + * + * Example usage: + * \code{.cpp} + * std::ofstream file("output.csv"); + * csv_writer writer(file); + * writer.write_line("Name", "Age", "City"); + * writer.write_line("Alice", 20, "New York"); + * \endcode + */ class csv_writer { public: /** - * Standard constructor. Opens a file. + * Standard constructor. * - * @param filename CSV file to write into. + * @param os Output stream to write CSV data into. */ - explicit csv_writer(const std::string_view& filename) : file{filename.data(), std::ios::out | std::ios::app} {} + explicit csv_writer(std::ostream& os) : stream{os} {} /** - * Writes a single line of comma-separated values to the stored file. Note that no escape checks are performed. + * Writes a single line of values to the output stream separated by a DELIMITER. No delimiter placed after the last + * value. Note that no escape checks are performed. Upon receiving no arguments, only a newline is written. This + * function uses template recursion to process the variadic parameters. * - * @tparam Ts Types of the variadic parameter pack. - * @param args Arguments to write to the file. + * @tparam T The type of the first argument. + * @tparam Ts Types of the rest of the variadic parameter pack. + * @param arg First argument to write to the stream. + * @param args Rest of the arguments to write to the stream if any exist. + */ + template + void write_line(T&& arg, Ts&&... args) + { + if constexpr (sizeof...(args) > 0) + { + stream << std::forward(arg) << DELIMITER; + write_line(std::forward(args)...); + } + else + { + stream << std::forward(arg) << std::endl; + } + } + /** + * Writes a newline to the output stream. This is the base case of the variadic template function, and is invoked + * when there are no additional arguments to process. + * + * @tparam Ts An empty variadic template argument pack. */ template - void write_line(Ts&&... args) + void write_line() { - if (file.is_open()) - (file << ... << add_delimiter(std::forward(args))) << std::endl; + stream << std::endl; } private: /** - * CSV file to write into. + * Output stream to write to. */ - std::ofstream file; + std::ostream& stream; /** * The delimiter to use. */ - static constexpr const char* DELIMITER = ", "; - /** - * Writes the given argument to the stored file and returns a delimiter. - * - * @tparam T Type of the given parameter. - * @param arg Argument to write to the file. - * @return Delimiter. - */ - template - const char* add_delimiter(T&& arg) - { - if (file.is_open()) - file << arg; - - return DELIMITER; - } + static constexpr char DELIMITER = ','; }; } // namespace fiction diff --git a/include/fiction/io/read_sqd_layout.hpp b/include/fiction/io/read_sqd_layout.hpp index 4f84aadcb..92f20ba8c 100644 --- a/include/fiction/io/read_sqd_layout.hpp +++ b/include/fiction/io/read_sqd_layout.hpp @@ -87,7 +87,7 @@ class read_sqd_layout_impl throw sqd_parsing_error("Error parsing SQD file: no attribute 'type' in element 'layer'"); } - if (layer_type == std::string{"DB"} && !has_siqad_coord_v) + if (std::string{layer_type} == "DB") { for (const auto* db_dot = layer->FirstChildElement("dbdot"); db_dot != nullptr; db_dot = db_dot->NextSiblingElement("dbdot")) @@ -96,16 +96,7 @@ class read_sqd_layout_impl } } - if (layer_type == std::string{"DB"} && has_siqad_coord_v) - { - for (const auto* db_dot = layer->FirstChildElement("dbdot"); db_dot != nullptr; - db_dot = db_dot->NextSiblingElement("dbdot")) - { - parse_db_dot_siqad(db_dot); - } - } - - else if (layer_type == std::string{"Defects"}) + else if (std::string{layer_type} == "Defects") { for (const auto* defect = layer->FirstChildElement("defect"); defect != nullptr; defect = defect->NextSiblingElement("defect")) @@ -122,8 +113,13 @@ class read_sqd_layout_impl } private: + /** + * The layout to which the parsed cells are added. + */ Lyt lyt; - + /** + * The input stream from which the SQD file is read. + */ std::istream& is; /** * The maximum position of a cell in the layout. @@ -182,21 +178,52 @@ class read_sqd_layout_impl throw sqd_parsing_error("Error parsing SQD file: no attribute 'n', 'm' or 'l' in element 'latcoord'"); } + // special case for SiQAD coordinates + if constexpr (has_siqad_coord_v) + { + return cell{std::stoll(n), std::stoll(m), std::stoll(l)}; + } + + // Cartesian coordinates return dimer_to_cell(std::stoll(n), std::stoll(m), std::stoll(l)); } - - cell parse_latcoord_siqad(const tinyxml2::XMLElement* latcoord) + /** + * Parses the attribute of a element from the SQD file and returns the corresponding cell type. + * + * @param db_dot The element. + * @return The cell type specified by the element. If non is specified, the cell type is assumed to be + * normal. + */ + sidb_technology::cell_type parse_dot_type(const tinyxml2::XMLElement* dot_type) { - const auto n = latcoord->Attribute("n"), m = latcoord->Attribute("m"), l = latcoord->Attribute("l"); + // if no dot type is given, assume normal dot + if (dot_type == nullptr) + { + return sidb_technology::cell_type::NORMAL; + } - if (n == nullptr || m == nullptr || l == nullptr) + const auto* const type = dot_type->GetText(); + + if (type == nullptr) { - throw sqd_parsing_error("Error parsing SQD file: no attribute 'n', 'm' or 'l' in element 'latcoord'"); + throw sqd_parsing_error("Error parsing SQD file: no text in element 'type'"); } - return cell(std::stoll(n), std::stoll(m), std::stoll(l)); - } + if (std::string{type} == "input") + { + return sidb_technology::cell_type::INPUT; + } + if (std::string{type} == "output") + { + return sidb_technology::cell_type::OUTPUT; + } + if (std::string{type} == "normal") + { + return sidb_technology::cell_type::NORMAL; + } + throw sqd_parsing_error("Error parsing SQD file: invalid dot type"); + } /** * Parses a element from the SQD file and adds the respective dot to the layout. * @@ -211,19 +238,9 @@ class read_sqd_layout_impl throw sqd_parsing_error("Error parsing SQD file: no element 'latcoord' in element 'dbdot'"); } - lyt.assign_cell_type(parse_latcoord(latcoord), sidb_technology::cell_type::NORMAL); - } + const auto* const dot_type = db_dot->FirstChildElement("type"); - void parse_db_dot_siqad(const tinyxml2::XMLElement* db_dot) - { - const auto* const latcoord = db_dot->FirstChildElement("latcoord"); - - if (latcoord == nullptr) - { - throw sqd_parsing_error("Error parsing SQD file: no element 'latcoord' in element 'dbdot'"); - } - - lyt.assign_cell_type(parse_latcoord_siqad(latcoord), sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type(parse_latcoord(latcoord), parse_dot_type(dot_type)); } /** * Parses a attribute of a element of a element from the SQD file and converts it @@ -278,14 +295,7 @@ class read_sqd_layout_impl for (const auto* latcoord = incl_coords->FirstChildElement("latcoord"); latcoord != nullptr; latcoord = latcoord->NextSiblingElement("latcoord")) { - if constexpr (has_siqad_coord_v) - { - incl_cells.push_back(parse_latcoord_siqad(latcoord)); - } - else - { - incl_cells.push_back(parse_latcoord(latcoord)); - } + incl_cells.push_back(parse_latcoord(latcoord)); } if (incl_cells.empty()) { diff --git a/include/fiction/io/write_operational_domain.hpp b/include/fiction/io/write_operational_domain.hpp new file mode 100644 index 000000000..86b362310 --- /dev/null +++ b/include/fiction/io/write_operational_domain.hpp @@ -0,0 +1,130 @@ +// +// Created by marcel on 02.08.23. +// + +#ifndef FICTION_WRITE_OPERATIONAL_DOMAIN_HPP +#define FICTION_WRITE_OPERATIONAL_DOMAIN_HPP + +#include "fiction/algorithms/simulation/sidb/operational_domain.hpp" +#include "fiction/io/csv_writer.hpp" + +#include +#include +#include + +namespace fiction +{ + +/** + * Parameters for writing an operational domain to a CSV file. + */ +struct write_operational_domain_params +{ + /** + * The tag used to represent the operational value of a parameter set. + */ + std::string_view operational_tag = "operational"; + /** + * The tag used to represent the non-operational value of a parameter set. + */ + std::string_view non_operational_tag = "non-operational"; +}; + +namespace detail +{ + +/** + * Converts a sweep parameter to a string representation. This is used to write the parameter name to the CSV file. + * + * @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 +{ + switch (param) + { + case operational_domain::sweep_parameter::EPSILON_R: + { + return "epsilon_r"; + } + case operational_domain::sweep_parameter::LAMBDA_TF: + { + return "lambda_tf"; + } + case operational_domain::sweep_parameter::MU_MINUS: + { + return "mu_minus"; + } + } + + return ""; +} + +} // namespace detail + +/** + * Writes a CSV representation of an operational domain to the specified output stream. The data are written + * as rows, each corresponding to one set of simulation parameters and their corresponding operational status. + * + * The output CSV format is as follows: + * X_DIMENSION, Y_DIMENSION, OPERATIONAL STATUS + * ... subsequent rows for each set of simulation parameters. + * + * The operational status is a binary value represented by specified tags in `params` indicating whether the simulation + * parameters are within the operational domain or not. + * + * @param opdom The operational domain to be written. It contains a mapping from sets of simulation parameters + * (represented as a pair of sweep parameters for the X and Y dimensions) to their operational status. + * @param os The output stream where the CSV representation of the operational domain is written to. + * @param params The parameters used for writing, including the operational and non-operational tags. Defaults to an + * empty `write_operational_domain_params` object, which provides standard tags. + */ +inline void write_operational_domain(const operational_domain& opdom, std::ostream& os, + const write_operational_domain_params& params = {}) +{ + csv_writer writer{os}; + + writer.write_line(detail::sweep_parameter_to_string(opdom.x_dimension), + detail::sweep_parameter_to_string(opdom.y_dimension), "operational status"); + + for (const auto& [sim_param, op_val] : opdom.operational_values) + { + writer.write_line(sim_param.x, sim_param.y, + op_val == operational_domain::operational_status::OPERATIONAL ? params.operational_tag : + params.non_operational_tag); + } +} +/** + * Writes a CSV representation of an operational domain to the specified file. The data are written as rows, each + * corresponding to one set of simulation parameters and their corresponding operational status. + * + * The output CSV format is as follows: + * X_DIMENSION, Y_DIMENSION, OPERATIONAL STATUS + * ... subsequent rows for each set of simulation parameters. + * + * The operational status is a binary value represented by specified tags in `params` indicating whether the simulation + * parameters are within the operational domain or not. + * + * @param opdom The operational domain to be written. It contains a mapping from sets of simulation parameters + * (represented as a pair of sweep parameters for the X and Y dimensions) to their operational status. + * @param filename The filename where the CSV representation of the operational domain is written to. + * @param params The parameters used for writing, including the operational and non-operational tags. Defaults to an + * empty `write_operational_domain_params` object, which provides standard tags. + */ +inline void write_operational_domain(const operational_domain& opdom, const std::string_view& filename, + const write_operational_domain_params& params = {}) +{ + std::ofstream os{filename.data(), std::ofstream::out}; + + if (!os.is_open()) + { + throw std::ofstream::failure("could not open file"); + } + + write_operational_domain(opdom, os, params); + os.close(); +} + +} // namespace fiction + +#endif // FICTION_WRITE_OPERATIONAL_DOMAIN_HPP diff --git a/include/fiction/io/write_sqd_layout.hpp b/include/fiction/io/write_sqd_layout.hpp index 39bb801c7..17c9a9317 100644 --- a/include/fiction/io/write_sqd_layout.hpp +++ b/include/fiction/io/write_sqd_layout.hpp @@ -112,10 +112,12 @@ inline constexpr const char* CLOSE_DEFECTS_LAYER = " \n"; inline constexpr const char* CLOSE_DESIGN = " \n"; inline constexpr const char* LATTICE_COORDINATE = R"()"; +inline constexpr const char* DOT_TYPE = R"({})"; inline constexpr const char* DBDOT_BLOCK = " \n" " 2\n" - " {}\n" + " {}\n" // lattice coordinates + " {}\n" // dot type " {}\n" " \n"; @@ -221,18 +223,26 @@ class write_sqd_layout_impl // generate SiDB cells if constexpr (has_sidb_technology_v) { + const auto type = this->lyt.get_cell_type(c); + const auto type_str = + type == sidb_technology::cell_type::NORMAL ? "" : + type == sidb_technology::cell_type::INPUT ? fmt::format(siqad::DOT_TYPE, "input") : + type == sidb_technology::cell_type::OUTPUT ? fmt::format(siqad::DOT_TYPE, "output") : + ""; + if constexpr (has_siqad_coord_v) { design << fmt::format(siqad::DBDOT_BLOCK, fmt::format(siqad::LATTICE_COORDINATE, c.x, c.y, c.z), - siqad::NORMAL_COLOR); + type_str, siqad::NORMAL_COLOR); } else { + const auto siqad_coord = fiction::siqad::to_siqad_coord(c); + design << fmt::format( siqad::DBDOT_BLOCK, - fmt::format(siqad::LATTICE_COORDINATE, fiction::siqad::to_siqad_coord(c).x, - fiction::siqad::to_siqad_coord(c).y, fiction::siqad::to_siqad_coord(c).z), - siqad::NORMAL_COLOR); + fmt::format(siqad::LATTICE_COORDINATE, siqad_coord.x, siqad_coord.y, siqad_coord.z), + type_str, siqad::NORMAL_COLOR); } } // generate QCA cell blocks @@ -249,21 +259,21 @@ class write_sqd_layout_impl { // top left design << fmt::format(siqad::DBDOT_BLOCK, - fmt::format(siqad::LATTICE_COORDINATE, c.x * 14, c.y * 7, 0), color); + fmt::format(siqad::LATTICE_COORDINATE, c.x * 14, c.y * 7, 0), "", color); // bottom right design << fmt::format(siqad::DBDOT_BLOCK, fmt::format(siqad::LATTICE_COORDINATE, (c.x * 14) + 6, (c.y * 7) + 3, 0), - color); + "", color); } if (!qca_technology::is_const_0_cell(type)) { // top right design << fmt::format(siqad::DBDOT_BLOCK, - fmt::format(siqad::LATTICE_COORDINATE, (c.x * 14) + 6, c.y * 7, 0), + fmt::format(siqad::LATTICE_COORDINATE, (c.x * 14) + 6, c.y * 7, 0), "", color); // bottom left design << fmt::format(siqad::DBDOT_BLOCK, - fmt::format(siqad::LATTICE_COORDINATE, c.x * 14, (c.y * 7) + 3, 0), + fmt::format(siqad::LATTICE_COORDINATE, c.x * 14, (c.y * 7) + 3, 0), "", color); } } @@ -285,7 +295,7 @@ class write_sqd_layout_impl { const auto& defect = cd.second; - // layout is not based on siqad coordinates, coordinate transformation is performed + // layout is not based on SiQAD coordinates, coordinate transformation is performed if constexpr (has_siqad_coord_v) { const auto& cell = cd.first; diff --git a/include/fiction/layouts/cell_level_layout.hpp b/include/fiction/layouts/cell_level_layout.hpp index 2b9ded670..e5be8d81c 100644 --- a/include/fiction/layouts/cell_level_layout.hpp +++ b/include/fiction/layouts/cell_level_layout.hpp @@ -119,6 +119,18 @@ class cell_level_layout : public ClockedLayout explicit cell_level_layout(std::shared_ptr> s) : strg{std::move(s)} {} + /** + * Clones the layout returning a deep copy. + * + * @return Deep copy of the layout. + */ + cell_level_layout clone() const + { + auto cl = cell_level_layout{strg}; + cl.strg = std::make_shared>(*strg); + return cl; + } + #pragma endregion #pragma region Cell types diff --git a/include/fiction/technology/cell_technologies.hpp b/include/fiction/technology/cell_technologies.hpp index 7557ec2b8..5ad5efc36 100644 --- a/include/fiction/technology/cell_technologies.hpp +++ b/include/fiction/technology/cell_technologies.hpp @@ -357,7 +357,7 @@ struct sidb_technology static constexpr double CELL_HSPACE = 0.384; /** * Default average vertical spacing between two SiDBs in SiQAD. - * Depending on their lattice, they can be closer together or further apart. + * Depending on whether they are on the same or different dimer rows, SiDBs can be closer together or further apart. */ static constexpr double CELL_VSPACE = 0.384; }; diff --git a/include/fiction/technology/charge_distribution_surface.hpp b/include/fiction/technology/charge_distribution_surface.hpp index 6dc753f71..a6a3a2c97 100644 --- a/include/fiction/technology/charge_distribution_surface.hpp +++ b/include/fiction/technology/charge_distribution_surface.hpp @@ -6,7 +6,7 @@ #define FICTION_CHARGE_DISTRIBUTION_SURFACE_HPP #include "fiction/algorithms/path_finding/distance.hpp" -#include "fiction/algorithms/simulation/sidb/enum_class_exhaustive_algorithm.hpp" +#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/physical_constants.hpp" @@ -244,7 +244,7 @@ class charge_distribution_surface : public Lyt * @param cs The charge state used for the initialization of all SiDBs, default is a negative charge. */ explicit charge_distribution_surface(const sidb_simulation_parameters& params = sidb_simulation_parameters{}, - const sidb_charge_state& cs = sidb_charge_state::NEGATIVE) : + const sidb_charge_state cs = sidb_charge_state::NEGATIVE) : Lyt(), strg{std::make_shared(params)} { @@ -265,7 +265,7 @@ class charge_distribution_surface : public Lyt */ explicit charge_distribution_surface( const Lyt& lyt, const sidb_simulation_parameters& params = sidb_simulation_parameters{}, - const sidb_charge_state& cs = sidb_charge_state::NEGATIVE, const typename Lyt::cell& variable_cells = {}, + const sidb_charge_state cs = sidb_charge_state::NEGATIVE, const typename Lyt::cell& variable_cells = {}, const std::unordered_map& external_potential = {}) : Lyt(lyt), strg{std::make_shared(params, external_potential, variable_cells)} @@ -362,10 +362,10 @@ class charge_distribution_surface : public Lyt * * @param cs Charge state. */ - [[nodiscard]] bool charge_exists(const sidb_charge_state& cs) const noexcept + [[nodiscard]] bool charge_exists(const sidb_charge_state cs) const noexcept { return std::any_of(strg->cell_charge.cbegin(), strg->cell_charge.cend(), - [&cs](const sidb_charge_state& c) { return c == cs; }); + [&cs](const sidb_charge_state c) { return c == cs; }); } /** * This function searches the index of an SiDB. @@ -390,7 +390,7 @@ class charge_distribution_surface : public Lyt * @param cs The charge state to be assigned to the cell. * @param update_charge_index `true` if the charge index should be changed, `false` otherwise. */ - void assign_charge_state(const typename Lyt::cell& c, const sidb_charge_state& cs, + void assign_charge_state(const typename Lyt::cell& c, const sidb_charge_state cs, const bool update_charge_index = true) const noexcept { if (auto index = cell_to_index(c); index != -1) @@ -409,7 +409,7 @@ class charge_distribution_surface : public Lyt * @param i The index of the cell. * @param cs The charge state to be assign to the cell. */ - void assign_charge_by_cell_index(const uint64_t i, const sidb_charge_state& cs) const noexcept + void assign_charge_by_cell_index(const uint64_t i, const sidb_charge_state cs) const noexcept { strg->cell_charge[i] = cs; this->charge_distribution_to_index(); @@ -419,7 +419,7 @@ class charge_distribution_surface : public Lyt * * @param cs The charge state to be assigned to all the SiDBs. */ - void assign_all_charge_states(const sidb_charge_state& cs) noexcept + void assign_all_charge_states(const sidb_charge_state cs) noexcept { for (uint64_t i = 0u; i < strg->cell_charge.size(); ++i) { @@ -533,7 +533,7 @@ class charge_distribution_surface : public Lyt * @param update_charge_configuration if set to `true`, the charge distribution index is updated after the charge * distribution is changed. */ - void assign_charge_state_by_cell_index(const uint64_t index, const sidb_charge_state& cs, + void assign_charge_state_by_cell_index(const uint64_t index, const sidb_charge_state cs, const bool update_charge_configuration = true) noexcept { strg->cell_charge[index] = cs; @@ -803,7 +803,7 @@ class charge_distribution_surface : public Lyt * results of the previous charge distribution. */ void update_local_potential( - const charge_distribution_history& history_mode = charge_distribution_history::NEGLECT) noexcept + const charge_distribution_history history_mode = charge_distribution_history::NEGLECT) noexcept { if (history_mode == charge_distribution_history::NEGLECT) { @@ -951,9 +951,9 @@ class charge_distribution_surface : public Lyt * otherwise. */ void update_after_charge_change( - const dependent_cell_mode& dependent_cell = dependent_cell_mode::FIXED, - const energy_calculation& energy_calculation_mode = energy_calculation::UPDATE_ENERGY, - const charge_distribution_history& history_mode = charge_distribution_history::NEGLECT) noexcept + const dependent_cell_mode dependent_cell = dependent_cell_mode::FIXED, + const energy_calculation energy_calculation_mode = energy_calculation::UPDATE_ENERGY, + const charge_distribution_history history_mode = charge_distribution_history::NEGLECT) noexcept { this->update_local_potential(history_mode); if (dependent_cell == dependent_cell_mode::VARIABLE) @@ -1220,14 +1220,14 @@ class charge_distribution_surface : public Lyt * @param history_mode charge_distribution_history::NEGLECT if the information (local electrostatic energy) of the * previous charge distribution is used to make the update more efficient, charge_distribution_history::CONSIDER * otherwise. - * @param engine exhaustive_algorithm::EXGS if `ExGS``should be used, exhaustive_algorithm::QUICKEXACT for - * `QuickExact`. + * @param engine exhaustive_sidb_simulation_engine::EXGS if `ExGS``should be used, + * exhaustive_sidb_simulation_engine::QUICKEXACT for `QuickExact`. */ void increase_charge_index_by_one( - const dependent_cell_mode& dependent_cell_fixed = dependent_cell_mode::FIXED, - const energy_calculation& recompute_system_energy = energy_calculation::UPDATE_ENERGY, - const charge_distribution_history& consider_history = charge_distribution_history::NEGLECT, - const exhaustive_algorithm& engine = exhaustive_algorithm::EXGS) noexcept + const dependent_cell_mode dependent_cell_fixed = dependent_cell_mode::FIXED, + const energy_calculation recompute_system_energy = energy_calculation::UPDATE_ENERGY, + const charge_distribution_history consider_history = charge_distribution_history::NEGLECT, + const exhaustive_sidb_simulation_engine engine = exhaustive_sidb_simulation_engine::EXGS) noexcept { if (strg->charge_index_and_base.first < strg->max_charge_index) { @@ -1360,7 +1360,7 @@ class charge_distribution_surface : public Lyt // check if all SiDBs are negatively charged this->foreach_cell( - [this](const auto& c) { + [this]([[maybe_unused]] const auto& c) { assert(this->get_charge_state(c) == sidb_charge_state::NEGATIVE && "All SiDBs have to be negatively charged"); }); @@ -1661,7 +1661,8 @@ class charge_distribution_surface : public Lyt * @param new_gray_code Gray code as uint64_t of the new charge distribution. * @param old_gray_code Gray code as uint64_t of the previous charge distribution layout. */ - void charge_index_gray_code_to_charge_distribution(uint64_t new_gray_code, uint64_t old_gray_code) noexcept + void charge_index_gray_code_to_charge_distribution(const uint64_t new_gray_code, + const uint64_t old_gray_code) noexcept { strg->cell_history_gray_code = {}; @@ -1681,8 +1682,8 @@ class charge_distribution_surface : public Lyt index_changed++; } - const auto sign_old = int8_t{-1} * static_cast(r_old[index_changed]); - const auto sign_new = int8_t{-1} * static_cast(r_new[index_changed]); + const auto sign_old = static_cast(-1 * static_cast(r_old[index_changed])); + const auto sign_new = static_cast(-1 * static_cast(r_new[index_changed])); if (index_changed < strg->dependent_cell_index) { @@ -1713,14 +1714,14 @@ class charge_distribution_surface : public Lyt * @param history_mode charge_distribution_history::NEGLECT if the information (local electrostatic energy) of the * previous charge distribution is used to make the update more efficient, charge_distribution_history::CONSIDER * otherwise. - * @param engine exhaustive_algorithm::EXGS if `ExGS``should be used, exhaustive_algorithm::QUICKEXACT for - * `QuickExact`. + * @param engine exhaustive_sidb_simulation_engine::EXGS if `ExGS``should be used, + * exhaustive_sidb_simulation_engine::QUICKEXACT for `QuickExact`. */ void increase_charge_index_of_sub_layout_by_one( - const dependent_cell_mode& dependent_cell_fixed = dependent_cell_mode::FIXED, - const energy_calculation& recompute_system_energy = energy_calculation::UPDATE_ENERGY, - const charge_distribution_history& consider_history = charge_distribution_history::NEGLECT, - const exhaustive_algorithm& engine = exhaustive_algorithm::QUICKEXACT) noexcept + const dependent_cell_mode dependent_cell_fixed = dependent_cell_mode::FIXED, + const energy_calculation recompute_system_energy = energy_calculation::UPDATE_ENERGY, + const charge_distribution_history consider_history = charge_distribution_history::NEGLECT, + const exhaustive_sidb_simulation_engine engine = exhaustive_sidb_simulation_engine::QUICKEXACT) noexcept { if (strg->charge_index_sublayout < strg->max_charge_index_sulayout) { @@ -1744,9 +1745,9 @@ class charge_distribution_surface : public Lyt */ void assign_charge_index_by_gray_code( const uint64_t current_gray_code, const uint64_t previous_gray_code, - const dependent_cell_mode& dependent_cell = dependent_cell_mode::FIXED, - const energy_calculation& energy_calc_mode = energy_calculation::UPDATE_ENERGY, - const charge_distribution_history& history_mode = charge_distribution_history::NEGLECT) noexcept + const dependent_cell_mode dependent_cell = dependent_cell_mode::FIXED, + const energy_calculation energy_calc_mode = energy_calculation::UPDATE_ENERGY, + const charge_distribution_history history_mode = charge_distribution_history::NEGLECT) noexcept { if (current_gray_code <= strg->max_charge_index) { @@ -1760,7 +1761,7 @@ class charge_distribution_surface : public Lyt void reset_charge_index_sub_layout() noexcept { strg->charge_index_sublayout = 0; - this->index_to_charge_distribution(exhaustive_algorithm::QUICKEXACT); + this->index_to_charge_distribution(exhaustive_sidb_simulation_engine::QUICKEXACT); this->update_after_charge_change(dependent_cell_mode::VARIABLE, energy_calculation::KEEP_OLD_ENERGY_VALUE, charge_distribution_history::CONSIDER); } @@ -1801,7 +1802,7 @@ class charge_distribution_surface : public Lyt * @param cell Cell which is added to the layout. * @param charge Charge state of the added cell. */ - void add_sidb(const typename Lyt::cell& cell, const sidb_charge_state& charge) noexcept + void add_sidb(const typename Lyt::cell& cell, const sidb_charge_state charge) noexcept { strg->cell_charge.push_back(charge); strg->sidb_order.push_back(cell); @@ -1815,7 +1816,7 @@ class charge_distribution_surface : public Lyt * * @param cs The charge state assigned to all SiDBs. */ - void initialize(const sidb_charge_state& cs = sidb_charge_state::NEGATIVE) noexcept + void initialize(const sidb_charge_state cs = sidb_charge_state::NEGATIVE) noexcept { strg->sidb_order.reserve(this->num_cells()); strg->cell_charge.reserve(this->num_cells()); @@ -1924,13 +1925,14 @@ class charge_distribution_surface : public Lyt /** * The stored unique index is converted to a charge distribution. * - * @param engine exhaustive_algorithm::EXGS if `ExGS``should be used, exhaustive_algorithm::QUICKEXACT for - * `QuickExact`. + * @param engine exhaustive_sidb_simulation_engine::EXGS if `ExGS``should be used, + * exhaustive_sidb_simulation_engine::QUICKEXACT for `QuickExact`. */ - void index_to_charge_distribution(const exhaustive_algorithm& engine = exhaustive_algorithm::EXGS) noexcept + void index_to_charge_distribution( + const exhaustive_sidb_simulation_engine engine = exhaustive_sidb_simulation_engine::EXGS) noexcept { // This scope is executed if the function is used by `quickexact`. - if (engine == exhaustive_algorithm::QUICKEXACT) + if (engine == exhaustive_sidb_simulation_engine::QUICKEXACT) { // Cell_history collects the cells (SiDBs) that have changed their charge state. strg->cell_history = {}; @@ -2130,15 +2132,15 @@ template charge_distribution_surface(const T&, const sidb_simulation_parameters&) -> charge_distribution_surface; template -charge_distribution_surface(const T&, const sidb_simulation_parameters&, const sidb_charge_state& cs) +charge_distribution_surface(const T&, const sidb_simulation_parameters&, sidb_charge_state cs) -> charge_distribution_surface; template -charge_distribution_surface(const T&, const sidb_simulation_parameters&, const sidb_charge_state& cs, +charge_distribution_surface(const T&, const sidb_simulation_parameters&, sidb_charge_state cs, const typename T::cell& variable_cells) -> charge_distribution_surface; template -charge_distribution_surface(const T&, const sidb_simulation_parameters&, const sidb_charge_state& cs, +charge_distribution_surface(const T&, const sidb_simulation_parameters&, sidb_charge_state cs, const typename T::cell& variable_cells, const std::unordered_map& external_pot) -> charge_distribution_surface; diff --git a/include/fiction/technology/sidb_nm_position.hpp b/include/fiction/technology/sidb_nm_position.hpp index 4792ca14e..076197358 100644 --- a/include/fiction/technology/sidb_nm_position.hpp +++ b/include/fiction/technology/sidb_nm_position.hpp @@ -15,16 +15,19 @@ namespace fiction { /** - * Computes the position of a cell in nanometers from the layout origin (unit: nm). + * Computes the position of a cell in nanometers from the layout origin in an SiDB layout (unit: nm). * - * @tparam Lyt The layout type. + * @tparam Lyt SiDB cell-level layout type. * @param sp The simulation parameters (required for the lattice constants). * @param c The cell to compute the position for. * @return A pair representing the `(x,y)` position of `c` in nanometers from the layout origin. */ template -constexpr std::pair sidb_nm_position(const sidb_simulation_parameters& sp, const cell& c) noexcept +[[nodiscard]] constexpr std::pair sidb_nm_position(const sidb_simulation_parameters& sp, + const cell& c) 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(has_siqad_coord_v, "Lyt is not based on SiQAD coordinates"); const auto x = (c.x * sp.lat_a) * 0.1; diff --git a/include/fiction/utils/execution_utils.hpp b/include/fiction/utils/execution_utils.hpp index 9550fada7..873502b67 100644 --- a/include/fiction/utils/execution_utils.hpp +++ b/include/fiction/utils/execution_utils.hpp @@ -15,21 +15,21 @@ /** * Sequential execution policy for STL algorithms. * - * @note This macro automatically detetcs whether the C++ library supports execution policies and whether the compiler + * @note This macro automatically detects whether the C++ library supports execution policies and whether the compiler * is able to compile them. If not, the macro defaults to nothing. */ #define FICTION_EXECUTION_POLICY_SEQ std::execution::seq, /** * Parallel execution policy for STL algorithms. * - * @note This macro automatically detetcs whether the C++ library supports execution policies and whether the compiler + * @note This macro automatically detects whether the C++ library supports execution policies and whether the compiler * is able to compile them. If not, the macro defaults to nothing. */ #define FICTION_EXECUTION_POLICY_PAR std::execution::par, /** * Parallel unsequenced execution policy for STL algorithms. * - * @note This macro automatically detetcs whether the C++ library supports execution policies and whether the compiler + * @note This macro automatically detects whether the C++ library supports execution policies and whether the compiler * is able to compile them. If not, the macro defaults to nothing. */ #define FICTION_EXECUTION_POLICY_PAR_UNSEQ std::execution::par_unseq, diff --git a/include/fiction/utils/phmap_utils.hpp b/include/fiction/utils/phmap_utils.hpp new file mode 100644 index 000000000..2c16a908e --- /dev/null +++ b/include/fiction/utils/phmap_utils.hpp @@ -0,0 +1,29 @@ +// +// Created by marcel on 08.08.23. +// + +#ifndef FICTION_PHMAP_UTILS_HPP +#define FICTION_PHMAP_UTILS_HPP + +#include + +#include +#include +#include + +namespace fiction +{ + +/** + * A parallel flat hash map with built-in mutexes. This enables thread-safe access to the map without the need to + * manually lock and unlock the map. Since the map uses internal buckets that can be accessed in parallel, this + * implementation is a lot faster than a regular hash map with a single mutex. + */ +template +using locked_parallel_flat_hash_map = + phmap::parallel_flat_hash_map, phmap::priv::hash_default_eq, + std::allocator>, 4, std::mutex>; + +} // namespace fiction + +#endif // FICTION_PHMAP_UTILS_HPP diff --git a/test/algorithms/iter/aspect_ratio_iterator.cpp b/test/algorithms/iter/aspect_ratio_iterator.cpp index 2e3f85003..fb2140c96 100644 --- a/test/algorithms/iter/aspect_ratio_iterator.cpp +++ b/test/algorithms/iter/aspect_ratio_iterator.cpp @@ -7,8 +7,19 @@ #include #include +#include +#include + using namespace fiction; +TEST_CASE("Traits", "[bdl-input-iterator]") +{ + CHECK(std::is_same_v>::iterator_category, + std::forward_iterator_tag>); + + CHECK(std::is_same_v>::value_type, offset::ucoord_t>); +} + TEST_CASE("Aspect ratio iteration", "[aspect-ratio-iterator]") { aspect_ratio_iterator ari{1}; diff --git a/test/algorithms/iter/bdl_input_iterator.cpp b/test/algorithms/iter/bdl_input_iterator.cpp new file mode 100644 index 000000000..7aabba41a --- /dev/null +++ b/test/algorithms/iter/bdl_input_iterator.cpp @@ -0,0 +1,289 @@ +// +// Created by marcel on 24.07.23. +// + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace fiction; + +TEST_CASE("Traits", "[bdl-input-iterator]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + CHECK(std::is_same_v>::iterator_category, + std::random_access_iterator_tag>); + + CHECK(std::is_same_v>::value_type, layout>); + + CHECK(std::is_same_v>::difference_type, int64_t>); +} + +TEST_CASE("Operators", "[bdl-input-iterators]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + const layout lyt{}; + + bdl_input_iterator bii{lyt}; + + CHECK(bii == 0ull); + CHECK(bii != 1ull); + + CHECK(bii < 1ull); + CHECK(bii <= 1ull); + + CHECK(bii >= 0ull); + + // increment + ++bii; + + CHECK(bii == 1ull); + CHECK(bii != 0ull); + + CHECK(bii < 2ull); + CHECK(bii <= 2ull); + + CHECK(bii > 0ull); + CHECK(bii >= 0ull); + + // decrement + --bii; + + CHECK(bii == 0ull); + CHECK(bii != 1ull); + + CHECK(bii < 1ull); + CHECK(bii <= 1ull); + + CHECK(bii >= 0ull); + + // increment assignment + bii += 2ull; + + CHECK(bii == 2ull); + CHECK(bii != 1ull); + + CHECK(bii < 3ull); + CHECK(bii <= 3ull); + + CHECK(bii > 1ull); + CHECK(bii >= 1ull); + + const auto bii_cp = bii; + + // decrement assignment + bii -= 2ull; + + CHECK(bii == 0ull); + CHECK(bii != 1ull); + + CHECK(bii < 1ull); + CHECK(bii <= 1ull); + + CHECK(bii >= 0ull); + + // difference + CHECK(bii_cp - bii == 2); + + // subscript + CHECK(bii[0] == 0ull); + CHECK(bii[1] == 1ull); + CHECK(bii[2] == 2ull); + CHECK(bii[3] == 3ull); + CHECK(bii[4] == 4ull); +} + +TEST_CASE("Empty layout iteration", "[bdl-input-iterator]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + const layout lyt{}; + + bdl_input_iterator bii{lyt}; + + CHECK((*bii).num_cells() == 0); + + // increment + + ++bii; + + CHECK((*bii).num_cells() == 0); + + auto bii_cp = bii++; + + CHECK((*bii).num_cells() == 0); + CHECK((*bii_cp).num_cells() == 0); + + // decrement + + --bii; + + CHECK((*bii).num_cells() == 0); + + auto bii_cm = bii--; + + CHECK((*bii).num_cells() == 0); + + CHECK((*bii_cm).num_cells() == 0); +} + +TEST_CASE("BDL wire iteration", "[bdl-input-iterator]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{20, 0}, "BDL wire"}; + + lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 0, 0}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({8, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({12, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({14, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({18, 0, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({20, 0, 0}, sidb_technology::cell_type::OUTPUT); + + bdl_input_iterator bii{lyt}; + CHECK(bii == 0ull); + + // start by incrementing over all input states + + // layout at input state 0 + const auto& lyt_0 = *bii; + + // the iterator should have toggled the second input cell + CHECK(lyt_0.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_0.get_cell_type({2, 0, 0}) == sidb_technology::cell_type::EMPTY); + + ++bii; + CHECK(bii == 1ull); + + // layout at input state 1 + const auto& lyt_1 = *bii; + + // the iterator should have toggled the first input cell + CHECK(lyt_1.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_1.get_cell_type({2, 0, 0}) == sidb_technology::cell_type::INPUT); + + // doing another iteration should overflow and set it back to 0 + ++bii; + CHECK(bii == 2ull); + + const auto& lyt_2 = *bii; + + CHECK(lyt_2.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_2.get_cell_type({2, 0, 0}) == sidb_technology::cell_type::EMPTY); + + // finally, decrement back to the initial state, doing another wrap-around + + --bii; + CHECK(bii == 1ull); + + const auto& lyt_1_1 = *bii; + + CHECK(lyt_1_1.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_1_1.get_cell_type({2, 0, 0}) == sidb_technology::cell_type::INPUT); + + --bii; + CHECK(bii == 0ull); + + const auto& lyt_0_1 = *bii; + + CHECK(lyt_0_1.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_0_1.get_cell_type({2, 0, 0}) == sidb_technology::cell_type::EMPTY); +} + +TEST_CASE("SiQAD's AND gate iteration", "[bdl-input-iterator]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{20, 10}, "AND gate"}; + + lyt.assign_cell_type({0, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({20, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({18, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({4, 2, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({6, 3, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({14, 3, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({16, 2, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({10, 6, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({10, 7, 0}, sidb_technology::cell_type::OUTPUT); + + lyt.assign_cell_type({10, 9, 1}, sidb_technology::cell_type::NORMAL); + + bdl_input_iterator bii{lyt}; + + for (auto i = 0; bii < 4; ++bii, ++i) + { + switch (i) + { + case 0: + { + const auto& lyt_0 = *bii; + + CHECK(lyt_0.get_cell_type({0, 0, 1}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_0.get_cell_type({2, 1, 1}) == sidb_technology::cell_type::EMPTY); + + CHECK(lyt_0.get_cell_type({20, 0, 1}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_0.get_cell_type({18, 1, 1}) == sidb_technology::cell_type::EMPTY); + + break; + } + case 1: + { + const auto& lyt_1 = *bii; + + CHECK(lyt_1.get_cell_type({0, 0, 1}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_1.get_cell_type({2, 1, 1}) == sidb_technology::cell_type::EMPTY); + + CHECK(lyt_1.get_cell_type({20, 0, 1}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_1.get_cell_type({18, 1, 1}) == sidb_technology::cell_type::INPUT); + + break; + } + case 2: + { + const auto& lyt_2 = *bii; + + CHECK(lyt_2.get_cell_type({0, 0, 1}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_2.get_cell_type({2, 1, 1}) == sidb_technology::cell_type::INPUT); + + CHECK(lyt_2.get_cell_type({20, 0, 1}) == sidb_technology::cell_type::INPUT); + CHECK(lyt_2.get_cell_type({18, 1, 1}) == sidb_technology::cell_type::EMPTY); + + break; + } + case 3: + { + const auto& lyt_3 = *bii; + + CHECK(lyt_3.get_cell_type({0, 0, 1}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_3.get_cell_type({2, 1, 1}) == sidb_technology::cell_type::INPUT); + + CHECK(lyt_3.get_cell_type({20, 0, 1}) == sidb_technology::cell_type::EMPTY); + CHECK(lyt_3.get_cell_type({18, 1, 1}) == sidb_technology::cell_type::INPUT); + + break; + } + default: + { + CHECK(false); + } + } + } +} diff --git a/test/algorithms/simulation/sidb/critical_temperature.cpp b/test/algorithms/simulation/sidb/critical_temperature.cpp index ecbb6457c..9f1ae39ea 100644 --- a/test/algorithms/simulation/sidb/critical_temperature.cpp +++ b/test/algorithms/simulation/sidb/critical_temperature.cpp @@ -29,9 +29,10 @@ TEMPLATE_TEST_CASE( TestType lyt{{10, 10}}; critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.32}}, 0.99, 350}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.32}}, 0.99, 350}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.num_valid_lyt == 0); CHECK(criticalstats.critical_temperature == 0); @@ -44,26 +45,28 @@ TEMPLATE_TEST_CASE( // gate-based simulation critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.32}}, - 0.99, - 350, - create_or_tt(), - 2}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.32}}, + 0.99, + 350, + create_or_tt(), + 2}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.num_valid_lyt == 1); CHECK(criticalstats.critical_temperature == 350); // non-gate-based simulation critical_temperature_stats criticalstats_non_gate_based{}; - const critical_temperature_params params_non_gate_based{simulation_engine::EXACT, - critical_temperature_mode::NON_GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.32}}, - 0.99, - 350, - create_or_tt(), - 2}; + const critical_temperature_params params_non_gate_based{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::NON_GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.32}}, + 0.99, + 350, + create_or_tt(), + 2}; critical_temperature(lyt, params, &criticalstats_non_gate_based); CHECK(criticalstats_non_gate_based.num_valid_lyt == 1); CHECK(criticalstats_non_gate_based.critical_temperature == 350); @@ -81,24 +84,26 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({20, 3, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.23}}, - 0.99, - 350, - create_or_tt(), - 2}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.23}}, + 0.99, + 350, + create_or_tt(), + 2}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.critical_temperature == 350); critical_temperature_stats criticalstats_one{}; - const critical_temperature_params params_one{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.23}}, - 0.99, - 350, - create_and_tt(), - 3}; + const critical_temperature_params params_one{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.23}}, + 0.99, + 350, + create_and_tt(), + 3}; critical_temperature(lyt, params_one, &criticalstats_one); CHECK(criticalstats_one.critical_temperature == 350); @@ -107,13 +112,14 @@ TEMPLATE_TEST_CASE( critical_temperature(lyt, params_one, &criticalstats_second); CHECK(criticalstats_second.critical_temperature == 350); - const critical_temperature_params params_two{simulation_engine::EXACT, - critical_temperature_mode::NON_GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.23}}, - 0.999, - 450, - create_and_tt(), - 3}; + const critical_temperature_params params_two{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::NON_GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.23}}, + 0.999, + 450, + create_and_tt(), + 3}; critical_temperature_stats criticalstats_no_logic{}; critical_temperature(lyt, params_two, &criticalstats_no_logic); CHECK(criticalstats_no_logic.critical_temperature < 40); @@ -140,13 +146,14 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({37, 3, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.28}}, - 0.99, - 350, - create_xnor_tt(), - 3}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.28}}, + 0.99, + 350, + create_xnor_tt(), + 3}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.critical_temperature < 13); } @@ -172,13 +179,14 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({37, 3, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.15}}, - 0.99, - 350, - create_xnor_tt(), - 3}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.15}}, + 0.99, + 350, + create_xnor_tt(), + 3}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.critical_temperature == 0); } @@ -204,9 +212,10 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({37, 3, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::EXACT, - critical_temperature_mode::NON_GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.15}}, 0.99, 350}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::EXACT, + critical_temperature_params::critical_temperature_mode::NON_GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.15}}, 0.99, 350}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.algorithm_name == "QuickExact"); CHECK(criticalstats.critical_temperature < 200); @@ -234,9 +243,10 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({37, 3, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::APPROXIMATE, - critical_temperature_mode::NON_GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.15}}, 0.99, 350}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::APPROXIMATE, + critical_temperature_params::critical_temperature_mode::NON_GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.15}}, 0.99, 350}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.algorithm_name == "QuickSim"); CHECK(criticalstats.critical_temperature < 200); @@ -260,13 +270,14 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({3, 8, 0}, TestType::cell_type::NORMAL); critical_temperature_stats criticalstats{}; - const critical_temperature_params params{simulation_engine::APPROXIMATE, - critical_temperature_mode::GATE_BASED_SIMULATION, - quicksim_params{sidb_simulation_parameters{2, -0.35}}, - 0.99, - 350, - create_fan_out_tt(), - 1}; + const critical_temperature_params params{ + critical_temperature_params::simulation_engine::APPROXIMATE, + critical_temperature_params::critical_temperature_mode::GATE_BASED_SIMULATION, + quicksim_params{sidb_simulation_parameters{2, -0.35}}, + 0.99, + 350, + create_fan_out_tt(), + 1}; critical_temperature(lyt, params, &criticalstats); CHECK(criticalstats.algorithm_name == "QuickSim"); CHECK(criticalstats.critical_temperature < 200); diff --git a/test/algorithms/simulation/sidb/detect_bdl_pairs.cpp b/test/algorithms/simulation/sidb/detect_bdl_pairs.cpp new file mode 100644 index 000000000..2d66f75ec --- /dev/null +++ b/test/algorithms/simulation/sidb/detect_bdl_pairs.cpp @@ -0,0 +1,301 @@ +// +// Created by marcel on 21.07.23. +// + +#include + +#include +#include + +using namespace fiction; + +TEST_CASE("Empty layout BDL detection", "[detect-bdl-pairs]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + const layout lyt{}; + + const auto result = detect_bdl_pairs(lyt, sidb_technology::cell_type::NORMAL); + + CHECK(result.empty()); +} + +TEST_CASE("Atomic wire BDL detection", "[detect-bdl-pairs]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{7, 0}, "Atomic wire"}; + + lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({1, 0, 0}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({2, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({3, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({4, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({5, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({7, 0, 0}, sidb_technology::cell_type::OUTPUT); + + detect_bdl_pairs_params params{}; + + SECTION("default lower threshold") + { + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + const auto normal_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::NORMAL, params); + + REQUIRE(input_bdl_pairs.empty()); + REQUIRE(output_bdl_pairs.empty()); + REQUIRE(normal_bdl_pairs.size() == 2); + + const auto& normal_pair1 = normal_bdl_pairs[0]; + const auto& normal_pair2 = normal_bdl_pairs[1]; + + CHECK(normal_pair1.type == layout::cell_type::NORMAL); + CHECK((normal_pair1.upper == cell{2, 0, 0} || normal_pair1.upper == cell{3, 0, 0})); + CHECK((normal_pair1.lower == cell{4, 0, 0} || normal_pair1.lower == cell{5, 0, 0})); + + CHECK(normal_pair2.type == layout::cell_type::NORMAL); + CHECK((normal_pair2.upper == cell{2, 0, 0} || normal_pair2.upper == cell{3, 0, 0})); + CHECK((normal_pair2.lower == cell{4, 0, 0} || normal_pair2.lower == cell{5, 0, 0})); + } + SECTION("0.5 nm lower threshold") + { + params.minimum_distance = 0.5; + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + const auto normal_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::NORMAL, params); + + REQUIRE(input_bdl_pairs.empty()); + REQUIRE(output_bdl_pairs.empty()); + REQUIRE(normal_bdl_pairs.size() == 2); + + const auto& normal_pair1 = normal_bdl_pairs[0]; + const auto& normal_pair2 = normal_bdl_pairs[1]; + + CHECK(normal_pair1.type == layout::cell_type::NORMAL); + CHECK((normal_pair1.upper == cell{2, 0, 0} || normal_pair1.upper == cell{3, 0, 0})); + CHECK((normal_pair1.lower == cell{4, 0, 0} || normal_pair1.lower == cell{5, 0, 0})); + + CHECK(normal_pair2.type == layout::cell_type::NORMAL); + CHECK((normal_pair2.upper == cell{2, 0, 0} || normal_pair2.upper == cell{3, 0, 0})); + CHECK((normal_pair2.lower == cell{4, 0, 0} || normal_pair2.lower == cell{5, 0, 0})); + } + SECTION("0 nm lower threshold") + { + params.minimum_distance = 0; + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + const auto normal_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::NORMAL, params); + + REQUIRE(input_bdl_pairs.size() == 1); + REQUIRE(output_bdl_pairs.size() == 1); + REQUIRE(normal_bdl_pairs.size() == 2); + + const auto& input_pair = input_bdl_pairs.front(); + const auto& output_pair = output_bdl_pairs.front(); + + const auto& normal_pair1 = normal_bdl_pairs[0]; + const auto& normal_pair2 = normal_bdl_pairs[1]; + + CHECK(input_pair.type == layout::cell_type::INPUT); + CHECK(input_pair.upper == cell{0, 0, 0}); + CHECK(input_pair.lower == cell{1, 0, 0}); + + CHECK(output_pair.type == layout::cell_type::OUTPUT); + CHECK(output_pair.upper == cell{6, 0, 0}); + CHECK(output_pair.lower == cell{7, 0, 0}); + + CHECK(normal_pair1.type == layout::cell_type::NORMAL); + CHECK((normal_pair1.upper == cell{2, 0, 0} || normal_pair1.upper == cell{4, 0, 0})); + CHECK((normal_pair1.lower == cell{3, 0, 0} || normal_pair1.lower == cell{5, 0, 0})); + + CHECK(normal_pair2.type == layout::cell_type::NORMAL); + CHECK((normal_pair2.upper == cell{2, 0, 0} || normal_pair2.upper == cell{4, 0, 0})); + CHECK((normal_pair2.lower == cell{3, 0, 0} || normal_pair2.lower == cell{5, 0, 0})); + } +} + +TEST_CASE("BDL wire BDL detection", "[detect-bdl-pairs]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{20, 0}, "BDL wire"}; + + lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 0, 0}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({8, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({12, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({14, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({18, 0, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({20, 0, 0}, sidb_technology::cell_type::OUTPUT); + + detect_bdl_pairs_params params{}; + // set default lower threshold to 0 for testing + params.minimum_distance = 0; + + SECTION("default upper threshold") + { + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + + REQUIRE(input_bdl_pairs.size() == 1); + REQUIRE(output_bdl_pairs.size() == 1); + + const auto& input_pair = input_bdl_pairs.front(); + const auto& output_pair = output_bdl_pairs.front(); + + CHECK(input_pair.type == layout::cell_type::INPUT); + CHECK(input_pair.upper == cell{0, 0, 0}); + CHECK(input_pair.lower == cell{2, 0, 0}); + + CHECK(output_pair.type == layout::cell_type::OUTPUT); + CHECK(output_pair.upper == cell{18, 0, 0}); + CHECK(output_pair.lower == cell{20, 0, 0}); + } + SECTION("1 nm upper threshold") + { + params.maximum_distance = 1; + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + + REQUIRE(input_bdl_pairs.size() == 1); + REQUIRE(output_bdl_pairs.size() == 1); + + const auto& input_pair = input_bdl_pairs.front(); + const auto& output_pair = output_bdl_pairs.front(); + + CHECK(input_pair.type == layout::cell_type::INPUT); + CHECK(input_pair.upper == cell{0, 0, 0}); + CHECK(input_pair.lower == cell{2, 0, 0}); + + CHECK(output_pair.type == layout::cell_type::OUTPUT); + CHECK(output_pair.upper == cell{18, 0, 0}); + CHECK(output_pair.lower == cell{20, 0, 0}); + } + SECTION("0.5 nm upper threshold") + { + params.maximum_distance = 0.5; + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT, params); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, params); + + // the threshold is too small to detect any BDL pairs + REQUIRE(input_bdl_pairs.empty()); + REQUIRE(output_bdl_pairs.empty()); + } +} + +TEST_CASE("SiQAD's AND gate BDL detection", "[detect-bdl-pairs]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{20, 10}, "AND gate"}; + + lyt.assign_cell_type({0, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({20, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({18, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({4, 2, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({6, 3, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({14, 3, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({16, 2, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({10, 6, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({10, 7, 0}, sidb_technology::cell_type::OUTPUT); + + lyt.assign_cell_type({10, 9, 1}, sidb_technology::cell_type::NORMAL); + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT); + + REQUIRE(input_bdl_pairs.size() == 2); + REQUIRE(output_bdl_pairs.size() == 1); + + const auto& input_pair1 = input_bdl_pairs[0]; + const auto& input_pair2 = input_bdl_pairs[1]; + const auto& output_pair = output_bdl_pairs.front(); + + CHECK(input_pair1.type == layout::cell_type::INPUT); + CHECK((input_pair1.upper == cell{20, 0, 1} || input_pair1.upper == cell{0, 0, 1})); + CHECK((input_pair1.lower == cell{18, 1, 1} || input_pair1.lower == cell{2, 1, 1})); + + CHECK(input_pair2.type == layout::cell_type::INPUT); + CHECK((input_pair2.upper == cell{20, 0, 1} || input_pair2.upper == cell{0, 0, 1})); + CHECK((input_pair2.lower == cell{18, 1, 1} || input_pair2.lower == cell{2, 1, 1})); + + CHECK(output_pair.type == layout::cell_type::OUTPUT); + CHECK(output_pair.upper == cell{10, 6, 0}); + CHECK(output_pair.lower == cell{10, 7, 0}); +} + +TEST_CASE("Bestagon fan-out BDL detection", "[detect-bdl-pairs]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{42, 21}, "Fan-out"}; + + lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({4, 2, 0}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({8, 3, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({10, 4, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({14, 5, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({16, 6, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({20, 7, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({21, 8, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({19, 12, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({23, 12, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({20, 14, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({16, 16, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({14, 17, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({26, 16, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({28, 17, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({10, 18, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({8, 19, 0}, sidb_technology::cell_type::OUTPUT); + + lyt.assign_cell_type({32, 18, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({34, 19, 0}, sidb_technology::cell_type::OUTPUT); + + lyt.assign_cell_type({4, 20, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({38, 20, 0}, sidb_technology::cell_type::NORMAL); + + const auto input_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::INPUT); + const auto output_bdl_pairs = detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT); + + REQUIRE(input_bdl_pairs.size() == 1); + REQUIRE(output_bdl_pairs.size() == 2); + + const auto& input_pair = input_bdl_pairs.front(); + const auto& output_pair1 = output_bdl_pairs[0]; + const auto& output_pair2 = output_bdl_pairs[1]; + + CHECK(input_pair.type == layout::cell_type::INPUT); + CHECK(input_pair.upper == cell{2, 1, 0}); + CHECK(input_pair.lower == cell{4, 2, 0}); + + CHECK(output_pair1.type == layout::cell_type::OUTPUT); + CHECK((output_pair1.upper == cell{10, 18, 0} || output_pair1.upper == cell{32, 18, 0})); + CHECK((output_pair1.lower == cell{8, 19, 0} || output_pair1.lower == cell{34, 19, 0})); + + CHECK(output_pair2.type == layout::cell_type::OUTPUT); + CHECK((output_pair2.upper == cell{10, 18, 0} || output_pair2.upper == cell{32, 18, 0})); + CHECK((output_pair2.lower == cell{8, 19, 0} || output_pair2.lower == cell{34, 19, 0})); +} diff --git a/test/algorithms/simulation/sidb/operational_domain.cpp b/test/algorithms/simulation/sidb/operational_domain.cpp new file mode 100644 index 000000000..a1c614469 --- /dev/null +++ b/test/algorithms/simulation/sidb/operational_domain.cpp @@ -0,0 +1,438 @@ +// +// Created by marcel on 27.07.23. +// + +#include + +#include +#include +#include +#include + +using namespace fiction; + +TEST_CASE("Structured binding support for parameter_points", "[operational-domain]") +{ + auto param_point = operational_domain::parameter_point{1.0, 2.0}; + + CHECK(param_point.x == 1.0); + CHECK(param_point.y == 2.0); + + const auto& [x, y] = param_point; + + CHECK(x == 1.0); + CHECK(y == 2.0); +} + +void check_op_domain_params_and_operational_status( + const operational_domain& op_domain, const operational_domain_params& params, + const std::optional& status) noexcept +{ + CHECK(op_domain.x_dimension == params.x_dimension); + CHECK(op_domain.y_dimension == params.y_dimension); + + for (const auto& [coord, op_value] : op_domain.operational_values) + { + CHECK(coord.x >= params.x_min); + CHECK(coord.x <= params.x_max); + CHECK(coord.y >= params.y_min); + CHECK(coord.y <= params.y_max); + + if (status) + { + CHECK(op_value == *status); + } + } +} + +TEST_CASE("BDL wire operational domain computation", "[operational-domain]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{24, 0}, "BDL wire"}; + + lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({3, 0, 0}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({8, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({12, 0, 0}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({14, 0, 0}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({18, 0, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({20, 0, 0}, sidb_technology::cell_type::OUTPUT); + + // output perturber + lyt.assign_cell_type({24, 0, 0}, sidb_technology::cell_type::NORMAL); + + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + + operational_domain_params op_domain_params{}; + op_domain_params.sim_params = sim_params; + op_domain_params.x_dimension = operational_domain::sweep_parameter::EPSILON_R; + op_domain_params.y_dimension = operational_domain::sweep_parameter::LAMBDA_TF; + + operational_domain_stats op_domain_stats{}; + + SECTION("operational area") + { + op_domain_params.x_min = 5.1; + op_domain_params.x_max = 6.1; + op_domain_params.x_step = 0.1; + + op_domain_params.y_min = 4.5; + op_domain_params.y_max = 5.5; + op_domain_params.y_step = 0.1; + + SECTION("grid_search") + { + const auto op_domain = + operational_domain_grid_search(lyt, create_id_tt(), op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (10 steps in each dimension) + CHECK(op_domain.operational_values.size() == 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations == 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("random_sampling") + { + const auto op_domain = + operational_domain_random_sampling(lyt, create_id_tt(), 100, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_evaluated_parameter_combinations > 0); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("flood_fill") + { + const auto op_domain = + operational_domain_flood_fill(lyt, create_id_tt(), 1, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() == 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations == 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("contour_tracing") + { + const auto op_domain = + operational_domain_contour_tracing(lyt, create_id_tt(), 1, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + } + SECTION("non-operational area") + { + op_domain_params.x_min = 2.5; + op_domain_params.x_max = 3.5; + op_domain_params.x_step = 0.1; + + op_domain_params.y_min = 4.5; + op_domain_params.y_max = 5.5; + op_domain_params.y_step = 0.1; + + SECTION("grid_search") + { + const auto op_domain = + operational_domain_grid_search(lyt, create_id_tt(), op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (10 steps in each dimension) + CHECK(op_domain.operational_values.size() == 100); + + // for the selected range, all samples should be within the parameters and non-operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::NON_OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 0); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 100); + } + SECTION("random_sampling") + { + const auto op_domain = + operational_domain_random_sampling(lyt, create_id_tt(), 50, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct maximum size + CHECK(op_domain.operational_values.size() <= 50); + + // for the selected range, all samples should be within the parameters and non-operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::NON_OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 100); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 50); + CHECK(op_domain_stats.num_operational_parameter_combinations == 0); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 50); + } + SECTION("flood_fill") + { + const auto op_domain = + operational_domain_flood_fill(lyt, create_id_tt(), 25, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct maximum size + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters and non-operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::NON_OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 0); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 100); + } + SECTION("contour_tracing") + { + const auto op_domain = + operational_domain_contour_tracing(lyt, create_id_tt(), 25, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct maximum size + CHECK(op_domain.operational_values.size() <= 25); + + // for the selected range, all samples should be within the parameters and non-operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::NON_OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 50); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 25); + CHECK(op_domain_stats.num_operational_parameter_combinations == 0); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 25); + } + } + SECTION("semi-operational area") + { + op_domain_params.x_min = 0.5; + op_domain_params.x_max = 4.5; + op_domain_params.x_step = 0.25; + + op_domain_params.y_min = 0.5; + op_domain_params.y_max = 4.5; + op_domain_params.y_step = 0.25; + + SECTION("grid_search") + { + const auto op_domain = + operational_domain_grid_search(lyt, create_id_tt(), op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (16 steps in each dimension) + CHECK(op_domain.operational_values.size() == 256); + + // for the selected range, all samples should be within the parameters + check_op_domain_params_and_operational_status(op_domain, op_domain_params, std::nullopt); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 512); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 256); + CHECK(op_domain_stats.num_operational_parameter_combinations == 80); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 176); + } + SECTION("random_sampling") + { + const auto op_domain = + operational_domain_random_sampling(lyt, create_id_tt(), 100, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct maximum size + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters + check_op_domain_params_and_operational_status(op_domain, op_domain_params, std::nullopt); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 100); + } + SECTION("flood_fill") + { + const auto op_domain = + operational_domain_flood_fill(lyt, create_id_tt(), 50, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size + CHECK(op_domain.operational_values.size() >= 80); + + // for the selected range, all samples should be within the parameters + check_op_domain_params_and_operational_status(op_domain, op_domain_params, std::nullopt); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 512); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 512); + CHECK(op_domain_stats.num_operational_parameter_combinations == 80); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 100); + } + SECTION("contour_tracing") + { + const auto op_domain = + operational_domain_contour_tracing(lyt, create_id_tt(), 50, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters + check_op_domain_params_and_operational_status(op_domain, op_domain_params, std::nullopt); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 200); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 512); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 80); + CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 100); + } + } +} + +TEST_CASE("SiQAD's AND gate operational domain computation", "[operational-domain]") +{ + using layout = sidb_cell_clk_lyt_siqad; + + layout lyt{{20, 10}, "AND gate"}; + + lyt.assign_cell_type({0, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({20, 0, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({18, 1, 1}, sidb_technology::cell_type::INPUT); + + lyt.assign_cell_type({4, 2, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({6, 3, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({14, 3, 1}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({16, 2, 1}, sidb_technology::cell_type::NORMAL); + + lyt.assign_cell_type({10, 6, 0}, sidb_technology::cell_type::OUTPUT); + lyt.assign_cell_type({10, 7, 0}, sidb_technology::cell_type::OUTPUT); + + lyt.assign_cell_type({10, 9, 1}, sidb_technology::cell_type::NORMAL); + + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + sim_params.mu_minus = -0.28; + + operational_domain_params op_domain_params{}; + op_domain_params.sim_params = sim_params; + op_domain_params.x_dimension = operational_domain::sweep_parameter::EPSILON_R; + op_domain_params.x_min = 5.1; + op_domain_params.x_max = 6.1; + op_domain_params.x_step = 0.1; + op_domain_params.y_dimension = operational_domain::sweep_parameter::LAMBDA_TF; + op_domain_params.y_min = 4.5; + op_domain_params.y_max = 5.5; + op_domain_params.y_step = 0.1; + + operational_domain_stats op_domain_stats{}; + + SECTION("grid_search") + { + const auto op_domain = operational_domain_grid_search(lyt, create_and_tt(), op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (10 steps in each dimension) + CHECK(op_domain.operational_values.size() == 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations == 400); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("random_sampling") + { + const auto op_domain = + operational_domain_random_sampling(lyt, create_and_tt(), 100, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 400); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("flood_fill") + { + const auto op_domain = + operational_domain_flood_fill(lyt, create_and_tt(), 1, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (10 steps in each dimension) + CHECK(op_domain.operational_values.size() == 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations == 400); + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 100); + CHECK(op_domain_stats.num_operational_parameter_combinations == 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } + SECTION("contour_tracing") + { + const auto op_domain = + operational_domain_contour_tracing(lyt, create_and_tt(), 1, op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size (max 10 steps in each dimension) + CHECK(op_domain.operational_values.size() <= 100); + + // for the selected range, all samples should be within the parameters and operational + check_op_domain_params_and_operational_status(op_domain, op_domain_params, + operational_domain::operational_status::OPERATIONAL); + + CHECK(mockturtle::to_seconds(op_domain_stats.time_total) > 0.0); + CHECK(op_domain_stats.num_simulator_invocations <= 400); + CHECK(op_domain_stats.num_evaluated_parameter_combinations <= 100); + CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 0); + } +} diff --git a/test/algorithms/simulation/sidb/quickexact.cpp b/test/algorithms/simulation/sidb/quickexact.cpp index 876d1da0c..c18317460 100644 --- a/test/algorithms/simulation/sidb/quickexact.cpp +++ b/test/algorithms/simulation/sidb/quickexact.cpp @@ -913,7 +913,7 @@ TEMPLATE_TEST_CASE("3 DBs next to each other with automatic base number detectio CHECK(std::any_cast(simulation_results.additional_simulation_parameters[0].second) == 3); const quickexact_params params_new{sidb_simulation_parameters{2, -0.32}, - automatic_base_number_detection::OFF}; + quickexact_params::automatic_base_number_detection::OFF}; const auto simulation_results_new = quickexact(lyt, params_new); @@ -925,9 +925,7 @@ TEMPLATE_TEST_CASE("3 DBs next to each other with automatic base number detectio TEMPLATE_TEST_CASE("13 DBs which are all negatively charged", "[quickexact]", (cell_level_layout>>)) { - using sidb_layout = cell_level_layout>>; - - sidb_layout lyt{{20, 10}}; + TestType lyt{{20, 10}}; lyt.assign_cell_type({26, 10, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({23, 19, 1}, TestType::cell_type::NORMAL); @@ -979,7 +977,8 @@ TEMPLATE_TEST_CASE("QuickExact simulation of a Y-shape SiDB OR gate with input 0 lyt.assign_cell_type({10, 8, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({16, 1, 0}, TestType::cell_type::NORMAL); - quickexact_params params{sidb_simulation_parameters{3, -0.28}, automatic_base_number_detection::OFF}; + quickexact_params params{sidb_simulation_parameters{3, -0.28}, + quickexact_params::automatic_base_number_detection::OFF}; SECTION("Standard Physical Parameters") { @@ -1131,7 +1130,8 @@ TEMPLATE_TEST_CASE("QuickExact simulation of a 3 DB Wire", "[ExGS]", lyt.assign_cell_type({26, 0, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({29, 0, 0}, TestType::cell_type::NORMAL); - quickexact_params params{sidb_simulation_parameters{3, -0.28}, automatic_base_number_detection::OFF}; + quickexact_params params{sidb_simulation_parameters{3, -0.28}, + quickexact_params::automatic_base_number_detection::OFF}; SECTION("Standard Physical Parameters") { diff --git a/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp b/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp index 7ebc49e6e..4decca875 100644 --- a/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp +++ b/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp @@ -98,9 +98,10 @@ TEST_CASE("Random cube::coord_t layout generation", "[generate_random_sidb_layou SECTION("given corner coordinates and number of placed SiDBs, and forbid positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 100, - positive_charges::ALLOWED}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 100, + generate_random_sidb_layout_params::positive_charges::ALLOWED}; const auto result_lyt = generate_random_sidb_layout(cube_layout{}, params); @@ -115,9 +116,10 @@ TEST_CASE("Random cube::coord_t layout generation", "[generate_random_sidb_layou SECTION("given corner coordinates and number of placed SiDBs, and allow positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 100, - positive_charges::FORBIDDEN}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 100, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN}; const auto result_lyt = generate_random_sidb_layout(cube_layout{}, params); @@ -146,7 +148,12 @@ TEST_CASE("Random cube::coord_t layout generation", "[generate_random_sidb_layou SECTION("given previous layouts") { const generate_random_sidb_layout_params params{ - {{-5, -2}, {9, 9}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 3}; + {{-5, -2}, {9, 9}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 3}; const auto result_lyts = generate_multiple_random_sidb_layouts(cube_layout{}, params); CHECK(result_lyts.size() == 3); @@ -166,7 +173,12 @@ TEST_CASE("Random cube::coord_t layout generation", "[generate_random_sidb_layou SECTION("Check uniqueness of two layouts") { const generate_random_sidb_layout_params params{ - {{0, 0}, {9, 9}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 2}; + {{0, 0}, {9, 9}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 2}; const auto result_lyts = generate_multiple_random_sidb_layouts(cube_layout{}, params); REQUIRE(result_lyts.size() == 2); @@ -256,9 +268,10 @@ TEST_CASE("Random offset::ucoord_t layout generation", "[generate_random_sidb_la SECTION("given corner coordinates and number of placed SiDBs, and forbid positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 100, - positive_charges::ALLOWED}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 100, + generate_random_sidb_layout_params::positive_charges::ALLOWED}; const auto result_lyt = generate_random_sidb_layout(sidb_cell_clk_lyt{}, params); @@ -273,9 +286,10 @@ TEST_CASE("Random offset::ucoord_t layout generation", "[generate_random_sidb_la SECTION("given corner coordinates and number of placed SiDBs, and allow positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 100, - positive_charges::FORBIDDEN}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 100, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN}; const auto result_lyt = generate_random_sidb_layout(sidb_cell_clk_lyt{}, params); @@ -304,7 +318,12 @@ TEST_CASE("Random offset::ucoord_t layout generation", "[generate_random_sidb_la SECTION("given previous layouts") { const generate_random_sidb_layout_params params{ - {{0, 0}, {9, 9, 2}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 3}; + {{0, 0}, {9, 9, 2}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 3}; const auto result_lyts = generate_multiple_random_sidb_layouts(sidb_cell_clk_lyt{}, params); CHECK(result_lyts.size() == 3); @@ -322,7 +341,12 @@ TEST_CASE("Random offset::ucoord_t layout generation", "[generate_random_sidb_la SECTION("Check uniqueness of two layouts") { const generate_random_sidb_layout_params params{ - {{0, 0}, {9, 9}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 2}; + {{0, 0}, {9, 9}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 2}; const auto result_lyts = generate_multiple_random_sidb_layouts(sidb_cell_clk_lyt{}, params); REQUIRE(result_lyts.size() == 2); @@ -360,7 +384,12 @@ TEST_CASE("Random offset::ucoord_t layout generation", "[generate_random_sidb_la SECTION("Check correct use of skeleton layout when generating multiple random layouts") { const generate_random_sidb_layout_params params{ - {{0, 0}, {9, 9}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 2}; + {{0, 0}, {9, 9}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 2}; sidb_cell_clk_lyt skeleton_layout{}; skeleton_layout.assign_cell_type({0, 0}, sidb_cell_clk_lyt::technology::NORMAL); skeleton_layout.assign_cell_type({3, 0}, sidb_cell_clk_lyt::technology::NORMAL); @@ -438,9 +467,10 @@ TEST_CASE("Random siqad::coord_t layout generation", "[generate_random_sidb_layo SECTION("given corner coordinates and number of placed SiDBs, and allow positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 100, - positive_charges::ALLOWED}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 100, + generate_random_sidb_layout_params::positive_charges::ALLOWED}; const auto result_lyt = generate_random_sidb_layout(sidb_cell_clk_lyt{}, params); @@ -455,9 +485,10 @@ TEST_CASE("Random siqad::coord_t layout generation", "[generate_random_sidb_layo SECTION("given corner coordinates and number of placed SiDBs, and forbid positive charges") { - const generate_random_sidb_layout_params params{{{0, 0, 0}, {90, 90, 0}}, - 10, - positive_charges::FORBIDDEN}; + const generate_random_sidb_layout_params params{ + {{0, 0, 0}, {90, 90, 0}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN}; const auto result_lyt = generate_random_sidb_layout(sidb_cell_clk_lyt{}, params); @@ -487,7 +518,12 @@ TEST_CASE("Random siqad::coord_t layout generation", "[generate_random_sidb_layo SECTION("given previous layouts") { const generate_random_sidb_layout_params params{ - {{0, 0, 1}, {9, 9, 1}}, 10, positive_charges::FORBIDDEN, 2, static_cast(10E6), 3}; + {{0, 0, 1}, {9, 9, 1}}, + 10, + generate_random_sidb_layout_params::positive_charges::FORBIDDEN, + 2, + static_cast(10E6), + 3}; const auto result_lyts = generate_multiple_random_sidb_layouts(sidb_cell_clk_lyt{}, params); CHECK(result_lyts.size() == 3); diff --git a/test/algorithms/simulation/sidb/time_to_solution.cpp b/test/algorithms/simulation/sidb/time_to_solution.cpp index 72d694616..f3ce1c458 100644 --- a/test/algorithms/simulation/sidb/time_to_solution.cpp +++ b/test/algorithms/simulation/sidb/time_to_solution.cpp @@ -34,7 +34,7 @@ TEMPLATE_TEST_CASE( const sidb_simulation_parameters params{2, -0.30}; const quicksim_params quicksim_params{params}; time_to_solution_stats tts_stat_quickexact{}; - const time_to_solution_params tts_params_quickexact{exhaustive_algorithm::QUICKEXACT}; + const time_to_solution_params tts_params_quickexact{exhaustive_sidb_simulation_engine::QUICKEXACT}; sim_acc_tts(lyt, quicksim_params, tts_params_quickexact, &tts_stat_quickexact); CHECK(tts_stat_quickexact.algorithm == "QuickExact"); @@ -44,7 +44,7 @@ TEMPLATE_TEST_CASE( CHECK(tts_stat_quickexact.mean_single_runtime > 0.0); time_to_solution_stats tts_stat_exgs{}; - const time_to_solution_params tts_params_exgs{exhaustive_algorithm::EXGS}; + const time_to_solution_params tts_params_exgs{exhaustive_sidb_simulation_engine::EXGS}; sim_acc_tts(lyt, quicksim_params, tts_params_exgs, &tts_stat_exgs); CHECK(tts_stat_exgs.algorithm == "ExGS"); @@ -67,7 +67,7 @@ TEMPLATE_TEST_CASE( const sidb_simulation_parameters params{3, -0.30}; const quicksim_params quicksim_params{params}; - const time_to_solution_params tts_params_exgs{exhaustive_algorithm::EXGS}; + const time_to_solution_params tts_params_exgs{exhaustive_sidb_simulation_engine::EXGS}; time_to_solution_stats tts_stat_exgs{}; sim_acc_tts(lyt, quicksim_params, tts_params_exgs, &tts_stat_exgs); @@ -76,7 +76,7 @@ TEMPLATE_TEST_CASE( CHECK(tts_stat_exgs.mean_single_runtime > 0.0); time_to_solution_stats tts_stat_quickexact{}; - const time_to_solution_params tts_params{exhaustive_algorithm::QUICKEXACT}; + const time_to_solution_params tts_params{exhaustive_sidb_simulation_engine::QUICKEXACT}; sim_acc_tts(lyt, quicksim_params, tts_params, &tts_stat_quickexact); REQUIRE(tts_stat_quickexact.acc == 100); diff --git a/test/io/csv_writer.cpp b/test/io/csv_writer.cpp new file mode 100644 index 000000000..54d149fff --- /dev/null +++ b/test/io/csv_writer.cpp @@ -0,0 +1,79 @@ +// +// Created by marcel on 02.08.23. +// + +#include + +#include + +#include +#include +#include + +using namespace fiction; + +TEST_CASE("CSV writer", "[csv-writer]") +{ + std::stringstream ss{}; + + csv_writer writer(ss); + + SECTION("writing single integer value") + { + writer.write_line(1); + CHECK(ss.str() == "1\n"); + } + + SECTION("writing single string value") + { + writer.write_line("fiction"); + CHECK(ss.str() == "fiction\n"); + } + + SECTION("writing single double value") + { + writer.write_line(3.14159); + CHECK(ss.str() == "3.14159\n"); + } + + SECTION("writing multiple integer values") + { + writer.write_line(1, 2, 3, 4, 5); + CHECK(ss.str() == "1,2,3,4,5\n"); + } + + SECTION("writing multiple string values") + { + writer.write_line("a", "b", "c"); + CHECK(ss.str() == "a,b,c\n"); + } + + SECTION("writing mixed type values") + { + writer.write_line("mixed", 1234, 42.0); + CHECK(ss.str() == "mixed,1234,42\n"); + } + + SECTION("writing with empty values") + { + writer.write_line(""); + CHECK(ss.str() == "\n"); + writer.write_line(1234, "", 42.0); + CHECK(ss.str() == "\n1234,,42\n"); + } + SECTION("writing multiple lines") + { + writer.write_line("line1", 1234, 42.0); + writer.write_line("line2", 5678, 43.0); + CHECK(ss.str() == "line1,1234,42\nline2,5678,43\n"); + + writer.write_line(1, 2, 3); + writer.write_line(4, 5, 6); + CHECK(ss.str() == "line1,1234,42\nline2,5678,43\n1,2,3\n4,5,6\n"); + + writer.write_line("a", "b", "c"); + writer.write_line("d", "e", "f"); + CHECK(ss.str() == "line1,1234,42\nline2,5678,43\n1,2,3\n4,5,6\n" + "a,b,c\nd,e,f\n"); + } +} diff --git a/test/io/read_sqd_layout.cpp b/test/io/read_sqd_layout.cpp index 7650e6ba7..a0933d9a0 100644 --- a/test/io/read_sqd_layout.cpp +++ b/test/io/read_sqd_layout.cpp @@ -120,6 +120,52 @@ TEST_CASE("Read multi-dot SQD layout", "[sqd]") CHECK(layout.get_cell_type({2, 5}) == sidb_technology::cell_type::NORMAL); } +TEST_CASE("Read multi-dot SQD layout with cell type definitions", "[sqd]") +{ + static constexpr const char* sqd_layout = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " 2\n" + " \n" + " input\n" + " \n" + " \n" + " 2\n" + " \n" + " output\n" + " \n" + " \n" + " 2\n" + " \n" + " normal\n" + " \n" + " \n" + " 2\n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + std::istringstream layout_stream{sqd_layout}; + + using sidb_layout = cell_level_layout>>; + const auto layout = read_sqd_layout(layout_stream); + + CHECK(layout.x() == 2); + CHECK(layout.y() == 5); + + CHECK(layout.get_cell_type({0, 0}) == sidb_technology::cell_type::INPUT); + CHECK(layout.get_cell_type({0, 1}) == sidb_technology::cell_type::OUTPUT); + CHECK(layout.get_cell_type({2, 4}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({2, 5}) == sidb_technology::cell_type::NORMAL); +} + TEST_CASE("Read single defect SQD layout", "[sqd]") { static constexpr const char* sqd_layout = "\n" @@ -442,7 +488,7 @@ TEST_CASE("Read SQD defect despite missing element", "[sqd]") CHECK(defect.type == sidb_defect_type::UNKNOWN); - CHECK(defect.charge == 0.0); + CHECK(defect.charge == 0); CHECK(defect.epsilon_r == 0.0); CHECK(defect.lambda_tf == 0.0); } @@ -719,6 +765,58 @@ TEST_CASE("SQD parsing error: missing element in elemen CHECK_THROWS_AS(read_sqd_layout(layout_stream), sqd_parsing_error); } +TEST_CASE("SQD parsing error: missing element in element") +{ + static constexpr const char* sqd_layout = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " 2\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + std::istringstream layout_stream{sqd_layout}; + + using sidb_layout = + sidb_surface>>>; + + CHECK_THROWS_AS(read_sqd_layout(layout_stream), sqd_parsing_error); +} + +TEST_CASE("SQD parsing error: invalid element in element") +{ + static constexpr const char* sqd_layout = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " 2\n" + " \n" + " invalid\n" + " \n" + " \n" + " \n" + "\n"; + + std::istringstream layout_stream{sqd_layout}; + + using sidb_layout = + sidb_surface>>>; + + CHECK_THROWS_AS(read_sqd_layout(layout_stream), sqd_parsing_error); +} + TEST_CASE("SQD parsing error: missing 'charge' attribute in element", "[sqd]") { static constexpr const char* sqd_layout = "\n" diff --git a/test/io/write_operational_domain.cpp b/test/io/write_operational_domain.cpp new file mode 100644 index 000000000..71e575fa5 --- /dev/null +++ b/test/io/write_operational_domain.cpp @@ -0,0 +1,126 @@ +// +// Created by marcel on 24.06.22. +// + +#include + +#include +#include + +#include +#include + +using namespace fiction; + +TEST_CASE("Write empty operational domain", "[write-operational-domain]") +{ + operational_domain opdom{}; + + std::ostringstream os{}; + + SECTION("default sweep dimensions") + { + static constexpr const char* expected = "epsilon_r,lambda_tf,operational status\n"; + + write_operational_domain(opdom, os); + + CHECK(os.str() == expected); + } + SECTION("custom sweep dimensions") + { + opdom.x_dimension = operational_domain::sweep_parameter::LAMBDA_TF; + opdom.y_dimension = operational_domain::sweep_parameter::MU_MINUS; + + static constexpr const char* expected = "lambda_tf,mu_minus,operational status\n"; + + write_operational_domain(opdom, os); + + CHECK(os.str() == expected); + } +} + +TEST_CASE("Write simple operational domain", "[write-operational-domain]") +{ + operational_domain opdom{}; + opdom.operational_values = {{{0, 0}, operational_domain::operational_status::OPERATIONAL}, + {{0, 1}, operational_domain::operational_status::NON_OPERATIONAL}}; + + std::ostringstream os{}; + + SECTION("default operational tags") + { + std::set expected{"epsilon_r,lambda_tf,operational status", "0,0,operational", + "0,1,non-operational"}; + + write_operational_domain(opdom, os); + const auto os_str = os.str(); + + std::istringstream is{os_str}; + + for (std::string line{}; std::getline(is, line);) + { + CHECK(expected.find(line) != expected.end()); + } + } + + SECTION("custom operational tags") + { + const write_operational_domain_params params = {"True", "False"}; + + std::set expected{"epsilon_r,lambda_tf,operational status", "0,0,True", "0,1,False"}; + + write_operational_domain(opdom, os, params); + const auto os_str = os.str(); + + std::istringstream is{os_str}; + + for (std::string line{}; std::getline(is, line);) + { + CHECK(expected.find(line) != expected.end()); + } + } +} + +TEST_CASE("Write operational domain with floating-point parameter values", "[write-operational-domain]") +{ + operational_domain opdom{}; + opdom.operational_values = {{{0.1, 0.2}, operational_domain::operational_status::OPERATIONAL}, + {{0.3, 0.4}, operational_domain::operational_status::NON_OPERATIONAL}, + {{1.2, 1.4}, operational_domain::operational_status::OPERATIONAL}, + {{2.4, 5.75}, operational_domain::operational_status::NON_OPERATIONAL}}; + + std::ostringstream os{}; + + SECTION("default operational tags") + { + std::set expected{"epsilon_r,lambda_tf,operational status", "0.1,0.2,operational", + "0.3,0.4,non-operational", "1.2,1.4,operational", "2.4,5.75,non-operational"}; + + write_operational_domain(opdom, os); + const auto os_str = os.str(); + + std::istringstream is{os_str}; + + for (std::string line{}; std::getline(is, line);) + { + CHECK(expected.find(line) != expected.end()); + } + } + SECTION("custom operational tags") + { + const write_operational_domain_params params = {"1", "0"}; + + std::set expected{"epsilon_r,lambda_tf,operational status", "0.1,0.2,1", "0.3,0.4,0", "1.2,1.4,1", + "2.4,5.75,0"}; + + write_operational_domain(opdom, os, params); + const auto os_str = os.str(); + + std::istringstream is{os_str}; + + for (std::string line{}; std::getline(is, line);) + { + CHECK(expected.find(line) != expected.end()); + } + } +} diff --git a/test/io/write_sqd_layout.cpp b/test/io/write_sqd_layout.cpp index bac318ba5..13d0edaba 100644 --- a/test/io/write_sqd_layout.cpp +++ b/test/io/write_sqd_layout.cpp @@ -47,7 +47,7 @@ void compare_written_and_read_layout(const WLyt& wlyt, const RLyt& rlyt) noexcep CHECK(wlyt.num_pis() == rlyt.num_pis()); CHECK(wlyt.num_pos() == rlyt.num_pos()); - wlyt.foreach_cell([&rlyt](const auto& c) { CHECK(rlyt.get_cell_type(c) == sidb_technology::cell_type::NORMAL); }); + wlyt.foreach_cell([&wlyt, &rlyt](const auto& c) { CHECK(wlyt.get_cell_type(c) == rlyt.get_cell_type(c)); }); if constexpr (has_foreach_sidb_defect_v && has_get_sidb_defect_v) { @@ -135,14 +135,34 @@ TEST_CASE("Write multi-dot SQD layout", "[sqd]") compare_written_and_read_layout(layout, read_layout); } -TEST_CASE("Write bestagon SQD layout", "[sqd]") +TEST_CASE("Write multi-dot SQD layout with differing dot types", "[sqd]") +{ + using sidb_layout = cell_level_layout>>; + + sidb_layout layout{{4, 4}}; + layout.assign_cell_type({0, 0}, sidb_technology::cell_type::INPUT); + layout.assign_cell_type({1, 1}, sidb_technology::cell_type::OUTPUT); + layout.assign_cell_type({0, 2}, sidb_technology::cell_type::NORMAL); + layout.assign_cell_type({0, 3}, sidb_technology::cell_type::OUTPUT); + layout.assign_cell_type({4, 4}, sidb_technology::cell_type::INPUT); + + std::stringstream layout_stream{}; + + write_sqd_layout(layout, layout_stream); + + const auto read_layout = read_sqd_layout(layout_stream); + + compare_written_and_read_layout(layout, read_layout); +} + +TEST_CASE("Write Bestagon SQD layout", "[sqd]") { using gate_layout = gate_level_layout>>>; using sidb_layout = cell_level_layout>>; auto g_layout = blueprints::row_clocked_and_xor_gate_layout(); - g_layout.set_layout_name("bestagon"); + g_layout.set_layout_name("Bestagon"); const auto c_layout = apply_gate_library(g_layout); @@ -150,7 +170,7 @@ TEST_CASE("Write bestagon SQD layout", "[sqd]") write_sqd_layout(c_layout, layout_stream); - const auto read_layout = read_sqd_layout(layout_stream, "bestagon"); + const auto read_layout = read_sqd_layout(layout_stream, "Bestagon"); compare_written_and_read_layout(c_layout, read_layout); } diff --git a/test/technology/charge_distribution_surface.cpp b/test/technology/charge_distribution_surface.cpp index d02e76376..d0566caa3 100644 --- a/test/technology/charge_distribution_surface.cpp +++ b/test/technology/charge_distribution_surface.cpp @@ -145,7 +145,8 @@ TEMPLATE_TEST_CASE( charge_distribution_surface charge_layout{lyt, sidb_simulation_parameters{2}}; CHECK(charge_layout.get_charge_index_and_base().first == 0); charge_layout.increase_charge_index_by_one(dependent_cell_mode::FIXED, energy_calculation::UPDATE_ENERGY, - charge_distribution_history::NEGLECT, exhaustive_algorithm::EXGS); + charge_distribution_history::NEGLECT, + exhaustive_sidb_simulation_engine::EXGS); CHECK(charge_layout.get_charge_index_and_base().first == 1); CHECK(charge_layout.get_charge_state_by_index(0) == sidb_charge_state::NEGATIVE); CHECK(charge_layout.get_charge_state_by_index(1) == sidb_charge_state::NEGATIVE); @@ -163,7 +164,7 @@ TEMPLATE_TEST_CASE( CHECK(charge_layout_quickexact.get_charge_index_and_base().first == 0); charge_layout_quickexact.increase_charge_index_by_one( dependent_cell_mode::FIXED, energy_calculation::UPDATE_ENERGY, charge_distribution_history::NEGLECT, - exhaustive_algorithm::QUICKEXACT); + exhaustive_sidb_simulation_engine::QUICKEXACT); CHECK(charge_layout_quickexact.get_charge_index_and_base().first == 1); CHECK(charge_layout_quickexact.get_charge_state_by_index(0) == sidb_charge_state::NEGATIVE); CHECK(charge_layout_quickexact.get_charge_state_by_index(1) == sidb_charge_state::NEGATIVE);