diff --git a/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp b/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp index c8d2e936c..de51da168 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp @@ -2,8 +2,8 @@ // Created by marcel on 21.11.23. // -#ifndef PYFICTION_CAN_POSITIVE_CHARGE_OCCUR_HPP -#define PYFICTION_CAN_POSITIVE_CHARGE_OCCUR_HPP +#ifndef PYFICTION_CAN_POSITIVE_CHARGES_OCCUR_HPP +#define PYFICTION_CAN_POSITIVE_CHARGES_OCCUR_HPP #include "pyfiction/documentation.hpp" #include "pyfiction/types.hpp" @@ -40,4 +40,4 @@ inline void can_positive_charges_occur(pybind11::module& m) } // namespace pyfiction -#endif // PYFICTION_CAN_POSITIVE_CHARGE_OCCUR_HPP +#endif // PYFICTION_CAN_POSITIVE_CHARGES_OCCUR_HPP diff --git a/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/is_operational.hpp b/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/is_operational.hpp index 5d2329a78..c2cf73349 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/is_operational.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/algorithms/simulation/sidb/is_operational.hpp @@ -8,12 +8,14 @@ #include "pyfiction/documentation.hpp" #include "pyfiction/types.hpp" +#include #include #include #include #include +#include namespace pyfiction { @@ -26,23 +28,59 @@ void is_operational(pybind11::module& m) { namespace py = pybind11; - m.def("is_operational", &fiction::is_operational, py::arg("lyt"), py::arg("spec"), - py::arg("params") = fiction::is_operational_params{}, py::arg("input_bdl_wire") = std::nullopt, - py::arg("output_bdl_wire") = std::nullopt, DOC(fiction_is_operational)); - - m.def("operational_input_patterns", &fiction::operational_input_patterns, py::arg("lyt"), - py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, + m.def("is_operational", + py::overload_cast&, const fiction::is_operational_params&>( + &fiction::is_operational), + py::arg("lyt"), py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, + DOC(fiction_is_operational)); + + m.def("is_operational", + py::overload_cast&, const fiction::is_operational_params&, + const std::vector>&, const std::vector>&, + const std::optional&>(&fiction::is_operational), + py::arg("lyt"), py::arg("spec"), py::arg("params"), py::arg("input_bdl_wire"), py::arg("output_bdl_wire"), + py::arg("canvas_lyt") = std::nullopt, DOC(fiction_is_operational_2)); + + m.def("operational_input_patterns", + py::overload_cast&, const fiction::is_operational_params&>( + &fiction::operational_input_patterns), + py::arg("lyt"), py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, DOC(fiction_operational_input_patterns)); - m.def("is_kink_induced_non_operational", &fiction::is_kink_induced_non_operational, py::arg("lyt"), - py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, - py::arg("input_bdl_wire") = std::nullopt, py::arg("output_bdl_wire") = std::nullopt, - DOC(fiction_is_kink_induced_non_operational)); + m.def("operational_input_patterns", + py::overload_cast&, const fiction::is_operational_params&, + const std::vector>&, const std::vector>&, + const std::optional&>(&fiction::operational_input_patterns), + py::arg("lyt"), py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, + py::arg("input_bdl_wire"), py::arg("output_bdl_wire"), py::arg("canvas_lyt") = std::nullopt, + DOC(fiction_operational_input_patterns_2)); m.def("kink_induced_non_operational_input_patterns", - &fiction::kink_induced_non_operational_input_patterns, py::arg("lyt"), py::arg("spec"), - py::arg("params") = fiction::is_operational_params{}, + py::overload_cast&, const fiction::is_operational_params&>( + &fiction::kink_induced_non_operational_input_patterns), + py::arg("lyt"), py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, DOC(fiction_kink_induced_non_operational_input_patterns)); + + m.def( + "kink_induced_non_operational_input_patterns", + py::overload_cast&, const fiction::is_operational_params&, + const std::vector>&, const std::vector>&, + const std::optional&>(&fiction::kink_induced_non_operational_input_patterns), + py::arg("lyt"), py::arg("spec"), py::arg("params"), py::arg("input_bdl_wire"), py::arg("output_bdl_wire"), + py::arg("canvas_lyt") = std::nullopt, DOC(fiction_kink_induced_non_operational_input_patterns_2)); + + m.def("is_kink_induced_non_operational", + py::overload_cast&, const fiction::is_operational_params&>( + &fiction::is_kink_induced_non_operational), + py::arg("lyt"), py::arg("spec"), py::arg("params") = fiction::is_operational_params{}, + DOC(fiction_is_kink_induced_non_operational)); + + m.def("is_kink_induced_non_operational", + py::overload_cast&, const fiction::is_operational_params&, + const std::vector>&, const std::vector>&, + const std::optional&>(&fiction::is_kink_induced_non_operational), + py::arg("lyt"), py::arg("spec"), py::arg("params"), py::arg("input_bdl_wire"), py::arg("output_bdl_wire"), + py::arg("canvas_lyt") = std::nullopt, DOC(fiction_is_kink_induced_non_operational_2)); } } // namespace detail @@ -56,11 +94,12 @@ inline void is_operational(pybind11::module& m) .value("NON_OPERATIONAL", fiction::operational_status::NON_OPERATIONAL, DOC(fiction_operational_status_NON_OPERATIONAL)); - py::enum_(m, "operational_condition", DOC(fiction_operational_condition)) - .value("TOLERATE_KINKS", fiction::operational_condition::TOLERATE_KINKS, - DOC(fiction_operational_condition_TOLERATE_KINKS)) - .value("REJECT_KINKS", fiction::operational_condition::REJECT_KINKS, - DOC(fiction_operational_condition_REJECT_KINKS)); + py::enum_( + m, "operational_condition", DOC(fiction_is_operational_params_operational_condition)) + .value("TOLERATE_KINKS", fiction::is_operational_params::operational_condition::TOLERATE_KINKS, + DOC(fiction_is_operational_params_operational_condition_TOLERATE_KINKS)) + .value("REJECT_KINKS", fiction::is_operational_params::operational_condition::REJECT_KINKS, + DOC(fiction_is_operational_params_operational_condition_REJECT_KINKS)); py::class_(m, "is_operational_params", DOC(fiction_is_operational_params)) .def(py::init<>()) @@ -71,7 +110,10 @@ inline void is_operational(pybind11::module& m) .def_readwrite("input_bdl_iterator_params", &fiction::is_operational_params::input_bdl_iterator_params, DOC(fiction_is_operational_params_input_bdl_iterator_params)) .def_readwrite("op_condition", &fiction::is_operational_params::op_condition, - DOC(fiction_is_operational_params_op_condition)); + DOC(fiction_is_operational_params_op_condition)) + .def_readwrite("strategy_to_analyze_operational_status", + &fiction::is_operational_params::strategy_to_analyze_operational_status, + DOC(fiction_is_operational_params_strategy_to_analyze_operational_status)); // NOTE be careful with the order of the following calls! Python will resolve the first matching overload! detail::is_operational(m); diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index d4e27f225..7cd51b729 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -587,6 +587,12 @@ starting pair. The resulting last BDL pairs are stored in @note Assumes that `input_bdl_wires` and `last_bdl_for_each_wire` are accessible within the scope.)doc"; +static const char *__doc_fiction_bdl_input_iterator_get_current_input_index = +R"doc(Returns the current input index. + +Returns: + The current input index.)doc"; + static const char *__doc_fiction_bdl_input_iterator_input_bdl_wires = R"doc(The detected input BDL wires.)doc"; static const char *__doc_fiction_bdl_input_iterator_input_pairs = R"doc(The detected input BDL pairs.)doc"; @@ -2179,6 +2185,15 @@ Parameter ``c``: Returns: Cell type assigned to cell position `c`.)doc"; +static const char *__doc_fiction_cell_level_layout_get_cells_by_type = +R"doc(Returns all cells of the given type. + +Parameter ``type``: + Type of cells to return. + +Returns: + All cells of the layout that have the given type.)doc"; + static const char *__doc_fiction_cell_level_layout_get_clock_number = R"doc(Returns the clock number of cell position `c` by accessing `ClockedLayout`'s underlying clocking scheme and respecting this @@ -5035,91 +5050,8 @@ Each combination is then used to create a gate layout candidate. A vector containing all possible gate layouts generated from the combinations.)doc"; -static const char *__doc_fiction_detail_design_sidb_gates_impl_determine_output_index_of_output = -R"doc(This function calculates the output index for a given input index by -evaluating the truth table. - -Parameter ``current_input_index``: - The index representing the current input pattern. - -Returns: - The output index derived from the truth table for the given input - index.)doc"; - static const char *__doc_fiction_detail_design_sidb_gates_impl_input_bdl_wires = R"doc(Input BDL wires.)doc"; -static const char *__doc_fiction_detail_design_sidb_gates_impl_is_io_signal_unstable = -R"doc(This function iterates through various input patterns and output wire -indices to determine if any configuration results in a physically -valid layout with energy below the given energy value, indicating I/O -signal instability. - -Parameter ``cds_layout``: - The charge distribution surface layout to be modified and checked. - -Parameter ``cds_canvas``: - The charge distribution of the canvas SiDBs. - -Parameter ``max_input_pattern_index``: - The maximum index for input pattern - -Parameter ``input_pattern``: - The specific input pattern for which the stability check is - conducted. - -Parameter ``logical_correct_output_pattern``: - The expected correct output pattern for the given input. - -Parameter ``minimal_energy_of_physically_valid_layout``: - The minimum energy threshold below which the layout is considered - unstable. - -Returns: - `true` if the I/O signal is unstable, `false` otherwise.)doc"; - -static const char *__doc_fiction_detail_design_sidb_gates_impl_is_physical_validity_feasible = -R"doc(This function determines if there is a charge distribution of the -canvas SiDBs for which the charge distribution of the whole layout is -physically valid. - -Parameter ``cds_layout``: - The charge distribution surface layout to be evaluated. - -Parameter ``cds_canvas``: - The charge distribution surface of the canvas SiDBs. All possible - configurations are enumerated - -Returns: - The minimum energy value if a physically valid configuration is - found, `std::nullopt` otherwise.)doc"; - -static const char *__doc_fiction_detail_design_sidb_gates_impl_layout_can_be_pruned = -R"doc(This function evaluates whether the given layout can be discarded -since it cannot implement the given Boolean function. The pruning is -subdivided into three single pruning steps: (1) discarding SiDB -layouts with potentially positively charged SiDBs, (2) utilizing an -efficient method to identify and discard SiDB layouts that do not -satisfy physical model constraints under the I/O pin conditions -required for the desired Boolean function, and (3) detecting I/O -signal instability. - -Parameter ``current_layout``: - The layout being evaluated for pruning. - -Parameter ``canvas_lyt``: - The canvas layout comprising of the canvas SiDBs. - -Parameter ``dependent_cell``: - A dependent-cell of the canvas SiDBs. - -Returns: - `true` if the current layout can be pruned. `false` otherwise, - which means that the layout is a candidate to be a valid gate - implementation. Physical simulation is required as a second step - to conduct the final validation.)doc"; - -static const char *__doc_fiction_detail_design_sidb_gates_impl_num_threads = R"doc(Number of threads to be used for parallel execution.)doc"; - static const char *__doc_fiction_detail_design_sidb_gates_impl_number_of_discarded_layouts_at_first_pruning = R"doc(Number of discarded layouts at first pruning.)doc"; static const char *__doc_fiction_detail_design_sidb_gates_impl_number_of_discarded_layouts_at_second_pruning = R"doc(Number of discarded layouts at second pruning.)doc"; @@ -5130,6 +5062,8 @@ static const char *__doc_fiction_detail_design_sidb_gates_impl_number_of_input_w static const char *__doc_fiction_detail_design_sidb_gates_impl_number_of_output_wires = R"doc(Number of output BDL wires.)doc"; +static const char *__doc_fiction_detail_design_sidb_gates_impl_number_of_threads = R"doc(Number of threads to be used for the design process.)doc"; + static const char *__doc_fiction_detail_design_sidb_gates_impl_output_bdl_wires = R"doc(Output BDL wires.)doc"; static const char *__doc_fiction_detail_design_sidb_gates_impl_params = R"doc(Parameters for the *SiDB Gate Designer*.)doc"; @@ -5175,64 +5109,6 @@ parameters. The design process is parallelized to improve performance. Returns: A vector of designed SiDB gate layouts.)doc"; -static const char *__doc_fiction_detail_design_sidb_gates_impl_set_charge_distribution_of_input_wires_based_on_input_pattern = -R"doc(This function assigns the charge states of the input wires in the -layout according to the provided input pattern index. It performs the -following steps: - For `NORTH-SOUTH` port wires, if the corresponding -bit in the input pattern is set, assigns `NEUTRAL` charge to the upper -part and `NEGATIVE` charge to the lower part of the BDLs of the wire. -- For `NORTH-SOUTH` port wires, if the corresponding bit in the input -pattern is not set, assigns `NEGATIVE` charge to the upper part and -`NEUTRAL` charge to the lower part of the BDLs of the wire. - For -`SOUTH-NORTH` port wires, if the corresponding bit in the input -pattern is set, assigns `NEGATIVE` charge to the upper part and -`NEUTRAL` charge to the lower part of the BDLs of the wire. - For -`SOUTH-NORTH` port wires, if the corresponding bit in the input -pattern is not set, assigns `NEUTRAL` charge to the upper part and -`NEGATIVE` charge to the lower part of the BDLs of the wire. - -Parameter ``layout``: - The charge distribution surface layout to be modified. - -Parameter ``current_input_index``: - The index representing the current input pattern.)doc"; - -static const char *__doc_fiction_detail_design_sidb_gates_impl_set_charge_distribution_of_output_wires_based_on_output_index = -R"doc(This function assigns the charge states of the input wires in the -layout according to the provided input pattern index. It performs the -following steps: - For `NORTH-SOUTH` port wires, if the corresponding -bit in the input pattern is set, assigns `NEUTRAL` charge to the upper -part and `NEGATIVE` charge to the lower part of the BDLs of the wire. -- For `NORTH-SOUTH` port wires, if the corresponding bit in the input -pattern is not set, assigns `NEGATIVE` charge to the upper part and -`NEUTRAL` charge to the lower part of the BDLs of the wire. - For -`SOUTH-NORTH` port wires, if the corresponding bit in the input -pattern is set, assigns `NEGATIVE` charge to the upper part and -`NEUTRAL` charge to the lower part of the BDLs of the wire. - For -`SOUTH-NORTH` port wires, if the corresponding bit in the input -pattern is not set, assigns `NEUTRAL` charge to the upper part and -`NEGATIVE` charge to the lower part of the BDLs of the wire. - -Parameter ``layout``: - The charge distribution surface layout to be modified. - -Parameter ``current_input_index``: - The index representing the current input pattern. - -Returns: - void)doc"; - -static const char *__doc_fiction_detail_design_sidb_gates_impl_set_charge_distribution_of_output_wires_based_on_truth_table = -R"doc(This function assigns the charge states of the output wires in the -layout according to the values in the truth table for the provided -input pattern index. - -Parameter ``layout``: - The charge distribution surface layout to be modified. - -Parameter ``input_index``: - The index representing the current input pattern.)doc"; - static const char *__doc_fiction_detail_design_sidb_gates_impl_skeleton_layout = R"doc(The skeleton layout serves as a starting layout to which SiDBs are added to create unique SiDB layouts and, if possible, working gates. @@ -7149,12 +7025,12 @@ static const char *__doc_fiction_detail_is_fanout_substituted_impl_run = R"doc() static const char *__doc_fiction_detail_is_fanout_substituted_impl_substituted = R"doc()doc"; static const char *__doc_fiction_detail_is_operational_impl = -R"doc(Implementation of the `is_operational` algorithm for a given gate +R"doc(Implementation of the `is_operational` algorithm for a given SiDB layout. This class provides an implementation of the `is_operational` -algorithm for a specified gate layout and parameters. It checks -whether the gate layout is operational by simulating its behavior for +algorithm for a specified SiDB layout and parameters. It checks +whether the SiDB layout is operational by simulating its behavior for different input combinations and comparing the results to expected outputs from a truth table. @@ -7166,6 +7042,8 @@ Template parameter ``TT``: static const char *__doc_fiction_detail_is_operational_impl_bii = R"doc(Iterator that iterates over all possible input states.)doc"; +static const char *__doc_fiction_detail_is_operational_impl_canvas_lyt = R"doc(Layout consisting of all canvas SiDBs.)doc"; + static const char *__doc_fiction_detail_is_operational_impl_check_existence_of_kinks_in_input_wires = R"doc(This function iterates through the input wires and evaluates their charge states against the expected states derived from the input @@ -7173,9 +7051,6 @@ pattern. A kink is considered to exist if an input wire's charge state does not match the expected value (i.e., bit one or bit zero) for the given input index. -Template parameter ``Lyt``: - SiDB cell-level layout type - Parameter ``ground_state``: The ground state charge distribution surface. @@ -7194,9 +7069,6 @@ table. A kink is considered to exist if an output wire's charge state does not match the expected value (i.e., bit one or bit zero) for the given input index. -Template parameter ``Lyt``: - SiDB cell-level layout type - Parameter ``ground_state``: The ground state charge distribution surface. @@ -7208,6 +7080,8 @@ Parameter ``current_input_index``: `true` if any output wire contains a kink (i.e., an unexpected charge state), `false` otherwise.)doc"; +static const char *__doc_fiction_detail_is_operational_impl_dependent_cell = R"doc(Dependent cell of the canvas SiDBs.)doc"; + static const char *__doc_fiction_detail_is_operational_impl_determine_non_operational_input_patterns_and_non_operationality_reason = R"doc(Determines the input combinations for which the layout is non- operational and the reason why the layout is non-operational. @@ -7223,9 +7097,6 @@ static const char *__doc_fiction_detail_is_operational_impl_encodes_bit_one = R"doc(This function returns `true` if `1` is encoded in the charge state of the given BDL pair. `false` otherwise. -Template parameter ``Lyt``: - SiDB cell-level layout type. - Parameter ``ground_state``: The ground state charge distribution surface. @@ -7239,9 +7110,6 @@ static const char *__doc_fiction_detail_is_operational_impl_encodes_bit_zero = R"doc(This function returns `true` if `0` is encoded in the charge state of the given BDL pair. `false` otherwise. -Template parameter ``Lyt``: - SiDB cell-level layout type. - Parameter ``ground_state``: The ground state charge distribution surface. @@ -7259,6 +7127,58 @@ R"doc(Returns the total number of simulator invocations. static const char *__doc_fiction_detail_is_operational_impl_input_bdl_wires = R"doc(Input BDL wires.)doc"; +static const char *__doc_fiction_detail_is_operational_impl_is_io_signal_unstable = +R"doc(This function iterates through various input patterns and output wire +indices to determine if any configuration results in a physically +valid layout with energy below the given energy value, indicating I/O +signal instability. + +Parameter ``cds_layout``: + The charge distribution surface layout to be modified and checked. + +Parameter ``max_input_pattern_index``: + The maximum index for input pattern + +Parameter ``input_pattern``: + The specific input pattern for which the stability check is + conducted. + +Parameter ``logical_correct_output_pattern``: + The expected correct output pattern for the given input. + +Parameter ``minimal_energy_of_physically_valid_layout``: + The minimum energy threshold below which the layout is considered + unstable. + +Returns: + `true` if the I/O signal is unstable, `false` otherwise.)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_is_layout_invalid = +R"doc(This function evaluates whether the given layout is invalid, i.e., it +cannot implement the given Boolean function. This is done in three +separate filtering steps: (1) discarding SiDB layouts with potentially +positively charged SiDBs, (2) utilizing an efficient method to +identify and discard SiDB layouts that do not satisfy physical model +constraints under the I/O pin conditions required for the desired +Boolean function, and (3) detecting I/O signal instability. + +Template parameter ``ChargeLyt``: + The charge distribution surface layout type. + +Parameter ``input_pattern``: + The current input pattern. + +Parameter ``cds_canvas``: + The charge distribution of the canvas layout. + +Parameter ``dependent_cell``: + A dependent-cell of the canvas SiDBs. + +Returns: + A `layout_invalidity_reason` object indicating why the layout is + non-operational; or `std::nullopt` if it could not certainly be + determined to be in fact non-operational.)doc"; + static const char *__doc_fiction_detail_is_operational_impl_is_operational_impl = R"doc(Constructor to initialize the algorithm with a layout and parameters. @@ -7273,7 +7193,8 @@ Parameter ``params``: Parameters for the `is_operational` algorithm.)doc"; static const char *__doc_fiction_detail_is_operational_impl_is_operational_impl_2 = -R"doc(Constructor to initialize the algorithm with a layout and parameters. +R"doc(Constructor to initialize the algorithm with a layout, parameters, +input and output wires. Parameter ``lyt``: The SiDB cell-level layout to be checked. @@ -7289,10 +7210,67 @@ Parameter ``input_wires``: BDL input wires of lyt. Parameter ``output_wires``: - BDL output wires of lyt.)doc"; + BDL output wires of lyt. + +Parameter ``initialize_bii``: + If `true`, the BDL input iterator is initialized, `false` + otherwise. This parameter is only needed in special cases + (verify_logic_match.hpp).)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_is_operational_impl_3 = +R"doc(Constructor to initialize the algorithm with a layout, parameters, +input and output wires, and a canvas layout. + +Parameter ``lyt``: + The SiDB cell-level layout to be checked. + +Parameter ``spec``: + Expected Boolean function of the layout given as a multi-output + truth table. + +Parameter ``params``: + Parameters for the `is_operational` algorithm. + +Parameter ``input_wires``: + BDL input wires of lyt. + +Parameter ``output_wires``: + BDL output wires of lyt. + +Parameter ``c_lyt``: + Canvas layout.)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_is_operational_impl_4 = +R"doc(Constructor to initialize the algorithm with a layout and parameters. + +Parameter ``lyt``: + The SiDB cell-level layout to be checked. + +Parameter ``spec``: + Expected Boolean function of the layout given as a multi-output + truth table. + +Parameter ``params``: + Parameters for the `is_operational` algorithm.)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_is_physical_validity_feasible = +R"doc(This function determines if there is a charge distribution of the +canvas SiDBs for which the charge distribution of the whole layout is +physically valid. + +Parameter ``cds_layout``: + The charge distribution surface layout to be evaluated. + +Returns: + The minimum energy value if a physically valid configuration is + found, `std::nullopt` otherwise.)doc"; static const char *__doc_fiction_detail_is_operational_impl_layout = R"doc(SiDB cell-level layout.)doc"; +static const char *__doc_fiction_detail_is_operational_impl_number_of_input_wires = R"doc(Number of input BDL wires.)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_number_of_output_wires = R"doc(Number of output BDL wires.)doc"; + static const char *__doc_fiction_detail_is_operational_impl_output_bdl_pairs = R"doc(Output BDL pairs.)doc"; static const char *__doc_fiction_detail_is_operational_impl_output_bdl_wires = R"doc(Output BDL wires.)doc"; @@ -7300,14 +7278,12 @@ static const char *__doc_fiction_detail_is_operational_impl_output_bdl_wires = R static const char *__doc_fiction_detail_is_operational_impl_parameters = R"doc(Parameters for the `is_operational` algorithm.)doc"; static const char *__doc_fiction_detail_is_operational_impl_physical_simulation_of_layout = -R"doc(This function conducts physical simulation of the given layout (gate -layout with certain input combination). The simulation results are -stored in the `sim_result` variable. +R"doc(This function conducts physical simulation of the given SiDB layout. +The simulation results are stored in the `sim_result` variable. Parameter ``bdl_iterator``: - A reference to a BDL input iterator representing the gate layout - at a given input combination. The simulation is performed based on - the configuration represented by the iterator. + BDL input iterator representing the SiDB layout with a given input + combination. Returns: Simulation results.)doc"; @@ -7316,13 +7292,36 @@ static const char *__doc_fiction_detail_is_operational_impl_run = R"doc(Run the `is_operational` algorithm. This function executes the operational status checking algorithm for -the gate layout and parameters provided during initialization. +the given SiDB layout and parameters provided during initialization. Returns: Pair with the first element indicating the operational status (either `OPERATIONAL` or `NON_OPERATIONAL`) and the second element indicating the reason if it is non-operational.)doc"; +static const char *__doc_fiction_detail_is_operational_impl_set_charge_distribution_of_input_pins = +R"doc(This function assigns the charge states of the input pins in the +layout according to the input index provided. This means that when a +zero is applied, each BDL pair in the wire is set to zero. + +Parameter ``cds``: + The charge distribution surface layout to be modified. + +Parameter ``current_input_index``: + The index representing the current input pattern.)doc"; + +static const char *__doc_fiction_detail_is_operational_impl_set_charge_distribution_of_output_pins = +R"doc(This function assigns the charge states of the output pins in the +layout according to the input index provided. This means that when a +zero is applied, each BDL pair in the wire is set to zero. + +Parameter ``cds``: + The charge distribution surface layout to be modified. + +Parameter ``output_wire_index``: + The index representing the current input pattern of the output + wire.)doc"; + static const char *__doc_fiction_detail_is_operational_impl_simulator_invocations = R"doc(Number of simulator invocations.)doc"; static const char *__doc_fiction_detail_is_operational_impl_truth_table = R"doc(The specification of the layout.)doc"; @@ -7523,6 +7522,20 @@ Parameter ``c``: Parameter ``g_val``: New g-value for c.)doc"; +static const char *__doc_fiction_detail_layout_invalidity_reason = +R"doc(Reason why the layout is not a valid gate implementation for the given +Boolean function.)doc"; + +static const char *__doc_fiction_detail_layout_invalidity_reason_IO_INSTABILITY = +R"doc(I/O signals are unstable, indicating that an information flip results +in a lower energy state.)doc"; + +static const char *__doc_fiction_detail_layout_invalidity_reason_PHYSICAL_INFEASIBILITY = +R"doc(The layout is physically infeasible, meaning no charge distribution of +the canvas SiDBs satisfies the criteria for physical validity.)doc"; + +static const char *__doc_fiction_detail_layout_invalidity_reason_POTENTIAL_POSITIVE_CHARGES = R"doc(Positive SiDBs can potentially occur.)doc"; + static const char *__doc_fiction_detail_nested_vector_hash = R"doc(This struct defines a hash function for a nested vector of layout tiles. It calculates a combined hash value for a vector of tiles based @@ -7589,6 +7602,8 @@ static const char *__doc_fiction_detail_on_the_fly_circuit_design_impl_stats = R static const char *__doc_fiction_detail_operational_domain_impl = R"doc()doc"; +static const char *__doc_fiction_detail_operational_domain_impl_canvas_lyt = R"doc(This layout consists of the canvas cells of the layout.)doc"; + static const char *__doc_fiction_detail_operational_domain_impl_contour_tracing = R"doc(Performs contour tracing to determine the operational domain. The algorithm first performs a random sampling of up to the specified @@ -7734,6 +7749,8 @@ static const char *__doc_fiction_detail_operational_domain_impl_inferred_op_doma R"doc(All the points inferred (assumed) to be operational but not actually simulated.)doc"; +static const char *__doc_fiction_detail_operational_domain_impl_input_bdl_wires = R"doc(Input BDL wires.)doc"; + static const char *__doc_fiction_detail_operational_domain_impl_is_step_point_inferred_operational = R"doc(Checks whether the given step point is part of the inferred operational domain. If it is, the point is marked as enclosed in the @@ -7873,6 +7890,8 @@ Parameter ``st``: static const char *__doc_fiction_detail_operational_domain_impl_output_bdl_pairs = R"doc(The output BDL pairs of the layout.)doc"; +static const char *__doc_fiction_detail_operational_domain_impl_output_bdl_wires = R"doc(Output BDL wires.)doc"; + static const char *__doc_fiction_detail_operational_domain_impl_params = R"doc(The parameters for the operational domain computation.)doc"; static const char *__doc_fiction_detail_operational_domain_impl_random_sampling = @@ -9932,7 +9951,7 @@ displacement in the y-direction.)doc"; static const char *__doc_fiction_displacement_robustness_domain_params_fixed_sidbs = R"doc(SiDBs in the given layout which shall not be affected by variations.)doc"; -static const char *__doc_fiction_displacement_robustness_domain_params_operational_params = R"doc(Parameters to check the operation status of the SiDB layout.)doc"; +static const char *__doc_fiction_displacement_robustness_domain_params_operational_params = R"doc(Parameters to check the operational status of the SiDB layout.)doc"; static const char *__doc_fiction_displacement_robustness_domain_params_percentage_of_analyzed_displaced_layouts = R"doc(This parameter defines the percentage of all possible displaced SiDB @@ -10286,6 +10305,19 @@ Template parameter ``Dist``: static const char *__doc_fiction_euclidean_distance_functor_euclidean_distance_functor = R"doc()doc"; +static const char *__doc_fiction_evaluate_output = +R"doc(This function evaluates the given multi-output truth table at the +given input index. + +Parameter ``truth_tables``: + The truth tables to evaluate. + +Parameter ``current_input_index``: + The index representing the current input pattern. + +Returns: + Output of the truth tables.)doc"; + static const char *__doc_fiction_even_column_cartesian = R"doc(\verbatim +-------+ +-------+ | | | | +-------+ (1,0) +-------+ (3,0) | | | | | | | (0,0) +-------+ (2,0) +-------+ | | | | | +-------+ @@ -14107,6 +14139,36 @@ considered as operational, if kinks were accepted. status being exclusively caused by kinks with an otherwise correct logic match. +Template parameter ``Lyt``: + SiDB cell-level layout type. + +Template parameter ``TT``: + Type of the truth table. + +Parameter ``lyt``: + The SiDB cell-level layout to be checked. + +Parameter ``spec``: + Expected Boolean function of the layout given as a multi-output + truth table. + +Parameter ``params``: + Parameters for the `is_operational` algorithm. + +Returns: + Bool that indicates whether kinks induce the layout to become non- + operational. `true` if the layout is non-operational due to kinks, + `false` otherwise.)doc"; + +static const char *__doc_fiction_is_kink_induced_non_operational_2 = +R"doc(This function determines if the layout is only considered as non- +operational because of kinks. This means that the layout would be +considered as operational, if kinks were accepted. + +@note "Kink induced non-operational" refers to the non-operational +status being exclusively caused by kinks with an otherwise correct +logic match. + Template parameter ``Lyt``: SiDB cell-level layout type. @@ -14129,6 +14191,9 @@ Parameter ``input_bdl_wire``: Parameter ``output_bdl_wire``: Optional BDL output wires of lyt. +Parameter ``canvas_lyt``: + Optional canvas layout. + Returns: Bool that indicates whether kinks induce the layout to become non- operational. `true` if the layout is non-operational due to kinks, @@ -14187,8 +14252,37 @@ Parameter ``defect``: static const char *__doc_fiction_is_operational = R"doc(Determine the operational status of an SiDB layout. -This function checks the operational status of a given gate layout -using the `is_operational` algorithm. It determines whether the gate +This function checks the operational status of a given SiDB layout +using the `is_operational` algorithm. It determines whether the SiDB +layout is operational and returns the correct result for all +:math:`2^n` input combinations. + +Template parameter ``Lyt``: + SiDB cell-level layout type. + +Template parameter ``TT``: + Type of the truth table. + +Parameter ``lyt``: + The SiDB cell-level layout to be checked. + +Parameter ``spec``: + Expected Boolean function of the layout given as a multi-output + truth table. + +Parameter ``params``: + Parameters for the `is_operational` algorithm. + +Returns: + A pair containing the operational status of the SiDB layout + (either `OPERATIONAL` or `NON_OPERATIONAL`) and the number of + input combinations tested.)doc"; + +static const char *__doc_fiction_is_operational_2 = +R"doc(Determine the operational status of an SiDB layout. + +This function checks the operational status of a given SiDB layout +using the `is_operational` algorithm. It determines whether the SiDB layout is operational and returns the correct result for all :math:`2^n` input combinations. @@ -14214,8 +14308,11 @@ Parameter ``input_bdl_wire``: Parameter ``output_bdl_wire``: Optional BDL output wires of lyt. +Parameter ``canvas_lyt``: + Optional canvas layout. + Returns: - A pair containing the operational status of the gate layout + A pair containing the operational status of the SiDB layout (either `OPERATIONAL` or `NON_OPERATIONAL`) and the number of input combinations tested.)doc"; @@ -14224,7 +14321,53 @@ static const char *__doc_fiction_is_operational_params = R"doc(Parameters for th static const char *__doc_fiction_is_operational_params_input_bdl_iterator_params = R"doc(Parameters for the BDL input iterator.)doc"; static const char *__doc_fiction_is_operational_params_op_condition = -R"doc(Condition which is used to decide if a layout is operational or non- +R"doc(Condition to decide whether a layout is operational or non- +operational.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_analysis_strategy = +R"doc(Simulation method to determine if the layout is operational or non- +operational. There are three possible strategies: + +- `SIMULATION_ONLY`: This setting does not apply any filtering +strategies to determine if the layout is operational. Instead, it +relies solely on physical simulation to make this determination. - +`FILTER_ONLY`: This setting does only apply filtering strategies to +determine if the layout is non-operational. If the layout passes all +filtering strategies, it is considered operational. This is only an +approximation. It may be possible that the layout is non-operational, +but the filtering strategies do not detect it. - +`FILTER_THEN_SIMULATION`: Before a physical simulation is conducted, +the algorithm checks if filtering strategies have detected whether the +layout is non-operational. This only provides any runtime benefits if +kinks are rejected.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_analysis_strategy_FILTER_ONLY = +R"doc(Apply filtering exclusively to determine whether the layout is non- +operational. If the layout passes all filter steps, it is considered +operational. + +@note This is an extremely fast approximation that may sometimes lead +to false positives.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_analysis_strategy_FILTER_THEN_SIMULATION = +R"doc(Before a physical simulation is conducted, the algorithm checks if +filter strategies can determine that the layout is non-operational. +This only provides any runtime benefits if kinks are rejected.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_analysis_strategy_SIMULATION_ONLY = +R"doc(Do not apply filter strategies to determine whether the layout is +operational. Instead, rely solely on physical simulation.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_condition = +R"doc(Condition to decide whether a layout is operational or non- +operational.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_condition_REJECT_KINKS = +R"doc(The I/O pins are not allowed to show kinks. If kinks exist, the layout +is considered as non-operational.)doc"; + +static const char *__doc_fiction_is_operational_params_operational_condition_TOLERATE_KINKS = +R"doc(Even if the I/O pins show kinks, the layout is still considered as operational.)doc"; static const char *__doc_fiction_is_operational_params_sim_engine = @@ -14235,6 +14378,10 @@ static const char *__doc_fiction_is_operational_params_simulation_parameters = R"doc(The simulation parameters for the physical simulation of the ground state.)doc"; +static const char *__doc_fiction_is_operational_params_strategy_to_analyze_operational_status = +R"doc(Strategy to determine whether a layout is operational or non- +operational.)doc"; + static const char *__doc_fiction_is_positively_charged_defect = R"doc(Checks whether the given defect has a positive charge value assigned to it. This function is irrespective of the associated defect type. @@ -14392,6 +14539,43 @@ Parameter ``params``: The input combinations where kinks induce the SiDB layout to become non-operational.)doc"; +static const char *__doc_fiction_kink_induced_non_operational_input_patterns_2 = +R"doc(This function determines all input combinations for which kinks induce +the SiDB layout to become non-operational. This means that the layout +is operational if kinks would be accepted. + +@note "Kink induced non-operational" refers to the non-operational +status being exclusively caused by kinks with an otherwise correct +logic match. + +Template parameter ``Lyt``: + SiDB cell-level layout type. + +Template parameter ``TT``: + Type of the truth table. + +Parameter ``lyt``: + The SiDB layout. + +Parameter ``spec``: + Vector of truth table specifications. + +Parameter ``params``: + Parameters for the `is_operational` algorithm. + +Parameter ``input_bdl_wire``: + Optional BDL input wires of lyt. + +Parameter ``output_bdl_wire``: + Optional BDL output wires of lyt. + +Parameter ``canvas_lyt``: + Optional canvas layout. + +Returns: + The input combinations where kinks induce the SiDB layout to + become non-operational.)doc"; + static const char *__doc_fiction_layout_coordinate_path = R"doc(A path in a layout defined as an ordered sequence of coordinates. @@ -14991,18 +15175,6 @@ Parameter ``n``: Returns: Irregular clocking scheme.)doc"; -static const char *__doc_fiction_operational_condition = -R"doc(Condition which is used to decide if a layout is operational or non- -operational.)doc"; - -static const char *__doc_fiction_operational_condition_REJECT_KINKS = -R"doc(The I/O pins are not allowed to show kinks. If kinks exist, the layout -is considered as non-operational.)doc"; - -static const char *__doc_fiction_operational_condition_TOLERATE_KINKS = -R"doc(Even if the I/O pins show kinks, the layout is still considered as -operational.)doc"; - static const char *__doc_fiction_operational_domain = R"doc(An operational domain is a set of simulation parameter values for which a given SiDB layout is logically operational. This means that a @@ -15376,6 +15548,37 @@ Parameter ``params``: Returns: The count of operational input combinations.)doc"; +static const char *__doc_fiction_operational_input_patterns_2 = +R"doc(This function determines the input combinations for which the layout +is operational. + +Template parameter ``Lyt``: + SiDB cell-level layout type. + +Template parameter ``TT``: + Type of the truth table. + +Parameter ``lyt``: + The SiDB layout. + +Parameter ``spec``: + Vector of truth table specifications. + +Parameter ``params``: + Parameters to simulate if a input combination is operational. + +Parameter ``input_bdl_wire``: + Optional BDL input wires of lyt. + +Parameter ``output_bdl_wire``: + Optional BDL output wires of lyt. + +Parameter ``canvas_lyt``: + Optional canvas layout. + +Returns: + The count of operational input combinations.)doc"; + static const char *__doc_fiction_operational_status = R"doc(Possible operational status of a layout.)doc"; static const char *__doc_fiction_operational_status_NON_OPERATIONAL = R"doc(The layout is non-operational.)doc"; diff --git a/bindings/mnt/pyfiction/test/algorithms/simulation/sidb/test_is_operational.py b/bindings/mnt/pyfiction/test/algorithms/simulation/sidb/test_is_operational.py index db587aeb6..f995637c8 100644 --- a/bindings/mnt/pyfiction/test/algorithms/simulation/sidb/test_is_operational.py +++ b/bindings/mnt/pyfiction/test/algorithms/simulation/sidb/test_is_operational.py @@ -2,7 +2,10 @@ import unittest from mnt.pyfiction import ( + bdl_wire_selection, create_and_tt, + detect_bdl_wires_100, + detect_bdl_wires_params, is_kink_induced_non_operational, is_operational, is_operational_params, @@ -54,6 +57,23 @@ def test_is_operational(self): self.assertEqual(op_status, operational_status.NON_OPERATIONAL) + # pre-determined I/O pins + output_bdl_wires = detect_bdl_wires_100(lyt, detect_bdl_wires_params(), bdl_wire_selection.OUTPUT) + input_bdl_wires = detect_bdl_wires_100(lyt, detect_bdl_wires_params(), bdl_wire_selection.INPUT) + [op_status, _evaluated_input_combinations] = is_operational( + lyt, [create_and_tt()], params, input_bdl_wires, output_bdl_wires + ) + self.assertEqual(op_status, operational_status.NON_OPERATIONAL) + + # pre-determined I/O pins and canvas layout + canvas_lyt = sidb_100_lattice() + canvas_lyt.assign_cell_type((4, 5), sidb_technology.cell_type.LOGIC) + canvas_lyt.assign_cell_type((6, 7), sidb_technology.cell_type.LOGIC) + [op_status, _evaluated_input_combinations] = is_operational( + lyt, [create_and_tt()], params, input_bdl_wires, output_bdl_wires + ) + self.assertEqual(op_status, operational_status.NON_OPERATIONAL) + def test_and_gate_kinks(self): lyt = read_sqd_layout_100(dir_path + "/../../../resources/AND_mu_032_kinks.sqd") diff --git a/docs/algorithms/sidb_simulation.rst b/docs/algorithms/sidb_simulation.rst index 2fb12360e..297e30454 100644 --- a/docs/algorithms/sidb_simulation.rst +++ b/docs/algorithms/sidb_simulation.rst @@ -220,13 +220,16 @@ Operational Domain Computation **Header:** ``fiction/algorithms/simulation/sidb/is_operational.hpp`` .. doxygenenum:: fiction::operational_status - .. doxygenenum:: fiction::operational_condition .. doxygenstruct:: fiction::is_operational_params :members: - .. doxygenfunction:: fiction::is_operational - .. doxygenfunction:: fiction::operational_input_patterns - .. doxygenfunction:: fiction::is_kink_induced_non_operational - .. doxygenfunction:: fiction::kink_induced_non_operational_input_patterns + .. doxygenfunction:: fiction::is_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}) + .. doxygenfunction:: fiction::is_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, const std::optional& canvas_lyt = std::nullopt) + .. doxygenfunction:: fiction::operational_input_patterns(const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}) + .. doxygenfunction:: fiction::operational_input_patterns(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, const std::optional& canvas_lyt = std::nullopt) + .. doxygenfunction:: fiction::is_kink_induced_non_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}) + .. doxygenfunction:: fiction::is_kink_induced_non_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, const std::optional& canvas_lyt = std::nullopt) + .. doxygenfunction:: fiction::kink_induced_non_operational_input_patterns(const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}) + .. doxygenfunction:: fiction::kink_induced_non_operational_input_patterns(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, const std::optional& canvas_lyt = std::nullopt) **Header:** ``fiction/algorithms/simulation/sidb/operational_domain.hpp`` diff --git a/docs/changelog.rst b/docs/changelog.rst index 0c552d6be..00833b8dd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,9 @@ Unreleased Added ##### +- Algorithms: + - Simulation: + - Added option to determine efficiently if a layout is non-operational by using QuickCell's pruning strategies. - Experiments: - Added rectangular SiDB gate skeletons and a script for designing a corresponding library. - Added information about how many layouts remain after each of QuickCell's pruning steps. diff --git a/experiments/operational_domain/operational_domain_bestagon.cpp b/experiments/operational_domain/operational_domain_bestagon.cpp index a1f68e066..b2affe84d 100644 --- a/experiments/operational_domain/operational_domain_bestagon.cpp +++ b/experiments/operational_domain/operational_domain_bestagon.cpp @@ -61,6 +61,9 @@ int main() // NOLINT operational_domain_params op_domain_params{}; op_domain_params.operational_params.simulation_parameters = sim_params; op_domain_params.operational_params.sim_engine = sidb_simulation_engine::QUICKEXACT; + + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::TOLERATE_KINKS; + op_domain_params.sweep_dimensions = {{sweep_parameter::EPSILON_R}, {sweep_parameter::LAMBDA_TF}}; op_domain_params.sweep_dimensions[0].min = 1.0; op_domain_params.sweep_dimensions[0].max = 10.0; diff --git a/experiments/operational_domain/operational_domain_bestagon_exact_vs_sketch.cpp b/experiments/operational_domain/operational_domain_bestagon_exact_vs_sketch.cpp new file mode 100644 index 000000000..702488bd3 --- /dev/null +++ b/experiments/operational_domain/operational_domain_bestagon_exact_vs_sketch.cpp @@ -0,0 +1,127 @@ +// +// Created by Jan Drewniok on 15.12.24. +// + +#include "fiction_experiments.hpp" // experiment class + +#include // operational domain computation algorithms +#include // SiDB simulation engines +#include // SiDB simulation parameters +#include // reader for SiDB layouts +#include // writer for operational domains +#include // pre-defined types suitable for the FCN domain +#include // truth tables helper functions + +#include // output formatting +#include // stopwatch for time measurement + +#include +#include +#include +#include +#include +#include + +using namespace fiction; + +// This script compares the exact operational domain computation of Bestagon gates with an approximate algorithm. The +// exact method employs a grid search approach, while the approximate algorithm estimates the operational domain by +// identifying non-operational parameter points through filtering strategies. For the remaining parameter points, the +// algorithm assumes an operational status, resulting in an approximation referred to as the operational domain sketch. + +int main() // NOLINT +{ + experiments::experiment opdomain_exp{ + "Operational Domain Bestagon Exact vs Sketch", + "Name", + "#SiDBs", // Benchmark + "num op exact", + "t in s (exact)", + "num op sketch", + "t in s (sketch)", + "num op sketch / num op exact", + "t in s (exact) / t in s (sketch)"}; + + // simulation parameters + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + sim_params.mu_minus = -0.32; + + // operational domain parameters + operational_domain_params op_domain_params{}; + op_domain_params.operational_params.simulation_parameters = sim_params; + op_domain_params.operational_params.sim_engine = sidb_simulation_engine::QUICKEXACT; + + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + + op_domain_params.sweep_dimensions = {{sweep_parameter::EPSILON_R}, {sweep_parameter::LAMBDA_TF}}; + op_domain_params.sweep_dimensions[0].min = 1.0; + op_domain_params.sweep_dimensions[0].max = 10.0; + op_domain_params.sweep_dimensions[0].step = 0.05; + op_domain_params.sweep_dimensions[1].min = 1.0; + op_domain_params.sweep_dimensions[1].max = 10.0; + op_domain_params.sweep_dimensions[1].step = 0.05; + + static const std::string folder = fmt::format("{}sidb_gate_libraries/bestagon_gates/", EXPERIMENTS_PATH); + + const auto truth_tables_and_names = + std::array, std::string>, 11>{{{std::vector{create_id_tt()}, "wire"}, + {std::vector{create_not_tt()}, "inv"}, + {std::vector{create_and_tt()}, "and"}, + {std::vector{create_nand_tt()}, "nand"}, + {std::vector{create_or_tt()}, "or"}, + {std::vector{create_nor_tt()}, "nor"}, + {std::vector{create_xor_tt()}, "xor"}, + {std::vector{create_xnor_tt()}, "xnor"}, + {create_crossing_wire_tt(), "cx"}, + {create_half_adder_tt(), "ha"}, + {create_double_wire_tt(), "hourglass"}}}; + + for (const auto& [truth_table, gate] : truth_tables_and_names) + { + const auto lyt = read_sqd_layout(fmt::format("{}/{}.sqd", folder, gate), gate); + + // operational domain stats + operational_domain_stats op_domain_stats_gs_exact{}; + operational_domain_stats op_domain_stats_sketch{}; + + op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::SIMULATION_ONLY; + + const auto op_domain_gs_exact = + operational_domain_grid_search(lyt, truth_table, op_domain_params, &op_domain_stats_gs_exact); + + op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + const auto op_domain_gs_sketch = + operational_domain_grid_search(lyt, truth_table, op_domain_params, &op_domain_stats_sketch); + + write_operational_domain(op_domain_gs_exact, fmt::format("{}/exact_{}.csv", folder, gate)); + write_operational_domain(op_domain_gs_sketch, fmt::format("{}/sketch_{}.csv", folder, gate)); + + opdomain_exp( + // Benchmark + gate, lyt.num_cells(), + + // Exact Operational Domain (determine the operation status by simulation) + op_domain_stats_gs_exact.num_operational_parameter_combinations, + mockturtle::to_seconds(op_domain_stats_gs_exact.time_total), + + // Operational Domain Sketch (determine the operation status by pruning) + op_domain_stats_sketch.num_operational_parameter_combinations, + mockturtle::to_seconds(op_domain_stats_sketch.time_total), + static_cast(op_domain_stats_sketch.num_operational_parameter_combinations) / + static_cast(op_domain_stats_gs_exact.num_operational_parameter_combinations), + mockturtle::to_seconds(op_domain_stats_gs_exact.time_total) / + mockturtle::to_seconds(op_domain_stats_sketch.time_total)); + + opdomain_exp.save(); + opdomain_exp.table(); + } + + opdomain_exp.save(); + opdomain_exp.table(); + + return EXIT_SUCCESS; +} diff --git a/experiments/quickcell/quickcell_3_input.cpp b/experiments/quickcell/quickcell_3_input.cpp index 118173de7..971f66bc4 100644 --- a/experiments/quickcell/quickcell_3_input.cpp +++ b/experiments/quickcell/quickcell_3_input.cpp @@ -32,18 +32,21 @@ using namespace fiction; int main() // NOLINT { experiments::experiment - simulation_exp{"benchmark", - "gate", // std::string - "#Total Layouts", // uint64_t - "#Gates (QuickCell)", // uint64_t - "runtime (QuickCell) [s]", // double - "#Lp1", // uint64_t - "#Lp1/N [%]", // double - "#Lp2", // uint64_t - "#Lp2/N [%]", // double - "#Lp3", // uint64_t - "#Lp3/N [%]"}; // double + double, double> + simulation_exp{ + "benchmark", + "gate", // std::string + "#Total Layouts", // uint64_t + "#Gates (QuickCell)", // uint64_t + "runtime (QuickCell) [s]", // double + "#Lp1", // uint64_t + "#Lp1/N [%]", // double + "#Lp2", // uint64_t + "#Lp2/N [%]", // double + "#Lp3", // uint64_t + "#Lp3/N [%]", // double + "t_pruning [s]" // double + }; const auto truth_tables_and_names = std::array, std::string>, 10>{{{std::vector{create_and3_tt()}, "and3"}, @@ -70,7 +73,7 @@ int main() // NOLINT const design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.31}, sidb_simulation_engine::QUICKEXACT, bdl_input_iterator_params{detect_bdl_wires_params{3.0}}, - operational_condition::REJECT_KINKS}, + is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::QUICKCELL, {{22, 6, 0}, {32, 12, 0}}, 4}; @@ -102,7 +105,8 @@ int main() // NOLINT static_cast(stats_quickcell.number_of_layouts), stats_quickcell.number_of_layouts_after_third_pruning, 100.0 * static_cast(stats_quickcell.number_of_layouts_after_third_pruning) / - static_cast(stats_quickcell.number_of_layouts)); + static_cast(stats_quickcell.number_of_layouts), + mockturtle::to_seconds(stats_quickcell.pruning_total)); simulation_exp.save(); simulation_exp.table(); diff --git a/experiments/quickcell/quickcell_rectangular_gate_library.cpp b/experiments/quickcell/quickcell_rectangular_gate_library.cpp index 6aab1af11..fdba10261 100644 --- a/experiments/quickcell/quickcell_rectangular_gate_library.cpp +++ b/experiments/quickcell/quickcell_rectangular_gate_library.cpp @@ -94,7 +94,8 @@ int main() // NOLINT design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{{3}}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{{3}}, + is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::QUICKCELL, {{18, 9, 0}, {26, 13, 0}}, num_canvas_sidbs}; diff --git a/experiments/quickcell/quickcell_vs_automatic_exhaustive_2_input.cpp b/experiments/quickcell/quickcell_vs_automatic_exhaustive_2_input.cpp index c238e7c79..12893f92b 100644 --- a/experiments/quickcell/quickcell_vs_automatic_exhaustive_2_input.cpp +++ b/experiments/quickcell/quickcell_vs_automatic_exhaustive_2_input.cpp @@ -32,7 +32,7 @@ using namespace fiction; int main() // NOLINT { experiments::experiment + uint64_t, double, uint64_t, double, double> simulation_exp{"benchmark", "gate", // std::string "#Total Layouts", // uint64_t @@ -46,11 +46,12 @@ int main() // NOLINT "#Lp2", // uint64_t "#Lp2/N [%]", // double "#Lp3", // uint64_t - "#Lp3/N [%]"}; // double + "#Lp3/N [%]", // double + "t_pruning [s]"}; // double const auto truth_tables_and_names = std::array, std::string>, 15>{ - {{std::vector{create_id_tt()}, "inv"}, - {std::vector{create_not_tt()}, "wire"}, + {{std::vector{create_not_tt()}, "inv"}, + {std::vector{create_id_tt()}, "wire"}, {std::vector{create_and_tt()}, "and"}, {std::vector{create_nand_tt()}, "nand"}, {std::vector{create_or_tt()}, "or"}, @@ -78,7 +79,7 @@ int main() // NOLINT design_sidb_gates_params> params_1_in_1_out_straight{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER, {{9, 6, 0}, {21, 14, 0}}, @@ -86,7 +87,7 @@ int main() // NOLINT design_sidb_gates_params> params_2_in_1_out{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER, {{14, 6, 0}, {24, 10, 0}}, @@ -94,7 +95,7 @@ int main() // NOLINT design_sidb_gates_params> params_2_in_2_out{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER, {{14, 6, 0}, {24, 14, 0}}, @@ -111,14 +112,15 @@ int main() // NOLINT params_2_in_1_out.design_mode = design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER; - params_2_in_1_out.operational_params.op_condition = operational_condition::REJECT_KINKS; + params_2_in_1_out.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; params_2_in_2_out.design_mode = design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER; - params_2_in_2_out.operational_params.op_condition = operational_condition::REJECT_KINKS; + params_2_in_2_out.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; params_1_in_1_out_straight.design_mode = design_sidb_gates_params< fiction::cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER; - params_1_in_1_out_straight.operational_params.op_condition = operational_condition::REJECT_KINKS; + params_1_in_1_out_straight.operational_params.op_condition = + is_operational_params::operational_condition::REJECT_KINKS; if (gate_name == "cx" || gate_name == "ha" || gate_name == "hourglass") { @@ -186,7 +188,8 @@ int main() // NOLINT static_cast(total_number_of_layout) * 100, stats_quickcell.number_of_layouts_after_third_pruning, static_cast(stats_quickcell.number_of_layouts_after_third_pruning) / - static_cast(total_number_of_layout) * 100); + static_cast(total_number_of_layout) * 100, + mockturtle::to_seconds(stats_quickcell.pruning_total)); simulation_exp.save(); simulation_exp.table(); @@ -195,7 +198,7 @@ int main() // NOLINT const auto total_time_reduction = sum_exhaustive_runtime / sum_quickcell_runtime; simulation_exp("", 0, sum_exhaustive_runtime, 0, 0, sum_quickcell_runtime, total_time_reduction, 0, 0.0, 0, 0.0, 0, - 0.0); + 0.0, 0.0); simulation_exp.save(); simulation_exp.table(); diff --git a/experiments/sidb_gate_libraries/bestagon_gates/cx.sqd b/experiments/sidb_gate_libraries/bestagon_gates/cx.sqd index acdd1be5f..51e74441c 100644 --- a/experiments/sidb_gate_libraries/bestagon_gates/cx.sqd +++ b/experiments/sidb_gate_libraries/bestagon_gates/cx.sqd @@ -129,7 +129,7 @@ 2 - output + logic #ffc8c8c8 diff --git a/experiments/sidb_gate_libraries/bestagon_gates/inv.sqd b/experiments/sidb_gate_libraries/bestagon_gates/inv.sqd index 97fb5671f..e58169fe3 100644 --- a/experiments/sidb_gate_libraries/bestagon_gates/inv.sqd +++ b/experiments/sidb_gate_libraries/bestagon_gates/inv.sqd @@ -116,7 +116,6 @@ 2 - logic #ffc8c8c8 diff --git a/include/fiction/algorithms/iter/bdl_input_iterator.hpp b/include/fiction/algorithms/iter/bdl_input_iterator.hpp index 1630dcee2..1c1057566 100644 --- a/include/fiction/algorithms/iter/bdl_input_iterator.hpp +++ b/include/fiction/algorithms/iter/bdl_input_iterator.hpp @@ -10,6 +10,7 @@ #include "fiction/technology/cell_technologies.hpp" #include "fiction/traits.hpp" +#include #include #include #include @@ -340,6 +341,15 @@ class bdl_input_iterator { return input_pairs.size(); } + /** + * Returns the current input index. + * + * @return The current input index. + */ + [[nodiscard]] uint64_t get_current_input_index() const noexcept + { + return current_input_index; + } private: /** @@ -425,6 +435,8 @@ class bdl_input_iterator */ void set_all_inputs() noexcept { + assert(num_inputs == input_bdl_wires.size() && "number of inputs and number of wires don't match"); + for (uint64_t i = num_inputs - 1; i < num_inputs; --i) { const auto& input_i = input_pairs[i]; diff --git a/include/fiction/algorithms/physical_design/design_sidb_gates.hpp b/include/fiction/algorithms/physical_design/design_sidb_gates.hpp index 991b23fbe..a57e5eb8f 100644 --- a/include/fiction/algorithms/physical_design/design_sidb_gates.hpp +++ b/include/fiction/algorithms/physical_design/design_sidb_gates.hpp @@ -13,7 +13,6 @@ #include "fiction/technology/cell_ports.hpp" #include "fiction/technology/cell_technologies.hpp" #include "fiction/technology/charge_distribution_surface.hpp" -#include "fiction/technology/physical_constants.hpp" #include "fiction/technology/sidb_charge_state.hpp" #include "fiction/technology/sidb_defects.hpp" #include "fiction/traits.hpp" @@ -27,15 +26,11 @@ #include #include -#include #include -#include #include #include #include -#include #include -#include #include #include #include @@ -119,6 +114,10 @@ struct design_sidb_gates_stats * The total runtime of SiDB gate design process. */ mockturtle::stopwatch<>::duration time_total{0}; + /** + * The runtime of the pruning process. + */ + mockturtle::stopwatch<>::duration pruning_total{0}; /** * The simulation engine to be used for the operational domain computation. */ @@ -236,9 +235,8 @@ class design_sidb_gates_impl // canvas SiDBs are added to the skeleton const auto layout_with_added_cells = skeleton_layout_with_canvas_sidbs(combination); - if (const auto [status, sim_calls] = - is_operational(layout_with_added_cells, truth_table, params.operational_params, - std::optional{input_bdl_wires}, std::optional{output_bdl_wires}); + if (const auto [status, sim_calls] = is_operational( + layout_with_added_cells, truth_table, params.operational_params, input_bdl_wires, output_bdl_wires); status == operational_status::OPERATIONAL) { { @@ -256,16 +254,14 @@ class design_sidb_gates_impl } }; - const std::size_t number_of_used_threads = - std::min(static_cast(num_threads), all_combinations.size()); + const std::size_t num_threads = std::min(number_of_threads, all_combinations.size()); - const std::size_t chunk_size = - (all_combinations.size() + number_of_used_threads - 1) / number_of_used_threads; // Ceiling division + const std::size_t chunk_size = (all_combinations.size() + num_threads - 1) / num_threads; // Ceiling division std::vector threads{}; - threads.reserve(number_of_used_threads); + threads.reserve(num_threads); - for (std::size_t i = 0; i < number_of_used_threads; ++i) + for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back( [i, chunk_size, &all_combinations, &add_combination_to_layout_and_check_operation, &solution_found, @@ -315,6 +311,8 @@ class design_sidb_gates_impl params.canvas, params.number_of_sidbs, generate_random_sidb_layout_params>::positive_charges::ALLOWED}; + const auto num_threads = std::min(number_of_threads, all_canvas_layouts.size()); + std::vector threads{}; threads.reserve(num_threads); @@ -342,9 +340,8 @@ class design_sidb_gates_impl } }); } - if (const auto [status, sim_calls] = - is_operational(result_lyt, truth_table, params.operational_params, - std::optional{input_bdl_wires}, std::optional{output_bdl_wires}); + if (const auto [status, sim_calls] = is_operational( + result_lyt, truth_table, params.operational_params, input_bdl_wires, output_bdl_wires); status == operational_status::OPERATIONAL) { const std::lock_guard lock{mutex_to_protect_designed_gate_layouts}; @@ -387,7 +384,14 @@ class design_sidb_gates_impl [[nodiscard]] std::vector run_quickcell() noexcept { mockturtle::stopwatch stop{stats.time_total}; - const auto gate_candidates = run_pruning(); + + std::vector gate_candidates{}; + gate_candidates.reserve(all_canvas_layouts.size()); + + { + mockturtle::stopwatch stop_pruning{stats.pruning_total}; + gate_candidates = run_pruning(); + } stats.number_of_layouts_after_first_pruning = all_canvas_layouts.size() - number_of_discarded_layouts_at_first_pruning.load(); @@ -408,12 +412,12 @@ class design_sidb_gates_impl gate_layouts.reserve(gate_candidates.size()); - const std::size_t number_of_threads = - std::min(static_cast(std::thread::hardware_concurrency()), gate_candidates.size()); + const std::size_t num_threads = std::min(number_of_threads, gate_candidates.size()); + const std::size_t chunk_size = (gate_candidates.size() + num_threads - 1) / num_threads; // Ceiling division std::vector threads; - threads.reserve(number_of_threads); + threads.reserve(num_threads); std::atomic gate_design_found = false; @@ -426,9 +430,13 @@ class design_sidb_gates_impl { return; } - if (const auto [status, sim_calls] = - is_operational(candidate, truth_table, params.operational_params, std::optional{input_bdl_wires}, - std::optional{output_bdl_wires}); + + // pruning was already conducted above. Hence, SIMULATION_ONLY is chosen. + params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::SIMULATION_ONLY; + + if (const auto [status, sim_calls] = is_operational(candidate, truth_table, params.operational_params, + input_bdl_wires, output_bdl_wires); status == operational_status::OPERATIONAL) { // Lock and update shared resources @@ -440,7 +448,7 @@ class design_sidb_gates_impl } }; - for (std::size_t i = 0; i < number_of_threads; ++i) + for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back( [this, i, chunk_size, &gate_candidates, &check_operational_status, &gate_design_found]() @@ -486,15 +494,11 @@ class design_sidb_gates_impl /** * Parameters for the *SiDB Gate Designer*. */ - const design_sidb_gates_params>& params; + design_sidb_gates_params> params; /** * All cells within the canvas. */ std::vector all_sidbs_in_canvas; - /** - * Number of threads to be used for parallel execution. - */ - const std::size_t num_threads{std::thread::hardware_concurrency()}; /** * The statistics of the gate design. */ @@ -532,412 +536,9 @@ class design_sidb_gates_impl */ std::atomic number_of_discarded_layouts_at_third_pruning{0}; /** - * This function assigns the charge states of the input wires in the layout according to the provided input pattern - * index. It performs the following steps: - * - For `NORTH-SOUTH` port wires, if the corresponding bit in the input pattern is set, assigns `NEUTRAL` - * charge to the upper part and `NEGATIVE` charge to the lower part of the BDLs of the wire. - * - For `NORTH-SOUTH` port wires, if the corresponding bit in the input pattern is not set, assigns `NEGATIVE` - * charge to the upper part and `NEUTRAL` charge to the lower part of the BDLs of the wire. - * - For `SOUTH-NORTH` port wires, if the corresponding bit in the input pattern is set, assigns `NEGATIVE` - * charge to the upper part and `NEUTRAL` charge to the lower part of the BDLs of the wire. - * - For `SOUTH-NORTH` port wires, if the corresponding bit in the input pattern is not set, assigns `NEUTRAL` - * charge to the upper part and `NEGATIVE` charge to the lower part of the BDLs of the wire. - * - * @param layout The charge distribution surface layout to be modified. - * @param current_input_index The index representing the current input pattern. - */ - void - set_charge_distribution_of_input_wires_based_on_input_pattern(charge_distribution_surface& layout, - const uint64_t current_input_index) const noexcept - { - layout.assign_all_charge_states(sidb_charge_state::NEGATIVE, charge_index_mode::KEEP_CHARGE_INDEX); - - for (auto i = 0u; i < number_of_input_wires; i++) - { - if (input_bdl_wires[number_of_input_wires - 1 - i].port.dir == port_direction::SOUTH || - input_bdl_wires[number_of_input_wires - 1 - i].port.dir == port_direction::EAST) - { - if ((current_input_index & (uint64_t{1ull} << i)) != 0ull) - { - for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) - { - if (bdl.type == sidb_technology::INPUT) - { - continue; - } - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) - { - if (bdl.type == sidb_technology::INPUT) - { - continue; - } - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - else - { - if ((current_input_index & (uint64_t{1ull} << i)) != 0ull) - { - for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) - { - if (bdl.type == sidb_technology::INPUT) - { - continue; - } - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) - { - if (bdl.type == sidb_technology::INPUT) - { - continue; - } - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - } - } - - /** - * This function assigns the charge states of the input wires in the layout according to the provided input pattern - * index. It performs the following steps: - * - For `NORTH-SOUTH` port wires, if the corresponding bit in the input pattern is set, assigns `NEUTRAL` - * charge to the upper part and `NEGATIVE` charge to the lower part of the BDLs of the wire. - * - For `NORTH-SOUTH` port wires, if the corresponding bit in the input pattern is not set, assigns - * `NEGATIVE` charge to the upper part and `NEUTRAL` charge to the lower part of the BDLs of the wire. - * - For `SOUTH-NORTH` port wires, if the corresponding bit in the input pattern is set, assigns `NEGATIVE` - * charge to the upper part and `NEUTRAL` charge to the lower part of the BDLs of the wire. - * - For `SOUTH-NORTH` port wires, if the corresponding bit in the input pattern is not set, assigns - * `NEUTRAL` charge to the upper part and `NEGATIVE` charge to the lower part of the BDLs of the wire. - * - * @param layout The charge distribution surface layout to be modified. - * @param current_input_index The index representing the current input pattern. - * @return void - */ - void set_charge_distribution_of_output_wires_based_on_output_index(charge_distribution_surface& layout, - const uint64_t output_wire_index) const noexcept - { - for (auto i = 0u; i < number_of_output_wires; i++) - { - if (output_bdl_wires[i].port.dir == port_direction::SOUTH || - output_bdl_wires[i].port.dir == port_direction::EAST) - { - if ((output_wire_index & (uint64_t{1ull} << i)) != 0ull) - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - else - { - if ((output_wire_index & (uint64_t{1ull} << i)) != 0ull) - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - if (bdl.type == sidb_technology::INPUT) - { - continue; - } - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - } - } - - /** - * This function calculates the output index for a given input index by evaluating the truth table. - * - * @param current_input_index The index representing the current input pattern. - * @return The output index derived from the truth table for the given input index. + * Number of threads to be used for the design process. */ - [[nodiscard]] uint64_t determine_output_index_of_output(const uint64_t current_input_index) const noexcept - { - std::bitset<64> bits{}; - for (auto i = 0u; i < number_of_output_wires; i++) - { - bits[i] = kitty::get_bit(truth_table[i], current_input_index); - } - return bits.to_ulong(); - } - - /** - * This function assigns the charge states of the output wires in the layout according to the values in the truth - * table for the provided input pattern index. - * - * @param layout The charge distribution surface layout to be modified. - * @param input_index The index representing the current input pattern. - */ - void set_charge_distribution_of_output_wires_based_on_truth_table(charge_distribution_surface& layout, - const uint64_t input_index) const noexcept - { - for (auto i = 0u; i < number_of_output_wires; i++) - { - if (output_bdl_wires[i].port.dir == port_direction::SOUTH || - output_bdl_wires[i].port.dir == port_direction::EAST) - { - if (kitty::get_bit(truth_table[i], input_index)) - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - else - { - if (kitty::get_bit(truth_table[i], input_index)) - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - else - { - for (const auto& bdl : output_bdl_wires[i].pairs) - { - layout.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, - charge_index_mode::KEEP_CHARGE_INDEX); - layout.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, - charge_index_mode::KEEP_CHARGE_INDEX); - } - } - } - } - } - - /** - * This function determines if there is a charge distribution of the canvas SiDBs for which the charge distribution - * of the whole layout is physically valid. - * - * @param cds_layout The charge distribution surface layout to be evaluated. - * @param cds_canvas The charge distribution surface of the canvas SiDBs. All possible configurations are enumerated - * @return The minimum energy value if a physically valid configuration is found, `std::nullopt` - * otherwise. - */ - [[nodiscard]] std::optional - is_physical_validity_feasible(charge_distribution_surface& cds_layout, - charge_distribution_surface& cds_canvas) const noexcept - { - auto min_energy = std::numeric_limits::infinity(); - - uint64_t canvas_charge_index = 0; - cds_canvas.assign_charge_index(canvas_charge_index); - - while (cds_canvas.get_charge_index_and_base().first <= cds_canvas.get_max_charge_index()) - { - cds_canvas.foreach_cell( - [&](const auto& c) - { - cds_layout.assign_charge_state(c, cds_canvas.get_charge_state(c), - charge_index_mode::KEEP_CHARGE_INDEX); - }); - cds_layout.update_after_charge_change(dependent_cell_mode::VARIABLE, - energy_calculation::KEEP_OLD_ENERGY_VALUE); - - if (cds_layout.is_physically_valid()) - { - cds_layout.recompute_system_energy(); - if (cds_layout.get_system_energy() + physical_constants::POP_STABILITY_ERR < min_energy) - { - min_energy = cds_layout.get_system_energy(); - } - } - - if (cds_canvas.get_charge_index_and_base().first == cds_canvas.get_max_charge_index()) - { - break; - } - - canvas_charge_index++; - cds_canvas.assign_charge_index(canvas_charge_index); - } - - if (min_energy < std::numeric_limits::infinity()) - { - return min_energy; - } - - return std::nullopt; - } - - /** - * This function iterates through various input patterns and output wire indices to determine if any configuration - * results in a physically valid layout with energy below the given energy value, indicating I/O signal instability. - * - * @param cds_layout The charge distribution surface layout to be modified and checked. - * @param cds_canvas The charge distribution of the canvas SiDBs. - * @param max_input_pattern_index The maximum index for input pattern - * @param input_pattern The specific input pattern for which the stability check is conducted. - * @param logical_correct_output_pattern The expected correct output pattern for the given input. - * @param minimal_energy_of_physically_valid_layout The minimum energy threshold below which the layout is - * considered unstable. - * @return `true` if the I/O signal is unstable, `false` otherwise. - */ - [[nodiscard]] bool is_io_signal_unstable(charge_distribution_surface& cds_layout, - charge_distribution_surface& cds_canvas, - const uint64_t max_input_pattern_index, const uint64_t input_pattern, - const uint64_t logical_correct_output_pattern, - const double minimal_energy_of_physically_valid_layout) const noexcept - { - for (auto kink_states_input = 0u; kink_states_input < max_input_pattern_index; ++kink_states_input) - { - for (auto output_wire_index = 0u; output_wire_index < std::pow(2, number_of_output_wires); - output_wire_index++) - { - if (output_wire_index == logical_correct_output_pattern && kink_states_input == input_pattern) - { - continue; - } - - set_charge_distribution_of_input_wires_based_on_input_pattern(cds_layout, kink_states_input); - set_charge_distribution_of_output_wires_based_on_output_index(cds_layout, output_wire_index); - - const auto physical_validity = is_physical_validity_feasible(cds_layout, cds_canvas); - - if (physical_validity.has_value()) - { - if (physical_validity.value() + physical_constants::POP_STABILITY_ERR < - minimal_energy_of_physically_valid_layout) - { - return true; - } - } - } - } - - return false; - } - - /** - * This function evaluates whether the given layout can be discarded since it cannot implement the given Boolean - * function. The pruning is subdivided into three single pruning steps: (1) discarding SiDB layouts with potentially - * positively charged SiDBs, (2) utilizing an efficient method to identify and discard SiDB layouts that do not - * satisfy physical model constraints under the I/O pin conditions required for the desired Boolean function, and - * (3) detecting I/O signal instability. - * - * @param current_layout The layout being evaluated for pruning. - * @param canvas_lyt The canvas layout comprising of the canvas SiDBs. - * @param dependent_cell A dependent-cell of the canvas SiDBs. - * @return `true` if the current layout can be pruned. `false` otherwise, which means that the layout is a candidate - * to be a valid gate implementation. Physical simulation is required as a second step to conduct the final - * validation. - */ - [[nodiscard]] bool layout_can_be_pruned(const Lyt& current_layout, const Lyt& canvas_lyt, - const cell& dependent_cell) noexcept - { - charge_distribution_surface cds_canvas{canvas_lyt, params.operational_params.simulation_parameters, - sidb_charge_state::NEGATIVE, - cds_configuration::CHARGE_LOCATION_ONLY}; - - cds_canvas.assign_dependent_cell(dependent_cell); - - auto bii = bdl_input_iterator{current_layout, params.operational_params.input_bdl_iterator_params, - input_bdl_wires}; - - for (auto i = 0u; i < truth_table.front().num_bits(); ++i, ++bii) - { - charge_distribution_surface cds_layout{*bii, params.operational_params.simulation_parameters}; - - if (can_positive_charges_occur(cds_layout, params.operational_params.simulation_parameters)) - { - number_of_discarded_layouts_at_first_pruning++; - return true; - } - - cds_layout.assign_dependent_cell(dependent_cell); - set_charge_distribution_of_input_wires_based_on_input_pattern(cds_layout, i); - set_charge_distribution_of_output_wires_based_on_truth_table(cds_layout, i); - - const auto physical_validity = is_physical_validity_feasible(cds_layout, cds_canvas); - - if (physical_validity.has_value()) - { - const auto output_index = determine_output_index_of_output(i); - if (is_io_signal_unstable(cds_layout, cds_canvas, truth_table.front().num_bits(), i, output_index, - physical_validity.value())) - { - number_of_discarded_layouts_at_third_pruning++; - return true; - }; - } - else - { - number_of_discarded_layouts_at_second_pruning++; - return true; - } - } - - return false; - } - + std::size_t number_of_threads{std::thread::hardware_concurrency()}; /** * This function processes each layout to determine if it represents a valid gate implementation or if it can be * pruned by using three distinct physically-informed pruning steps. It leverages multi-threading to accelerate the @@ -962,30 +563,72 @@ class design_sidb_gates_impl auto current_layout = skeleton_layout.clone(); cell dependent_cell{}; + canvas_lyt.foreach_cell( - [&](const auto& c) + [¤t_layout, &dependent_cell](const auto& c) { current_layout.assign_cell_type(c, Lyt::technology::cell_type::LOGIC); dependent_cell = c; }); - if (!layout_can_be_pruned(current_layout, canvas_lyt, dependent_cell)) + charge_distribution_surface cds_canvas{canvas_lyt, params.operational_params.simulation_parameters, + sidb_charge_state::NEGATIVE, + cds_configuration::CHARGE_LOCATION_ONLY}; + + cds_canvas.assign_dependent_cell(dependent_cell); + + auto bii = bdl_input_iterator{current_layout, params.operational_params.input_bdl_iterator_params, + input_bdl_wires}; + + detail::is_operational_impl is_operational_impl{ + current_layout, truth_table, params.operational_params, input_bdl_wires, output_bdl_wires, canvas_lyt}; + + for (auto i = 0u; i < truth_table.front().num_bits(); ++i, ++bii) { - const std::lock_guard lock{mutex_to_protect_gate_candidates}; - gate_candidate.push_back(current_layout); + const auto reason_for_filtering = + is_operational_impl.is_layout_invalid(bii.get_current_input_index(), cds_canvas); + + if (reason_for_filtering.has_value()) + { + switch (reason_for_filtering.value()) + { + case detail::layout_invalidity_reason::POTENTIAL_POSITIVE_CHARGES: + { + number_of_discarded_layouts_at_first_pruning++; + break; + } + case detail::layout_invalidity_reason::PHYSICAL_INFEASIBILITY: + { + number_of_discarded_layouts_at_second_pruning++; + break; + } + case detail::layout_invalidity_reason::IO_INSTABILITY: + { + number_of_discarded_layouts_at_third_pruning++; + break; + } + default: + { + break; + } + } + return; + } } + + const std::lock_guard lock{mutex_to_protect_gate_candidates}; + gate_candidate.push_back(current_layout); }; gate_candidate.reserve(all_canvas_layouts.size()); - const std::size_t number_of_threads = - std::min(static_cast(std::thread::hardware_concurrency()), all_canvas_layouts.size()); - const std::size_t chunk_size = (all_canvas_layouts.size() + number_of_threads - 1) / number_of_threads; + const std::size_t num_threads = std::min(number_of_threads, all_canvas_layouts.size()); + const std::size_t chunk_size = (all_canvas_layouts.size() + num_threads - 1) / num_threads; std::vector threads{}; - threads.reserve(number_of_threads); + threads.reserve(num_threads); - for (std::size_t i = 0; i < number_of_threads; ++i) + for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back( [i, chunk_size, this, &conduct_pruning_steps]() @@ -1010,7 +653,6 @@ class design_sidb_gates_impl return gate_candidate; } - /** * This function calculates all combinations of distributing a given number of SiDBs across a specified number of * positions in the canvas. Each combination is then used to create a gate layout candidate. @@ -1038,7 +680,6 @@ class design_sidb_gates_impl return designed_gate_layouts; } - /** * This function adds SiDBs (given by indices) to the skeleton layout that is returned afterwards. * @@ -1061,7 +702,6 @@ class design_sidb_gates_impl return lyt_copy; } - /** * This function generates canvas SiDb layouts. * @@ -1089,7 +729,7 @@ class design_sidb_gates_impl // the skeleton can already exhibit some canvas SiDBs (partially filled canvas) skeleton_layout.foreach_cell( - [&](const auto& c) + [this, &lyt](const auto& c) { if (skeleton_layout.get_cell_type(c) == sidb_technology::cell_type::LOGIC) { diff --git a/include/fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp b/include/fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp index 6483b4a79..58c888db7 100644 --- a/include/fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp +++ b/include/fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp @@ -137,7 +137,7 @@ template bool correct_output = true; is_operational_params params{}; - params.op_condition = operational_condition::REJECT_KINKS; + params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto operational_status = verify_logic_match(valid_layout, params, spec, input_index, input_bdl_wires, output_bdl_wires); diff --git a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp index d451f8a56..7e75273b9 100644 --- a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp +++ b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp @@ -182,7 +182,7 @@ class critical_temperature_impl auto input_bdl_wires = std::vector>{}; auto output_bdl_wires = std::vector>{}; - if (params.operational_params.op_condition == operational_condition::REJECT_KINKS) + if (params.operational_params.op_condition == is_operational_params::operational_condition::REJECT_KINKS) { input_bdl_wires = detect_bdl_wires(layout, params.operational_params.input_bdl_iterator_params.bdl_wire_params, @@ -217,7 +217,8 @@ class critical_temperature_impl sidb_energy_and_state_type energy_state_type{}; - if (params.operational_params.op_condition == operational_condition::REJECT_KINKS) + if (params.operational_params.op_condition == + is_operational_params::operational_condition::REJECT_KINKS) { energy_state_type = calculate_energy_and_state_type_with_kinks_rejected( distribution, sim_result.charge_distributions, spec, i, input_bdl_wires, output_bdl_wires); diff --git a/include/fiction/algorithms/simulation/sidb/displacement_robustness_domain.hpp b/include/fiction/algorithms/simulation/sidb/displacement_robustness_domain.hpp index 9b5cd8551..3d19ae0a9 100644 --- a/include/fiction/algorithms/simulation/sidb/displacement_robustness_domain.hpp +++ b/include/fiction/algorithms/simulation/sidb/displacement_robustness_domain.hpp @@ -103,7 +103,7 @@ struct displacement_robustness_domain_params */ std::pair displacement_variations = {1, 0}; /** - * Parameters to check the operation status of the SiDB layout. + * Parameters to check the operational status of the SiDB layout. */ is_operational_params operational_params{}; /** diff --git a/include/fiction/algorithms/simulation/sidb/is_operational.hpp b/include/fiction/algorithms/simulation/sidb/is_operational.hpp index 08984ea10..af69bd7ee 100644 --- a/include/fiction/algorithms/simulation/sidb/is_operational.hpp +++ b/include/fiction/algorithms/simulation/sidb/is_operational.hpp @@ -19,8 +19,10 @@ #include "fiction/technology/cell_ports.hpp" #include "fiction/technology/cell_technologies.hpp" #include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/technology/physical_constants.hpp" #include "fiction/technology/sidb_charge_state.hpp" #include "fiction/traits.hpp" +#include "fiction/utils/truth_table_utils.hpp" #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -53,25 +56,59 @@ enum class operational_status : uint8_t }; /** - * Condition which is used to decide if a layout is operational or non-operational. + * Parameters for the `is_operational` algorithm. */ -enum class operational_condition : uint8_t +struct is_operational_params { /** - * Even if the I/O pins show kinks, the layout is still considered as operational. + * Condition to decide whether a layout is operational or non-operational. */ - TOLERATE_KINKS, + enum class operational_condition : uint8_t + { + /** + * Even if the I/O pins show kinks, the layout is still considered as operational. + */ + TOLERATE_KINKS, + /** + * The I/O pins are not allowed to show kinks. If kinks exist, the layout is considered as non-operational. + */ + REJECT_KINKS + }; + /** - * The I/O pins are not allowed to show kinks. If kinks exist, the layout is considered as non-operational. + * Simulation method to determine if the layout is operational or non-operational. There are three possible + * strategies: + * + * - `SIMULATION_ONLY`: This setting does not apply any filtering strategies to determine if the layout is + * operational. Instead, it relies solely on physical simulation to make this determination. + * - `FILTER_ONLY`: This setting does only apply filtering strategies to determine if the layout is + * non-operational. If the layout passes all filtering strategies, it is considered operational. This is only an + * approximation. It may be possible that the layout is non-operational, but the filtering strategies do not detect + * it. + * - `FILTER_THEN_SIMULATION`: Before a physical simulation is conducted, the algorithm checks if filtering + * strategies have detected whether the layout is non-operational. This only provides any runtime benefits if kinks + * are rejected. */ - REJECT_KINKS -}; - -/** - * Parameters for the `is_operational` algorithm. - */ -struct is_operational_params -{ + enum class operational_analysis_strategy : uint8_t + { + /** + * Do not apply filter strategies to determine whether the layout is operational. + * Instead, rely solely on physical simulation. + */ + SIMULATION_ONLY, + /** + * Apply filtering exclusively to determine whether the layout is non-operational. If the layout + * passes all filter steps, it is considered operational. + * + * @note This is an extremely fast approximation that may sometimes lead to false positives. + */ + FILTER_ONLY, + /** + * Before a physical simulation is conducted, the algorithm checks if filter strategies can determine that the + * layout is non-operational. This only provides any runtime benefits if kinks are rejected. + */ + FILTER_THEN_SIMULATION + }; /** * The simulation parameters for the physical simulation of the ground state. */ @@ -85,9 +122,14 @@ struct is_operational_params */ bdl_input_iterator_params input_bdl_iterator_params{}; /** - * Condition which is used to decide if a layout is operational or non-operational. + * Condition to decide whether a layout is operational or non-operational. */ operational_condition op_condition = operational_condition::TOLERATE_KINKS; + /** + * Strategy to determine whether a layout is operational or non-operational. + */ + operational_analysis_strategy strategy_to_analyze_operational_status = + operational_analysis_strategy::SIMULATION_ONLY; }; namespace detail @@ -111,12 +153,31 @@ enum class non_operationality_reason : uint8_t */ NONE, }; +/** + * Reason why the layout is not a valid gate implementation for the given Boolean function. + */ +enum class layout_invalidity_reason : uint8_t +{ + /** + * Positive SiDBs can potentially occur. + */ + POTENTIAL_POSITIVE_CHARGES, + /** + * The layout is physically infeasible, meaning no charge distribution of the canvas SiDBs satisfies the criteria + * for physical validity. + */ + PHYSICAL_INFEASIBILITY, + /** + * I/O signals are unstable, indicating that an information flip results in a lower energy state. + */ + IO_INSTABILITY, +}; /** - * Implementation of the `is_operational` algorithm for a given gate layout. + * Implementation of the `is_operational` algorithm for a given SiDB layout. * * This class provides an implementation of the `is_operational` algorithm for - * a specified gate layout and parameters. It checks whether the gate layout is operational + * a specified SiDB layout and parameters. It checks whether the SiDB layout is operational * by simulating its behavior for different input combinations and comparing the results * to expected outputs from a truth table. * @@ -144,33 +205,160 @@ class is_operational_impl input_bdl_wires{ detect_bdl_wires(lyt, params.input_bdl_iterator_params.bdl_wire_params, bdl_wire_selection::INPUT)}, output_bdl_wires{ - detect_bdl_wires(lyt, params.input_bdl_iterator_params.bdl_wire_params, bdl_wire_selection::OUTPUT)} + detect_bdl_wires(lyt, params.input_bdl_iterator_params.bdl_wire_params, bdl_wire_selection::OUTPUT)}, + number_of_output_wires{output_bdl_wires.size()}, + number_of_input_wires{input_bdl_wires.size()} {} /** - * Constructor to initialize the algorithm with a layout and parameters. + * Constructor to initialize the algorithm with a layout, parameters, input and output wires. * * @param lyt The SiDB cell-level layout to be checked. * @param tt Expected Boolean function of the layout given as a multi-output truth table. * @param params Parameters for the `is_operational` algorithm. * @param input_wires BDL input wires of lyt. * @param output_wires BDL output wires of lyt. + * @param initialize_bii If `true`, the BDL input iterator is initialized, `false` otherwise. This parameter is only + * needed in special cases (verify_logic_match.hpp). */ is_operational_impl(const Lyt& lyt, const std::vector& tt, const is_operational_params& params, - const std::vector>& input_wires, const std::vector>& output_wires) : + const std::vector>& input_wires, const std::vector>& output_wires, + const bool initialize_bii = true) : layout{lyt}, truth_table{tt}, parameters{params}, output_bdl_pairs(detect_bdl_pairs(layout, sidb_technology::cell_type::OUTPUT, params.input_bdl_iterator_params.bdl_wire_params.bdl_pairs_params)), + bii{initialize_bii ? bdl_input_iterator{layout, params.input_bdl_iterator_params, input_wires} : + bdl_input_iterator{Lyt{}}}, + input_bdl_wires{input_wires}, + output_bdl_wires{output_wires}, + number_of_output_wires{output_bdl_wires.size()}, + number_of_input_wires{input_bdl_wires.size()} + {} + + /** + * Constructor to initialize the algorithm with a layout, parameters, input and output wires, and a canvas layout. + * + * @param lyt The SiDB cell-level layout to be checked. + * @param spec Expected Boolean function of the layout given as a multi-output truth table. + * @param params Parameters for the `is_operational` algorithm. + * @param input_wires BDL input wires of lyt. + * @param output_wires BDL output wires of lyt. + * @param c_lyt Canvas layout. + */ + is_operational_impl(const Lyt& lyt, const std::vector& tt, const is_operational_params& params, + const std::vector>& input_wires, const std::vector>& output_wires, + const Lyt& c_lyt) : + layout{lyt}, + truth_table{tt}, + parameters{params}, + output_bdl_pairs(detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, + params.input_bdl_iterator_params.bdl_wire_params.bdl_pairs_params)), bii{bdl_input_iterator{layout, params.input_bdl_iterator_params, input_wires}}, input_bdl_wires{input_wires}, - output_bdl_wires{output_wires} + output_bdl_wires{output_wires}, + number_of_output_wires{output_bdl_wires.size()}, + number_of_input_wires{input_bdl_wires.size()}, + canvas_lyt{c_lyt} + { + if (params.op_condition == is_operational_params::operational_condition::TOLERATE_KINKS) + { + output_bdl_pairs = detect_bdl_pairs(layout, sidb_technology::cell_type::OUTPUT, + params.input_bdl_iterator_params.bdl_wire_params.bdl_pairs_params); + } + canvas_lyt.foreach_cell( + [this](const auto& c) + { + dependent_cell = c; + return false; + }); + } + + /** + * Constructor to initialize the algorithm with a layout and parameters. + * + * @param lyt The SiDB cell-level layout to be checked. + * @param spec Expected Boolean function of the layout given as a multi-output truth table. + * @param params Parameters for the `is_operational` algorithm. + */ + is_operational_impl(const Lyt& lyt, const std::vector& tt, const is_operational_params& params, + const Lyt& c_lyt) : + layout{lyt}, + truth_table{tt}, + parameters{params}, + output_bdl_pairs(detect_bdl_pairs(lyt, sidb_technology::cell_type::OUTPUT, + params.input_bdl_iterator_params.bdl_wire_params.bdl_pairs_params)), + bii(bdl_input_iterator{lyt, params.input_bdl_iterator_params}), + input_bdl_wires{ + detect_bdl_wires(lyt, params.input_bdl_iterator_params.bdl_wire_params, bdl_wire_selection::INPUT)}, + output_bdl_wires{ + detect_bdl_wires(lyt, params.input_bdl_iterator_params.bdl_wire_params, bdl_wire_selection::OUTPUT)}, + number_of_output_wires{output_bdl_wires.size()}, + number_of_input_wires{input_bdl_wires.size()}, + canvas_lyt{c_lyt} {} + /** + * This function evaluates whether the given layout is invalid, i.e., it cannot implement the given Boolean + * function. This is done in three separate filtering steps: (1) discarding SiDB layouts with + * potentially positively charged SiDBs, (2) utilizing an efficient method to identify and discard SiDB layouts that + * do not satisfy physical model constraints under the I/O pin conditions required for the desired Boolean function, + * and (3) detecting I/O signal instability. + * + * @tparam ChargeLyt The charge distribution surface layout type. + * @param input_pattern The current input pattern. + * @param cds_canvas The charge distribution of the canvas layout. + * @param dependent_cell A dependent-cell of the canvas SiDBs. + * @return A `layout_invalidity_reason` object indicating why the layout is non-operational; or `std::nullopt` if it + * could not certainly be determined to be in fact non-operational. + */ + template + [[nodiscard]] std::optional is_layout_invalid(const uint64_t input_pattern, + ChargeLyt& cds_canvas) noexcept + { + static_assert(is_charge_distribution_surface_v, "ChargeLyt is not a charge distribution surface"); + + bii = input_pattern; + + ChargeLyt cds_layout{*bii}; + cds_layout.assign_all_charge_states(sidb_charge_state::NEGATIVE); + cds_layout.assign_physical_parameters(parameters.simulation_parameters); + + if (can_positive_charges_occur(cds_layout, parameters.simulation_parameters)) + { + return layout_invalidity_reason::POTENTIAL_POSITIVE_CHARGES; + } + + cds_layout.assign_dependent_cell(dependent_cell); + cds_canvas.assign_dependent_cell(dependent_cell); + + const auto input_index = bii.get_current_input_index(); + + set_charge_distribution_of_input_pins(cds_layout, bii.get_current_input_index()); + set_charge_distribution_of_output_pins(cds_layout, evaluate_output(truth_table, input_index)); + + const auto physical_validity = is_physical_validity_feasible(cds_layout); + + if (physical_validity.has_value()) + { + const auto output_index = evaluate_output(truth_table, input_index); + + if (is_io_signal_unstable(cds_layout, truth_table.front().num_bits(), input_index, output_index, + physical_validity.value())) + { + return layout_invalidity_reason::IO_INSTABILITY; + }; + + return std::nullopt; + } + + return layout_invalidity_reason::PHYSICAL_INFEASIBILITY; + } + /** * Run the `is_operational` algorithm. * - * This function executes the operational status checking algorithm for the gate layout + * This function executes the operational status checking algorithm for the given SiDB layout * and parameters provided during initialization. * * @return Pair with the first element indicating the operational status (either `OPERATIONAL` or `NON_OPERATIONAL`) @@ -178,47 +366,84 @@ class is_operational_impl */ [[nodiscard]] std::pair run() noexcept { - assert(!output_bdl_pairs.empty() && "No output cell provided."); - assert((truth_table.size() == output_bdl_pairs.size()) && - "Number of truth tables and output BDL pairs does not match"); - bool at_least_one_layout_is_kink_induced_non_operational = false; - // number of different input combinations - for (auto i = 0u; i < truth_table.front().num_bits(); ++i, ++bii) + if (!canvas_lyt.is_empty()) { - ++simulator_invocations; + charge_distribution_surface cds_canvas{canvas_lyt}; - // if positively charged SiDBs can occur, the SiDB layout is considered as non-operational - if (can_positive_charges_occur(*bii, parameters.simulation_parameters)) + cds_canvas.assign_dependent_cell(dependent_cell); + cds_canvas.assign_physical_parameters(parameters.simulation_parameters); + + if ((parameters.op_condition == is_operational_params::operational_condition::REJECT_KINKS && + parameters.strategy_to_analyze_operational_status == + is_operational_params::operational_analysis_strategy::FILTER_THEN_SIMULATION) || + parameters.strategy_to_analyze_operational_status == + is_operational_params::operational_analysis_strategy::FILTER_ONLY) { - return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; + // number of different input combinations + for (auto i = 0u; i < truth_table.front().num_bits(); ++i, ++bii) + { + if (is_layout_invalid(bii.get_current_input_index(), cds_canvas)) + { + return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; + } + } } + } - // performs physical simulation of a given SiDB layout at a given input combination - const auto simulation_results = physical_simulation_of_layout(bii); + // if the layout is not discarded during the three filtering steps, it is considered operational. + // This is only an approximation. + if (parameters.strategy_to_analyze_operational_status == + is_operational_params::operational_analysis_strategy::FILTER_ONLY && + !canvas_lyt.is_empty()) + { + return {operational_status::OPERATIONAL, non_operationality_reason::NONE}; + } - // if no physically valid charge distributions were found, the layout is non-operational - if (simulation_results.charge_distributions.empty()) + if (parameters.strategy_to_analyze_operational_status == + is_operational_params::operational_analysis_strategy::SIMULATION_ONLY || + parameters.strategy_to_analyze_operational_status == + is_operational_params::operational_analysis_strategy::FILTER_THEN_SIMULATION || + canvas_lyt.is_empty()) + { + bii = 0; + // number of different input combinations + for (auto i = 0u; i < truth_table.front().num_bits(); ++i, ++bii) { - return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; - } + // if positively charged SiDBs can occur, the SiDB layout is considered as non-operational + if (can_positive_charges_occur(*bii, parameters.simulation_parameters)) + { + return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; + } - const auto ground_states = groundstate_from_simulation_result(simulation_results); + ++simulator_invocations; + // performs physical simulation of a given SiDB layout at a given input combination + const auto simulation_results = physical_simulation_of_layout(bii); - for (const auto& gs : ground_states) - { - const auto [op_status, non_op_reason] = verify_logic_match_of_cds(gs, i); - if (op_status == operational_status::NON_OPERATIONAL && - non_op_reason == non_operationality_reason::LOGIC_MISMATCH) + // if no physically valid charge distributions were found, the layout is non-operational + if (simulation_results.charge_distributions.empty()) { return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; } - if (op_status == operational_status::NON_OPERATIONAL && - non_op_reason == non_operationality_reason::KINKS) + + const auto ground_states = groundstate_from_simulation_result(simulation_results); + + for (const auto& gs : ground_states) { - at_least_one_layout_is_kink_induced_non_operational = true; - continue; + const auto [op_status, non_op_reason] = verify_logic_match_of_cds(gs, i); + if (op_status == operational_status::NON_OPERATIONAL && + non_op_reason == non_operationality_reason::LOGIC_MISMATCH) + { + return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; + } + if (op_status == operational_status::NON_OPERATIONAL && + non_op_reason == non_operationality_reason::KINKS && + parameters.op_condition == is_operational_params::operational_condition::REJECT_KINKS) + { + at_least_one_layout_is_kink_induced_non_operational = true; + continue; + } } } } @@ -254,16 +479,14 @@ class is_operational_impl { auto non_operational_reason = non_operationality_reason::LOGIC_MISMATCH; - assert(!output_bdl_pairs.empty() && "No output cell provided."); - assert((truth_table.size() == output_bdl_pairs.size()) && - "Number of truth tables and output BDL pairs does not match"); - // if positively charged SiDBs can occur, the SiDB layout is considered as non-operational if (can_positive_charges_occur(given_cds, parameters.simulation_parameters)) { return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; } + assert(!output_bdl_pairs.empty() && "No output cell provided."); + // fetch the charge states of the output BDL pair for (auto output = 0u; output < output_bdl_pairs.size(); output++) { @@ -292,14 +515,19 @@ class is_operational_impl return {operational_status::NON_OPERATIONAL, non_operationality_reason::LOGIC_MISMATCH}; } } + } + + if (parameters.op_condition == is_operational_params::operational_condition::REJECT_KINKS) + { + assert(!input_bdl_wires.empty() && "No input wires provided."); + assert(!output_bdl_wires.empty() && "No output wires provided."); + assert((truth_table.size() == output_bdl_wires.size()) && + "Number of truth tables and output BDL wires don't not match"); - if (parameters.op_condition == operational_condition::REJECT_KINKS) + if (check_existence_of_kinks_in_input_wires(given_cds, input_pattern) || + check_existence_of_kinks_in_output_wires(given_cds, input_pattern)) { - if (check_existence_of_kinks_in_input_wires(given_cds, input_pattern) || - check_existence_of_kinks_in_output_wires(given_cds, input_pattern)) - { - non_operational_reason = non_operationality_reason::KINKS; - } + non_operational_reason = non_operationality_reason::KINKS; } } @@ -322,8 +550,7 @@ class is_operational_impl [[nodiscard]] std::vector> determine_non_operational_input_patterns_and_non_operationality_reason() noexcept { - assert(!output_bdl_pairs.empty() && "No output cell provided."); - assert((truth_table.size() == output_bdl_pairs.size()) && + assert((truth_table.size() == output_bdl_wires.size()) && "Number of truth tables and output BDL pairs does not match"); std::vector> @@ -374,6 +601,287 @@ class is_operational_impl return simulator_invocations; } + /** + * This function determines if there is a charge distribution of the canvas SiDBs for which the charge distribution + * of the whole layout is physically valid. + * + * @param cds_layout The charge distribution surface layout to be evaluated. + * @return The minimum energy value if a physically valid configuration is found, `std::nullopt` + * otherwise. + */ + [[nodiscard]] std::optional + is_physical_validity_feasible(charge_distribution_surface& cds_layout) const noexcept + { + auto min_energy = std::numeric_limits::infinity(); + + uint64_t canvas_charge_index = 0; + + charge_distribution_surface cds_canvas_copy{canvas_lyt}; + cds_canvas_copy.assign_base_number(2); + cds_canvas_copy.assign_charge_index(canvas_charge_index); + cds_canvas_copy.assign_dependent_cell(dependent_cell); + + const auto max_index = cds_canvas_copy.get_max_charge_index(); + + assert(max_index == static_cast(std::pow(2, cds_canvas_copy.num_cells() - 1) - 1) && + "The maximum charge index is incorrect. Probably, the dependent cell is not set."); + + while (canvas_charge_index <= max_index) + { + cds_canvas_copy.foreach_cell( + [&cds_layout, &cds_canvas_copy](const auto& c) + { + cds_layout.assign_charge_state(c, cds_canvas_copy.get_charge_state(c), + charge_index_mode::KEEP_CHARGE_INDEX); + }); + cds_layout.update_after_charge_change(dependent_cell_mode::VARIABLE, + energy_calculation::KEEP_OLD_ENERGY_VALUE); + + if (cds_layout.is_physically_valid()) + { + cds_layout.recompute_system_energy(); + if (cds_layout.get_system_energy() + physical_constants::POP_STABILITY_ERR < min_energy) + { + min_energy = cds_layout.get_system_energy(); + } + } + + if (canvas_charge_index == max_index) + { + break; + } + + canvas_charge_index++; + cds_canvas_copy.assign_charge_index(canvas_charge_index, + charge_distribution_mode::UPDATE_CHARGE_DISTRIBUTION); + } + + if (min_energy < std::numeric_limits::infinity()) + { + return min_energy; + } + + return std::nullopt; + } + + /** + * This function assigns the charge states of the input pins in the layout according to the input index provided. + * This means that when a zero is applied, each BDL pair in the wire is set to zero. + * + * @param cds The charge distribution surface layout to be modified. + * @param current_input_index The index representing the current input pattern. + */ + void set_charge_distribution_of_input_pins(charge_distribution_surface& cds, + const uint64_t current_input_index) const noexcept + { + cds.assign_all_charge_states(sidb_charge_state::NEGATIVE, charge_index_mode::KEEP_CHARGE_INDEX); + + for (auto i = 0u; i < number_of_input_wires; i++) + { + if (input_bdl_wires[number_of_input_wires - 1 - i].port.dir == port_direction::SOUTH || + input_bdl_wires[number_of_input_wires - 1 - i].port.dir == port_direction::EAST) + { + if ((current_input_index & (uint64_t{1ull} << i)) != 0ull) + { + for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + else + { + for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + } + else + { + if ((current_input_index & (uint64_t{1ull} << i)) != 0ull) + { + for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + else + { + for (const auto& bdl : input_bdl_wires[number_of_input_wires - 1 - i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + } + } + } + + /** + * This function assigns the charge states of the output pins in the layout according to the input index provided. + * This means that when a zero is applied, each BDL pair in the wire is set to zero. + * + * @param cds The charge distribution surface layout to be modified. + * @param output_wire_index The index representing the current input pattern of the output wire. + */ + void set_charge_distribution_of_output_pins(charge_distribution_surface& cds, + const uint64_t output_wire_index) const noexcept + { + for (auto i = 0u; i < number_of_output_wires; i++) + { + if (output_bdl_wires[i].port.dir == port_direction::SOUTH || + output_bdl_wires[i].port.dir == port_direction::EAST) + { + if ((output_wire_index & (uint64_t{1ull} << i)) != 0ull) + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + else + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + } + else if (output_bdl_wires[i].port.dir == port_direction::NONE) + { + if ((output_wire_index & (uint64_t{1ull} << i)) != 0ull) + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + else + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + } + else + { + if ((output_wire_index & (uint64_t{1ull} << i)) != 0ull) + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + if (bdl.type == sidb_technology::INPUT) + { + continue; + } + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + else + { + for (const auto& bdl : output_bdl_wires[i].pairs) + { + cds.assign_charge_state(bdl.upper, sidb_charge_state::NEUTRAL, + charge_index_mode::KEEP_CHARGE_INDEX); + cds.assign_charge_state(bdl.lower, sidb_charge_state::NEGATIVE, + charge_index_mode::KEEP_CHARGE_INDEX); + } + } + } + } + } + /** + * This function iterates through various input patterns and output wire indices to determine if any configuration + * results in a physically valid layout with energy below the given energy value, indicating I/O signal instability. + * + * @param cds_layout The charge distribution surface layout to be modified and checked. + * @param max_input_pattern_index The maximum index for input pattern + * @param input_pattern The specific input pattern for which the stability check is conducted. + * @param logical_correct_output_pattern The expected correct output pattern for the given input. + * @param minimal_energy_of_physically_valid_layout The minimum energy threshold below which the layout is + * considered unstable. + * @return `true` if the I/O signal is unstable, `false` otherwise. + */ + [[nodiscard]] bool is_io_signal_unstable(charge_distribution_surface& cds_layout, + const uint64_t max_input_pattern_index, const uint64_t input_pattern, + const uint64_t logical_correct_output_pattern, + const double minimal_energy_of_physically_valid_layout) const noexcept + { + for (auto kink_states_input = 0u; kink_states_input < max_input_pattern_index; ++kink_states_input) + { + for (auto output_wire_index = 0u; output_wire_index < std::pow(2, output_bdl_wires.size()); + output_wire_index++) + { + if (output_wire_index == logical_correct_output_pattern && kink_states_input == input_pattern) + { + continue; + } + + set_charge_distribution_of_input_pins(cds_layout, kink_states_input); + set_charge_distribution_of_output_pins(cds_layout, output_wire_index); + + const auto physical_validity = is_physical_validity_feasible(cds_layout); + + if (physical_validity.has_value()) + { + if (physical_validity.value() + physical_constants::POP_STABILITY_ERR < + minimal_energy_of_physically_valid_layout) + { + return true; + } + } + } + } + + return false; + } + private: /** * SiDB cell-level layout. @@ -386,7 +894,7 @@ class is_operational_impl /** * Parameters for the `is_operational` algorithm. */ - is_operational_params parameters; + const is_operational_params& parameters; /** * Output BDL pairs. */ @@ -409,11 +917,27 @@ class is_operational_impl std::size_t simulator_invocations{0}; /** - * This function conducts physical simulation of the given layout (gate layout with certain input combination). + * Number of output BDL wires. + */ + const std::size_t number_of_output_wires; + /** + * Number of input BDL wires. + */ + const std::size_t number_of_input_wires; + /** + * Layout consisting of all canvas SiDBs. + */ + Lyt canvas_lyt{}; + /** + * Dependent cell of the canvas SiDBs. + */ + cell dependent_cell{}; + /** + * This function conducts physical simulation of the given SiDB layout. * The simulation results are stored in the `sim_result` variable. * - * @param bdl_iterator A reference to a BDL input iterator representing the gate layout at a given input - * combination. The simulation is performed based on the configuration represented by the iterator. + * @param bdl_iterator BDL input iterator representing the SiDB layout with a given input + * combination. * @return Simulation results. */ [[nodiscard]] sidb_simulation_result @@ -453,7 +977,6 @@ class is_operational_impl * states derived from the input pattern. A kink is considered to exist if an input wire's charge state does not * match the expected value (i.e., bit one or bit zero) for the given input index. * - * @tparam Lyt SiDB cell-level layout type * @param ground_state The ground state charge distribution surface. * @param current_input_index The current input index used to retrieve the expected output from the truth table. * @return `true` if any input wire contains a kink (i.e., an unexpected charge state), `false` otherwise. @@ -488,7 +1011,6 @@ class is_operational_impl * states derived from the truth table. A kink is considered to exist if an output wire's charge state does not * match the expected value (i.e., bit one or bit zero) for the given input index. * - * @tparam Lyt SiDB cell-level layout type * @param ground_state The ground state charge distribution surface. * @param current_input_index The current input index used to retrieve the expected output from the truth table. * @return `true` if any output wire contains a kink (i.e., an unexpected charge state), `false` otherwise. @@ -522,7 +1044,6 @@ class is_operational_impl /** * This function returns `true` if `0` is encoded in the charge state of the given BDL pair. `false` otherwise. * - * @tparam Lyt SiDB cell-level layout type. * @param ground_state The ground state charge distribution surface. * @param bdl BDL pair to be evaluated. * @return `true` if `0` is encoded, `false` otherwise. @@ -542,7 +1063,6 @@ class is_operational_impl /** * This function returns `true` if `1` is encoded in the charge state of the given BDL pair. `false` otherwise. * - * @tparam Lyt SiDB cell-level layout type. * @param ground_state The ground state charge distribution surface. * @param bdl BDL pair to be evaluated. * @return `true` if `1` is encoded, `false` otherwise. @@ -566,8 +1086,46 @@ class is_operational_impl /** * Determine the operational status of an SiDB layout. * - * This function checks the operational status of a given gate layout using the `is_operational` algorithm. It - * determines whether the gate layout is operational and returns the correct result for all \f$2^n\f$ input + * This function checks the operational status of a given SiDB layout using the `is_operational` algorithm. It + * determines whether the SiDB layout is operational and returns the correct result for all \f$2^n\f$ input + * combinations. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Type of the truth table. + * @param lyt The SiDB cell-level layout to be checked. + * @param spec Expected Boolean function of the layout given as a multi-output truth table. + * @param params Parameters for the `is_operational` algorithm. + * @return A pair containing the operational status of the SiDB layout (either `OPERATIONAL` or `NON_OPERATIONAL`) and + * the number of input combinations tested. + */ +template +[[nodiscard]] std::pair +is_operational(const Lyt& lyt, const std::vector& spec, const is_operational_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(kitty::is_truth_table::value, "TT is not a truth table"); + + assert(lyt.num_pis() > 0 && "lyt needs input cells"); + assert(lyt.num_pos() > 0 && "lyt needs output cells"); + + assert(!spec.empty()); + // all elements in spec must have the same number of variables + assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); + + detail::is_operational_impl p{lyt, spec, params}; + + const auto [status, _] = p.run(); + + return {status, p.get_number_of_simulator_invocations()}; +} + +/** + * Determine the operational status of an SiDB layout. + * + * This function checks the operational status of a given SiDB layout using the `is_operational` algorithm. It + * determines whether the SiDB layout is operational and returns the correct result for all \f$2^n\f$ input * combinations. * * @tparam Lyt SiDB cell-level layout type. @@ -577,14 +1135,15 @@ class is_operational_impl * @param params Parameters for the `is_operational` algorithm. * @param input_bdl_wire Optional BDL input wires of lyt. * @param output_bdl_wire Optional BDL output wires of lyt. - * @return A pair containing the operational status of the gate layout (either `OPERATIONAL` or `NON_OPERATIONAL`) and + * @param canvas_lyt Optional canvas layout. + * @return A pair containing the operational status of the SiDB layout (either `OPERATIONAL` or `NON_OPERATIONAL`) and * the number of input combinations tested. */ template [[nodiscard]] std::pair -is_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}, - const std::optional>>& input_bdl_wire = std::nullopt, - const std::optional>>& output_bdl_wire = std::nullopt) +is_operational(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, + const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, + const std::optional& canvas_lyt = std::nullopt) 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"); @@ -598,16 +1157,34 @@ is_operational(const Lyt& lyt, const std::vector& spec, const is_operational assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) { return a.num_vars() != b.num_vars(); }) == spec.cend()); - if (input_bdl_wire.has_value() && output_bdl_wire.has_value()) + if (canvas_lyt.has_value()) { - detail::is_operational_impl p{lyt, spec, params, input_bdl_wire.value(), output_bdl_wire.value()}; + detail::is_operational_impl p{lyt, spec, params, input_bdl_wire, output_bdl_wire, canvas_lyt.value()}; const auto [status, _] = p.run(); return {status, p.get_number_of_simulator_invocations()}; } - detail::is_operational_impl p{lyt, spec, params}; + const auto logic_cells = lyt.get_cells_by_type(technology::cell_type::LOGIC); + + if (!logic_cells.empty()) + { + Lyt c_lyt{}; + + for (const auto& c : logic_cells) + { + c_lyt.assign_cell_type(c, technology::cell_type::LOGIC); + } + + detail::is_operational_impl p{lyt, spec, params, input_bdl_wire, output_bdl_wire, c_lyt}; + + const auto [status, _] = p.run(); + + return {status, p.get_number_of_simulator_invocations()}; + } + + detail::is_operational_impl p{lyt, spec, params, input_bdl_wire, output_bdl_wire}; const auto [status, _] = p.run(); @@ -659,6 +1236,82 @@ template return input_patterns; } +/** + * This function determines the input combinations for which the layout is operational. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Type of the truth table. + * @param lyt The SiDB layout. + * @param spec Vector of truth table specifications. + * @param params Parameters to simulate if a input combination is operational. + * @param input_bdl_wire Optional BDL input wires of lyt. + * @param output_bdl_wire Optional BDL output wires of lyt. + * @param canvas_lyt Optional canvas layout. + * @return The count of operational input combinations. + */ +template +[[nodiscard]] std::set +operational_input_patterns(const Lyt& lyt, const std::vector& spec, const is_operational_params& params, + const std::vector>& input_bdl_wire, + const std::vector>& output_bdl_wire, + const std::optional& canvas_lyt = std::nullopt) 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(kitty::is_truth_table::value, "TT is not a truth table"); + + assert(lyt.num_pis() > 0 && "skeleton needs input cells"); + assert(lyt.num_pos() > 0 && "skeleton needs output cells"); + + assert(!spec.empty()); + // all elements in spec must have the same number of variables + assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); + + if (canvas_lyt.has_value()) + { + detail::is_operational_impl p{lyt, spec, params, input_bdl_wire, output_bdl_wire, canvas_lyt.value()}; + + std::set input_patterns{}; + + // all possible input patterns + for (auto i = 0u; i < spec.front().num_bits(); ++i) + { + input_patterns.insert(i); + } + + const auto non_op_patterns_and_non_op_reason = + p.determine_non_operational_input_patterns_and_non_operationality_reason(); + + for (const auto& [input_pattern, _] : non_op_patterns_and_non_op_reason) + { + input_patterns.erase(input_pattern); + } + + return input_patterns; + } + + detail::is_operational_impl p{lyt, spec, params, input_bdl_wire, output_bdl_wire}; + + std::set input_patterns{}; + + // all possible input patterns + for (auto i = 0u; i < spec.front().num_bits(); ++i) + { + input_patterns.insert(i); + } + + const auto non_op_patterns_and_non_op_reason = + p.determine_non_operational_input_patterns_and_non_operationality_reason(); + + for (const auto& [input_pattern, _] : non_op_patterns_and_non_op_reason) + { + input_patterns.erase(input_pattern); + } + + return input_patterns; +} + /** * This function determines all input combinations for which kinks induce the SiDB layout to become non-operational. * This means that the layout is operational if kinks would be accepted. @@ -691,7 +1344,8 @@ kink_induced_non_operational_input_patterns(const Lyt& lyt, const std::vector p{lyt, spec, params_with_rejecting_kinks}; @@ -710,6 +1364,125 @@ kink_induced_non_operational_input_patterns(const Lyt& lyt, const std::vector +[[nodiscard]] std::set kink_induced_non_operational_input_patterns( + const Lyt& lyt, const std::vector& spec, const is_operational_params& params, + const std::vector>& input_bdl_wire, const std::vector>& output_bdl_wire, + const std::optional& canvas_lyt = std::nullopt) 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(kitty::is_truth_table::value, "TT is not a truth table"); + + assert(lyt.num_pis() > 0 && "skeleton needs input cells"); + assert(lyt.num_pos() > 0 && "skeleton needs output cells"); + + assert(!spec.empty()); + // all elements in tts must have the same number of variables + assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); + + is_operational_params params_with_rejecting_kinks = params; + + params_with_rejecting_kinks.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + + if (canvas_lyt.has_value()) + { + detail::is_operational_impl p{ + lyt, spec, params_with_rejecting_kinks, input_bdl_wire, output_bdl_wire, canvas_lyt.value()}; + + std::set kink_induced_non_op_patterns{}; + + const auto input_patterns_and_non_op_reason = + p.determine_non_operational_input_patterns_and_non_operationality_reason(); + + for (const auto& [input_pattern, status] : input_patterns_and_non_op_reason) + { + if (status == detail::non_operationality_reason::KINKS) + { + kink_induced_non_op_patterns.insert(input_pattern); + } + } + + return kink_induced_non_op_patterns; + } + + detail::is_operational_impl p{lyt, spec, params_with_rejecting_kinks, input_bdl_wire, output_bdl_wire}; + + std::set kink_induced_non_op_patterns{}; + + const auto input_patterns_and_non_op_reason = + p.determine_non_operational_input_patterns_and_non_operationality_reason(); + + for (const auto& [input_pattern, status] : input_patterns_and_non_op_reason) + { + if (status == detail::non_operationality_reason::KINKS) + { + kink_induced_non_op_patterns.insert(input_pattern); + } + } + + return kink_induced_non_op_patterns; +} +/** + * This function determines if the layout is only considered as non-operational because of kinks. This means that + * the layout would be considered as operational, if kinks were accepted. + * + * @note "Kink induced non-operational" refers to the non-operational status being exclusively caused by kinks with an + * otherwise correct logic match. + * + * @tparam Lyt SiDB cell-level layout type. + * @tparam TT Type of the truth table. + * @param lyt The SiDB cell-level layout to be checked. + * @param spec Expected Boolean function of the layout given as a multi-output truth table. + * @param params Parameters for the `is_operational` algorithm. + * @return Bool that indicates whether kinks induce the layout to become non-operational. `true` if the layout is + * non-operational due to kinks, `false` otherwise. + */ +template +[[nodiscard]] bool is_kink_induced_non_operational(const Lyt& lyt, const std::vector& spec, + const is_operational_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(kitty::is_truth_table::value, "TT is not a truth table"); + + assert(lyt.num_pis() > 0 && "lyt needs input cells"); + assert(lyt.num_pos() > 0 && "lyt needs output cells"); + + assert(!spec.empty()); + // all elements in spec must have the same number of variables + assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); + + is_operational_params params_with_rejecting_kinks = params; + params_with_rejecting_kinks.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + + detail::is_operational_impl p{lyt, spec, params_with_rejecting_kinks}; + + const auto [op_status, non_op_reason] = p.run(); + + return op_status == operational_status::NON_OPERATIONAL && + non_op_reason == detail::non_operationality_reason::KINKS; +} + /** * This function determines if the layout is only considered as non-operational because of kinks. This means that * the layout would be considered as operational, if kinks were accepted. @@ -724,14 +1497,16 @@ kink_induced_non_operational_input_patterns(const Lyt& lyt, const std::vector -[[nodiscard]] bool is_kink_induced_non_operational( - const Lyt& lyt, const std::vector& spec, const is_operational_params& params = {}, - const std::optional>>& input_bdl_wire = std::nullopt, - const std::optional>>& output_bdl_wire = std::nullopt) noexcept +[[nodiscard]] bool is_kink_induced_non_operational(const Lyt& lyt, const std::vector& spec, + const is_operational_params& params, + const std::vector>& input_bdl_wire, + const std::vector>& output_bdl_wire, + const std::optional& canvas_lyt = std::nullopt) 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"); @@ -746,12 +1521,12 @@ template { return a.num_vars() != b.num_vars(); }) == spec.cend()); is_operational_params params_with_rejecting_kinks = params; - params_with_rejecting_kinks.op_condition = operational_condition::REJECT_KINKS; + params_with_rejecting_kinks.op_condition = is_operational_params::operational_condition::REJECT_KINKS; - if (input_bdl_wire.has_value() && output_bdl_wire.has_value()) + if (canvas_lyt.has_value()) { - detail::is_operational_impl p{lyt, spec, params_with_rejecting_kinks, input_bdl_wire.value(), - output_bdl_wire.value()}; + detail::is_operational_impl p{ + lyt, spec, params_with_rejecting_kinks, input_bdl_wire, output_bdl_wire, canvas_lyt.value()}; const auto [op_status, non_op_reason] = p.run(); @@ -759,7 +1534,7 @@ template non_op_reason == detail::non_operationality_reason::KINKS; } - detail::is_operational_impl p{lyt, spec, params_with_rejecting_kinks}; + detail::is_operational_impl p{lyt, spec, params_with_rejecting_kinks, input_bdl_wire, output_bdl_wire}; const auto [op_status, non_op_reason] = p.run(); diff --git a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp index 26d8f3134..9d013ef35 100644 --- a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp +++ b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp @@ -5,6 +5,8 @@ #ifndef FICTION_OPERATIONAL_DOMAIN_HPP #define FICTION_OPERATIONAL_DOMAIN_HPP +#include "fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp" +#include "fiction/algorithms/simulation/sidb/detect_bdl_wires.hpp" #include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" #include "fiction/algorithms/simulation/sidb/is_operational.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" @@ -365,8 +367,25 @@ class operational_domain_impl output_bdl_pairs{detect_bdl_pairs( layout, sidb_technology::cell_type::OUTPUT, ps.operational_params.input_bdl_iterator_params.bdl_wire_params.bdl_pairs_params)}, - num_dimensions{params.sweep_dimensions.size()} + num_dimensions{params.sweep_dimensions.size()}, + input_bdl_wires{detect_bdl_wires(lyt, params.operational_params.input_bdl_iterator_params.bdl_wire_params, + bdl_wire_selection::INPUT)}, + output_bdl_wires{detect_bdl_wires(lyt, params.operational_params.input_bdl_iterator_params.bdl_wire_params, + bdl_wire_selection::OUTPUT)} { + const auto logic_cells = lyt.get_cells_by_type(technology::cell_type::LOGIC); + + assert(((params.operational_params.strategy_to_analyze_operational_status != + is_operational_params::operational_analysis_strategy::FILTER_ONLY) || + (logic_cells.size() > 0)) && + "No logic cells found in the layout"); + + // the canvas layout is created which is defined by the logic cells. + for (const auto& c : logic_cells) + { + canvas_lyt.assign_cell_type(c, technology::cell_type::NORMAL); + } + op_domain.dimensions.reserve(num_dimensions); indices.reserve(num_dimensions); @@ -842,6 +861,10 @@ class operational_domain_impl * All dimension values. */ std::vector> values; + /** + * This layout consists of the canvas cells of the layout. + */ + Lyt canvas_lyt{}; /** * The operational domain of the layout. */ @@ -866,6 +889,14 @@ class operational_domain_impl * Number of available hardware threads. */ const std::size_t num_threads{std::thread::hardware_concurrency()}; + /** + * Input BDL wires. + */ + const std::vector> input_bdl_wires; + /** + * Output BDL wires. + */ + const std::vector> output_bdl_wires; /** * 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. @@ -1077,7 +1108,8 @@ class operational_domain_impl auto op_params_set_dimension_values = params.operational_params; op_params_set_dimension_values.simulation_parameters = sim_params; - const auto& [status, sim_calls] = is_operational(layout, truth_table, op_params_set_dimension_values); + const auto& [status, sim_calls] = is_operational(layout, truth_table, op_params_set_dimension_values, + input_bdl_wires, output_bdl_wires, std::optional{canvas_lyt}); num_simulator_invocations += sim_calls; @@ -1518,7 +1550,7 @@ class operational_domain_impl const auto sp = queue.front(); queue.pop(); - // if the point is known to be non-operational continue with the next + // if the point is known to be non-operational, continue with the next if (const auto operational_status = has_already_been_sampled(sp); operational_status.has_value()) { if (operational_status.value() == operational_status::NON_OPERATIONAL) @@ -1779,7 +1811,6 @@ operational_domain_contour_tracing(const Lyt& lyt, const std::vector& spec, operational_domain_stats st{}; detail::operational_domain_impl> p{lyt, spec, params, st}; - const auto result = p.contour_tracing(samples); if (stats) diff --git a/include/fiction/algorithms/simulation/sidb/verify_logic_match.hpp b/include/fiction/algorithms/simulation/sidb/verify_logic_match.hpp index d2c538862..5a76b0805 100644 --- a/include/fiction/algorithms/simulation/sidb/verify_logic_match.hpp +++ b/include/fiction/algorithms/simulation/sidb/verify_logic_match.hpp @@ -58,7 +58,7 @@ template assert(std::adjacent_find(spec.cbegin(), spec.cend(), [](const auto& a, const auto& b) { return a.num_vars() != b.num_vars(); }) == spec.cend()); - detail::is_operational_impl p{cds, spec, params, input_wires, output_wires}; + detail::is_operational_impl p{cds, spec, params, input_wires, output_wires, false}; const auto [op_status, _] = p.verify_logic_match_of_cds(cds, input_pattern); diff --git a/include/fiction/layouts/cell_level_layout.hpp b/include/fiction/layouts/cell_level_layout.hpp index aa00a0225..671d4221c 100644 --- a/include/fiction/layouts/cell_level_layout.hpp +++ b/include/fiction/layouts/cell_level_layout.hpp @@ -6,7 +6,6 @@ #define FICTION_CELL_LEVEL_LAYOUT_HPP #include "fiction/layouts/clocking_scheme.hpp" -#include "fiction/technology/cell_technologies.hpp" #include "fiction/traits.hpp" #include @@ -17,6 +16,7 @@ #include #include #include +#include namespace fiction { @@ -189,13 +189,37 @@ class cell_level_layout : public ClockedLayout */ [[nodiscard]] cell_type get_cell_type(const cell& c) const noexcept { - if (auto it = strg->cell_type_map.find(c); it != strg->cell_type_map.cend()) + if (const auto it = strg->cell_type_map.find(c); it != strg->cell_type_map.cend()) { return it->second; } return Technology::cell_type::EMPTY; } + + /** + * Returns all cells of the given type. + * + * @param type Type of cells to return. + * @return All cells of the layout that have the given type. + */ + [[nodiscard]] std::vector get_cells_by_type(const typename Technology::cell_type type) const noexcept + { + std::vector cells; + cells.reserve(num_cells()); + + foreach_cell( + [&cells, &type, this](const auto& c) + { + const auto c_type = get_cell_type(c); + if (c_type == type) + { + cells.push_back(c); + } + }); + + return cells; + } /** * Returns `true` if no cell type is assigned to cell position `c` or if the empty type was assigned. * diff --git a/include/fiction/utils/truth_table_utils.hpp b/include/fiction/utils/truth_table_utils.hpp index 55f233dec..fa67aade1 100644 --- a/include/fiction/utils/truth_table_utils.hpp +++ b/include/fiction/utils/truth_table_utils.hpp @@ -5,9 +5,12 @@ #ifndef FICTION_TRUTH_TABLE_UTILS_HPP #define FICTION_TRUTH_TABLE_UTILS_HPP +#include #include #include +#include +#include #include #include @@ -409,6 +412,26 @@ namespace fiction return std::vector{table1, table2}; } +/** + * This function evaluates the given multi-output truth table at the given input index. + * + * @param truth_tables The truth tables to evaluate. + * @param current_input_index The index representing the current input pattern. + * @return Output of the truth tables. + */ +[[nodiscard]] inline uint64_t evaluate_output(const std::vector& truth_tables, + const uint64_t current_input_index) noexcept +{ + assert(truth_tables.size() <= 64 && "Number of truth tables exceeds 64"); + + std::bitset<64> bits{}; + for (auto i = 0u; i < truth_tables.size(); i++) + { + bits[i] = (kitty::get_bit(truth_tables[i], current_input_index) != 0u); + } + return bits.to_ulong(); +} + // NOLINTEND(*-pointer-arithmetic) } // namespace fiction diff --git a/test/algorithms/iter/bdl_input_iterator.cpp b/test/algorithms/iter/bdl_input_iterator.cpp index e97b8ae90..598630fe8 100644 --- a/test/algorithms/iter/bdl_input_iterator.cpp +++ b/test/algorithms/iter/bdl_input_iterator.cpp @@ -8,12 +8,12 @@ #include #include -#include #include #include #include #include +#include #include #include diff --git a/test/algorithms/physical_design/design_sidb_gates.cpp b/test/algorithms/physical_design/design_sidb_gates.cpp index 2fcc650df..74e337a38 100644 --- a/test/algorithms/physical_design/design_sidb_gates.cpp +++ b/test/algorithms/physical_design/design_sidb_gates.cpp @@ -17,12 +17,13 @@ #include #include #include -#include #include #include #include #include +#include + #include #include @@ -35,9 +36,9 @@ TEST_CASE("Design AND gate with skeleton, where one input wire and the output wi design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.31}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::QUICKCELL, - {{25, 6, 0}, {30, 8, 0}}, + {{27, 6, 0}, {30, 8, 0}}, 3}; SECTION("QuickCell") @@ -45,15 +46,16 @@ TEST_CASE("Design AND gate with skeleton, where one input wire and the output wi design_sidb_gates_stats design_gates_stats{}; const auto found_gate_layouts = design_sidb_gates(lyt, std::vector{create_and_tt()}, params, &design_gates_stats); - REQUIRE(found_gate_layouts.size() == 20); + REQUIRE(found_gate_layouts.size() == 10); const auto& first_gate = found_gate_layouts.front(); CHECK(is_operational(first_gate, std::vector{create_and_tt()}, params.operational_params).first == operational_status::OPERATIONAL); - CHECK(design_gates_stats.number_of_layouts == 4060); - CHECK(design_gates_stats.number_of_layouts_after_first_pruning == 1301); - CHECK(design_gates_stats.number_of_layouts_after_second_pruning == 418); - CHECK(design_gates_stats.number_of_layouts_after_third_pruning == 21); + CHECK(design_gates_stats.number_of_layouts == 1140); + CHECK(design_gates_stats.number_of_layouts_after_first_pruning == 167); + CHECK(design_gates_stats.number_of_layouts_after_second_pruning == 46); + CHECK(design_gates_stats.number_of_layouts_after_third_pruning == 11); + CHECK(design_gates_stats.time_total.count() > 0); } SECTION("Automatic Exhaustive Gate Designer") @@ -62,7 +64,7 @@ TEST_CASE("Design AND gate with skeleton, where one input wire and the output wi cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER; const auto found_gate_layouts = design_sidb_gates(lyt, std::vector{create_and_tt()}, params); - REQUIRE(found_gate_layouts.size() == 20); + REQUIRE(found_gate_layouts.size() == 10); const auto& first_gate = found_gate_layouts.front(); CHECK(is_operational(first_gate, std::vector{create_and_tt()}, params.operational_params).first == operational_status::OPERATIONAL); @@ -366,7 +368,8 @@ TEST_CASE("Design AND Bestagon shaped gate", "[design-sidb-gates]") { const design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::TOLERATE_KINKS}, + bdl_input_iterator_params{}, + is_operational_params::operational_condition::TOLERATE_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::RANDOM, {{14, 6, 0}, {24, 12, 0}}, 3}; @@ -382,7 +385,8 @@ TEST_CASE("Design AND Bestagon shaped gate", "[design-sidb-gates]") design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, + is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::RANDOM, {{18, 8, 1}, {22, 12, 0}}, 2}; @@ -473,7 +477,8 @@ TEST_CASE("Design NOR Bestagon shaped gate on H-Si 111", "[design-sidb-gates]") { const design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, + is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params< cell>::design_sidb_gates_mode::AUTOMATIC_EXHAUSTIVE_GATE_DESIGNER, {{10, 13, 0}, {15, 17, 0}}, @@ -563,7 +568,8 @@ TEST_CASE("Design AND gate with input left and output top-right with QuickCell ( { const design_sidb_gates_params> params{ is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}, + bdl_input_iterator_params{}, + is_operational_params::operational_condition::REJECT_KINKS}, design_sidb_gates_params>::design_sidb_gates_mode::QUICKCELL, {{17, 5, 0}, {24, 8, 0}}, 3}; diff --git a/test/algorithms/simulation/sidb/critical_temperature.cpp b/test/algorithms/simulation/sidb/critical_temperature.cpp index e45d87d56..1857507db 100644 --- a/test/algorithms/simulation/sidb/critical_temperature.cpp +++ b/test/algorithms/simulation/sidb/critical_temperature.cpp @@ -228,7 +228,9 @@ TEMPLATE_TEST_CASE("Test critical_temperature function", "[critical-temperature] } SECTION("Kinks are not allowed") { - params.operational_params.op_condition = operational_condition::REJECT_KINKS; + params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + params.operational_params.input_bdl_iterator_params.bdl_wire_params.threshold_bdl_interdistance = 2.5; + const auto ct = critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), @@ -328,7 +330,7 @@ TEMPLATE_TEST_CASE("Test critical_temperature function", "[critical-temperature] } SECTION("Kinks are not allowed") { - params.operational_params.op_condition = operational_condition::REJECT_KINKS; + params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto ct = critical_temperature_gate_based(lyt, std::vector{create_fan_out_tt()}, params, &critical_stats); @@ -398,7 +400,7 @@ TEMPLATE_TEST_CASE("Test critical_temperature function", "[critical-temperature] } SECTION("Kinks are not allowed") { - params.operational_params.op_condition = operational_condition::REJECT_KINKS; + params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto ct = critical_temperature_gate_based(lyt, std::vector{create_or_tt()}, params, &critical_stats); @@ -702,7 +704,7 @@ TEMPLATE_TEST_CASE("Critical temperature of Bestagon double wire, QuickExact", " } SECTION("Kinks are not allowed") { - params.operational_params.op_condition = operational_condition::REJECT_KINKS; + params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto ct = critical_temperature_gate_based(lyt_double_wire_gate, create_double_wire_tt(), params, &critical_stats); @@ -738,7 +740,7 @@ TEMPLATE_TEST_CASE("Critical temperature of Bestagon half adder gate, QuickExact } SECTION("Kinks are not allowed") { - params.operational_params.op_condition = operational_condition::REJECT_KINKS; + params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto ct = critical_temperature_gate_based(lyt_half_adder_gate, create_half_adder_tt(), params, &critical_stats); diff --git a/test/algorithms/simulation/sidb/is_operational.cpp b/test/algorithms/simulation/sidb/is_operational.cpp index 6553d6cd4..810452c90 100644 --- a/test/algorithms/simulation/sidb/is_operational.cpp +++ b/test/algorithms/simulation/sidb/is_operational.cpp @@ -27,39 +27,110 @@ using namespace fiction; TEST_CASE("SiQAD OR gate", "[is-operational]") { - const auto layout_or_gate = blueprints::siqad_or_gate(); + const auto or_gate = blueprints::siqad_or_gate(); - const sidb_100_cell_clk_lyt_siqad lat{layout_or_gate}; + const sidb_100_cell_clk_lyt_siqad lat{or_gate}; auto op_params = is_operational_params{ - sidb_simulation_parameters{2, -0.28}, sidb_simulation_engine::QUICKEXACT, + sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, bdl_input_iterator_params{detect_bdl_wires_params{1.5}, bdl_input_iterator_params::input_bdl_configuration::PERTURBER_ABSENCE_ENCODED}, - operational_condition::REJECT_KINKS}; + is_operational_params::operational_condition::TOLERATE_KINKS}; + + SECTION("determine if layout is operational, tolerate kinks") + { + CHECK(is_operational(lat, std::vector{create_or_tt()}, op_params).first == operational_status::OPERATIONAL); + } + + // from now on, we will reject kinks + op_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + + SECTION("determine if layout is operational, accept kinks") + { + CHECK(is_operational(lat, std::vector{create_and_tt()}, op_params).first == + operational_status::NON_OPERATIONAL); + } + + SECTION("determine if kinks induce layout to become non-operational") + { + const auto kink_induced_non_operational = + is_kink_induced_non_operational(lat, std::vector{create_or_tt()}, op_params); + CHECK(kink_induced_non_operational); + } + + const auto input_wires = detect_bdl_wires(lat, detect_bdl_wires_params{1.5}, bdl_wire_selection::INPUT); + const auto output_wires = detect_bdl_wires(lat, detect_bdl_wires_params{1.5}, bdl_wire_selection::OUTPUT); - CHECK(is_operational(lat, std::vector{create_or_tt()}, op_params).first == operational_status::NON_OPERATIONAL); + REQUIRE(input_wires.size() == 2); - // determine if kinks induce layout to become non-operational. - const auto kink_induced_non_operational = - is_kink_induced_non_operational(lat, std::vector{create_or_tt()}, op_params); - CHECK(kink_induced_non_operational); + CHECK(input_wires[0].pairs.size() == 2); + CHECK(input_wires[1].pairs.size() == 2); - const auto input_wires = detect_bdl_wires(lat, detect_bdl_wires_params{1.5}); - const auto output_wires = detect_bdl_wires(lat, detect_bdl_wires_params{1.5}); + CHECK(output_wires.size() == 1); + + SECTION("use pre-determined I/O pins") + { + CHECK(is_operational(lat, std::vector{create_and_tt()}, op_params, input_wires, output_wires).first == + operational_status::NON_OPERATIONAL); + } + + SECTION("determine if kinks induce layout to become non-operational") + { + CHECK(is_kink_induced_non_operational(lat, std::vector{create_or_tt()}, op_params, input_wires, + output_wires)); + } - // determine if kinks induce layout to become non-operational. - const auto kink_induced_non_operational_predefined_wires = is_kink_induced_non_operational( - lat, std::vector{create_or_tt()}, op_params, std::optional{input_wires}, std::optional{output_wires}); - CHECK(kink_induced_non_operational_predefined_wires); + SECTION("determine input patterns for which kinks induce layout to become non-operational") + { + const auto kink_induced_non_operational_input_pattern = + kink_induced_non_operational_input_patterns(lat, std::vector{create_or_tt()}, op_params); + + CHECK(kink_induced_non_operational_input_pattern.size() == 1); + + op_params.op_condition = is_operational_params::operational_condition::TOLERATE_KINKS; + CHECK(is_operational(lat, std::vector{create_or_tt()}, op_params).first == operational_status::OPERATIONAL); + } +} + +TEST_CASE("SiQAD NAND gate", "[is-operational]") +{ + const auto nand_gate = blueprints::siqad_nand_gate(); + + const sidb_100_cell_clk_lyt_siqad lat{nand_gate}; + + auto op_params = is_operational_params{ + sidb_simulation_parameters{2, -0.28}, sidb_simulation_engine::QUICKEXACT, + bdl_input_iterator_params{detect_bdl_wires_params{1.5}, + bdl_input_iterator_params::input_bdl_configuration::PERTURBER_ABSENCE_ENCODED}, + is_operational_params::operational_condition::REJECT_KINKS, + is_operational_params::operational_analysis_strategy::FILTER_THEN_SIMULATION}; + + SECTION("Pruning and simulation") + { + CHECK(is_operational(lat, std::vector{create_nand_tt()}, op_params).first == + operational_status::OPERATIONAL); + } + SECTION("only pruning") + { + op_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + CHECK(is_operational(lat, std::vector{create_nand_tt()}, op_params).first == + operational_status::OPERATIONAL); + } - // determine input patterns for which kinks induce layout to become non-operational. - const auto kink_induced_non_operational_input_pattern = - kink_induced_non_operational_input_patterns(lat, std::vector{create_or_tt()}, op_params); + const auto input_wires = detect_bdl_wires(lat, detect_bdl_wires_params{2.0}, bdl_wire_selection::INPUT); + const auto output_wires = detect_bdl_wires(lat, detect_bdl_wires_params{2.0}, bdl_wire_selection::OUTPUT); - CHECK(kink_induced_non_operational_input_pattern.size() == 1); + sidb_100_cell_clk_lyt_siqad canvas_lyt{}; + canvas_lyt.assign_cell_type({10, 4, 1}, sidb_technology::cell_type::NORMAL); + canvas_lyt.assign_cell_type({10, 5, 1}, sidb_technology::cell_type::NORMAL); - op_params.op_condition = operational_condition::TOLERATE_KINKS; - CHECK(is_operational(lat, std::vector{create_or_tt()}, op_params).first == operational_status::OPERATIONAL); + SECTION("use pre-determined I/O pins") + { + CHECK(is_operational(lat, std::vector{create_nand_tt()}, op_params, input_wires, output_wires, + std::optional{canvas_lyt}) + .first == operational_status::OPERATIONAL); + } } TEST_CASE("SiQAD's AND gate with input BDL pairs of different size", "[is-operational]") @@ -235,11 +306,12 @@ TEST_CASE("Not working diagonal Wire", "[is-operational]") .first == operational_status::NON_OPERATIONAL); } -TEMPLATE_TEST_CASE("AND gate on the H-Si(111)-1x1 surface", "[is-operational]", sidb_111_cell_clk_lyt_siqad) +TEMPLATE_TEST_CASE("AND gate on the H-Si(111)-1x1 surface", "[is-operational]", sidb_111_cell_clk_lyt_siqad, + cds_sidb_111_cell_clk_lyt_siqad) { const auto lyt = blueprints::and_gate_111(); - SECTION("Check operation for different values of mu") + SECTION("check operation for different values of mu") { const auto op_inputs = operational_input_patterns( lyt, std::vector{create_and_tt()}, @@ -247,7 +319,7 @@ TEMPLATE_TEST_CASE("AND gate on the H-Si(111)-1x1 surface", "[is-operational]", CHECK(op_inputs.size() == 4); CHECK(op_inputs == std::set{0, 1, 2, 3}); } - SECTION("Count the number of non-operational input combinations") + SECTION("count the number of non-operational input combinations") { const auto op_inputs = operational_input_patterns( lyt, std::vector{create_and_tt()}, @@ -256,7 +328,7 @@ TEMPLATE_TEST_CASE("AND gate on the H-Si(111)-1x1 surface", "[is-operational]", CHECK(op_inputs == std::set{0, 3}); } - SECTION("Verify the operational status of the AND gate, which is mirrored on the x-axis. Note that the input BDL " + SECTION("verify the operational status of the AND gate, which is mirrored on the x-axis. Note that the input BDL " "pairs are located at the bottom, while the output BDL pairs are at the top.") { const auto lyt_mirrored_x = blueprints::and_gate_111_mirrored_on_the_x_axis(); @@ -280,12 +352,12 @@ TEST_CASE( is_operational_params{sidb_simulation_parameters{2, -0.32}}) .first == operational_status::OPERATIONAL); } - SECTION("Forbid kink states") + SECTION("reject kink states") { CHECK(is_operational(lyt, std::vector{create_and_tt()}, is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, bdl_input_iterator_params{}, - operational_condition::REJECT_KINKS}) + is_operational_params::operational_condition::REJECT_KINKS}) .first == operational_status::NON_OPERATIONAL); } SECTION("check if is_kink_induced_non_operational returns true") @@ -294,7 +366,8 @@ TEST_CASE( CHECK(is_kink_induced_non_operational( lyt, std::vector{create_and_tt()}, is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::TOLERATE_KINKS})); + bdl_input_iterator_params{}, + is_operational_params::operational_condition::TOLERATE_KINKS})); } SECTION("check input patterns for which kinks induce the layout to become non-operational") @@ -302,7 +375,8 @@ TEST_CASE( CHECK(kink_induced_non_operational_input_patterns( lyt, std::vector{create_and_tt()}, is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::TOLERATE_KINKS}) == + bdl_input_iterator_params{}, + is_operational_params::operational_condition::TOLERATE_KINKS}) == std::set{1, 2}); } } @@ -339,6 +413,60 @@ TEST_CASE("BDL wire", "[is-operational]") CHECK(is_operational(lyt, std::vector{create_id_tt()}, params).first == operational_status::OPERATIONAL); } +TEST_CASE("Special wire that cannot be pruned, but is non-operational when kinks are rejected", "[is-operational]") +{ + sidb_cell_clk_lyt_siqad lyt{}; + + // input wires + lyt.assign_cell_type({0, 0, 0}, sidb_cell_clk_lyt_siqad::cell_type::INPUT); + lyt.assign_cell_type({2, 1, 0}, sidb_cell_clk_lyt_siqad::cell_type::INPUT); + + lyt.assign_cell_type({6, 2, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + lyt.assign_cell_type({8, 3, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + + lyt.assign_cell_type({14, 5, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + lyt.assign_cell_type({12, 4, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + + // canvas SiDBs + lyt.assign_cell_type({11, 7, 0}, sidb_cell_clk_lyt_siqad::cell_type::LOGIC); + lyt.assign_cell_type({13, 13, 0}, sidb_cell_clk_lyt_siqad::cell_type::LOGIC); + + // output wires + lyt.assign_cell_type({14, 15, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + lyt.assign_cell_type({12, 16, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + + lyt.assign_cell_type({8, 17, 0}, sidb_cell_clk_lyt_siqad::cell_type::OUTPUT); + lyt.assign_cell_type({6, 18, 0}, sidb_cell_clk_lyt_siqad::cell_type::OUTPUT); + + lyt.assign_cell_type({2, 19, 0}, sidb_cell_clk_lyt_siqad::cell_type::NORMAL); + + sidb_simulation_parameters sim_params{}; + + sim_params.base = 2; + + is_operational_params params{sim_params}; + + SECTION("Rejecting Kinks") + { + params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_THEN_SIMULATION; + + CHECK(is_operational(lyt, std::vector{create_id_tt()}, params).first == + operational_status::NON_OPERATIONAL); + } + + SECTION("Only conducting pruning and tolerating kinks") + { + params.op_condition = is_operational_params::operational_condition::TOLERATE_KINKS; + params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + CHECK(is_operational(lyt, std::vector{create_id_tt()}, params).first == + operational_status::NON_OPERATIONAL); + } +} + // to save runtime in the CI, this test is only run in RELEASE mode #ifdef NDEBUG TEST_CASE("flipped CX bestagon gate", "[is-operational]") @@ -347,20 +475,21 @@ TEST_CASE("flipped CX bestagon gate", "[is-operational]") CHECK(is_operational(lyt, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}) + bdl_input_iterator_params{}, + is_operational_params::operational_condition::REJECT_KINKS}) .first == operational_status::OPERATIONAL); const auto kink_induced_non_operational_input_pattern = kink_induced_non_operational_input_patterns( lyt, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}); + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}); CHECK(kink_induced_non_operational_input_pattern.empty()); const auto kink_induced_non_operational = is_kink_induced_non_operational( lyt, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT, - bdl_input_iterator_params{}, operational_condition::REJECT_KINKS}); + bdl_input_iterator_params{}, is_operational_params::operational_condition::REJECT_KINKS}); CHECK(!kink_induced_non_operational); } @@ -393,17 +522,30 @@ TEST_CASE("is operational check for Bestagon CX gate", "[is-operational], [quali CHECK(is_operational( lat, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.32}, sidb_simulation_engine::QUICKEXACT}, - std::optional{input_bdl_wires}, std::optional{output_bdl_wires}) + input_bdl_wires, output_bdl_wires) .first == operational_status::OPERATIONAL); CHECK(is_operational( lat, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.30}, sidb_simulation_engine::QUICKEXACT}, - std::optional{input_bdl_wires}, std::optional{output_bdl_wires}) + input_bdl_wires, output_bdl_wires) .first == operational_status::NON_OPERATIONAL); CHECK(!is_kink_induced_non_operational( lat, create_crossing_wire_tt(), is_operational_params{sidb_simulation_parameters{2, -0.30}, sidb_simulation_engine::QUICKEXACT}, - std::optional{input_bdl_wires}, std::optional{output_bdl_wires})); + input_bdl_wires, output_bdl_wires)); + } + + SECTION("using predetermined wires and only applying pruning without simulation") + { + const auto input_bdl_wires = detect_bdl_wires(lat, detect_bdl_wires_params{}, bdl_wire_selection::INPUT); + const auto output_bdl_wires = detect_bdl_wires(lat, detect_bdl_wires_params{}, bdl_wire_selection::OUTPUT); + + auto op_params = is_operational_params{sidb_simulation_parameters{2, -0.32}}; + op_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + CHECK(is_operational(lat, create_crossing_wire_tt(), op_params, input_bdl_wires, output_bdl_wires).first == + operational_status::OPERATIONAL); } } diff --git a/test/algorithms/simulation/sidb/operational_domain.cpp b/test/algorithms/simulation/sidb/operational_domain.cpp index d44bb3056..cff95a44f 100644 --- a/test/algorithms/simulation/sidb/operational_domain.cpp +++ b/test/algorithms/simulation/sidb/operational_domain.cpp @@ -887,6 +887,7 @@ TEST_CASE("BDL wire operational domain computation", "[operational-domain]") 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(lat, std::vector{create_id_tt()}, 100, @@ -904,6 +905,7 @@ TEST_CASE("BDL wire operational domain computation", "[operational-domain]") CHECK(op_domain_stats.num_operational_parameter_combinations <= 100); CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 100); } + SECTION("flood_fill") { SECTION("random sample to find operational parameter points") @@ -1284,9 +1286,9 @@ TEMPLATE_TEST_CASE("AND gate with Bestagon shape and kink states at default phys CHECK(op_domain_stats.num_non_operational_parameter_combinations == 33); } - SECTION("grid_search, forbid kinks") + SECTION("grid_search, reject kinks") { - op_domain_params.operational_params.op_condition = operational_condition::REJECT_KINKS; + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const auto op_domain = operational_domain_grid_search(layout, std::vector{create_and_tt()}, op_domain_params, &op_domain_stats); @@ -1299,3 +1301,60 @@ TEMPLATE_TEST_CASE("AND gate with Bestagon shape and kink states at default phys CHECK(op_domain_stats.num_non_operational_parameter_combinations == 36); } } + +TEMPLATE_TEST_CASE("Grid search to determine the operational domain. The operational status is determined by physical " + "simulation and the efficient but approximate method of pruning only.", + "[operational-domain]", sidb_100_cell_clk_lyt_siqad) +{ + const auto layout = blueprints::bestagon_and(); + + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + sim_params.mu_minus = -0.32; + + operational_domain_params op_domain_params{}; + op_domain_params.operational_params.simulation_parameters = sim_params; + op_domain_params.sweep_dimensions = {{sweep_parameter::EPSILON_R, 4.0, 6.0, 0.4}, + {sweep_parameter::LAMBDA_TF, 4.0, 6.0, 0.4}}; + + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + + operational_domain_stats op_domain_stats{}; + + SECTION("grid search, determine operational status with physical simulation") + { + const auto op_domain = operational_domain_grid_search(layout, std::vector{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() == 36); + + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 36); + CHECK(op_domain_stats.num_operational_parameter_combinations == 5); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 31); + } + + SECTION("grid search, determine operational status with only pruning") + { + op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + const auto op_domain = operational_domain_grid_search(layout, std::vector{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() == 36); + + CHECK(op_domain_stats.num_evaluated_parameter_combinations == 36); + CHECK(op_domain_stats.num_operational_parameter_combinations == 5); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 31); + + // this test was created to cover a special case: Strange behavior was observed when no clone was used in the + // `is_physical_validity_feasible` function. + for (const auto& [pp, status] : op_domain.operational_values) + { + CHECK(pp.parameters[0] >= 4.0); + CHECK(pp.parameters[1] >= 4.0); + } + } +} diff --git a/test/algorithms/simulation/sidb/operational_domain_ratio.cpp b/test/algorithms/simulation/sidb/operational_domain_ratio.cpp index 70cbfaba2..5a13bc3c6 100644 --- a/test/algorithms/simulation/sidb/operational_domain_ratio.cpp +++ b/test/algorithms/simulation/sidb/operational_domain_ratio.cpp @@ -69,8 +69,8 @@ TEST_CASE("BDL wire operational domain computation", "[compute-operational-ratio const auto op_domain_ratio = operational_domain_ratio(lat, std::vector{create_id_tt()}, parameter_point({5.5, 5.0, -0.32}), op_ratio_params); - // check if the operational domain has the correct size - CHECK(op_domain_ratio == 1.0); + // check if the operational domain has the correct size (1.0) + CHECK_THAT(op_domain_ratio - 1.0, Catch::Matchers::WithinAbs(0.0, physical_constants::POP_STABILITY_ERR)); } SECTION("semi-operational domain") @@ -90,11 +90,61 @@ TEST_CASE("BDL wire operational domain computation", "[compute-operational-ratio const auto op_domain_ratio = operational_domain_ratio(lat, std::vector{create_id_tt()}, parameter_point({4.25, 4.25}), op_ratio_params); - CHECK_THAT(op_domain_ratio - 80.0 / 256.0, + CHECK_THAT(op_domain_ratio - (80.0 / 256.0), Catch::Matchers::WithinAbs(0.0, physical_constants::POP_STABILITY_ERR)); } } +TEST_CASE("SiQAD NAND gate", "[compute-operational-ratio]") +{ + const auto lyt = blueprints::siqad_nand_gate(); + + sidb_simulation_parameters sim_params{}; + sim_params.base = 2; + sim_params.mu_minus = -0.28; + + operational_domain_params op_domain_params{}; + op_domain_params.operational_params.simulation_parameters = sim_params; + op_domain_params.operational_params.input_bdl_iterator_params.input_bdl_config = + bdl_input_iterator_params::input_bdl_configuration::PERTURBER_ABSENCE_ENCODED; + op_domain_params.sweep_dimensions = {{sweep_parameter::EPSILON_R}, {sweep_parameter::LAMBDA_TF}}; + op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_THEN_SIMULATION; + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + op_domain_params.operational_params.input_bdl_iterator_params.bdl_wire_params.threshold_bdl_interdistance = 1.5; + + // set x-dimension + op_domain_params.sweep_dimensions[0].min = 2.0; + op_domain_params.sweep_dimensions[0].max = 10.0; + op_domain_params.sweep_dimensions[0].step = 0.1; + + // set y-dimension + op_domain_params.sweep_dimensions[1].min = 2.0; + op_domain_params.sweep_dimensions[1].max = 10.0; + op_domain_params.sweep_dimensions[1].step = 0.1; + + operational_domain_ratio_params op_ratio_params{op_domain_params}; + + // pruning and simulation to determine the operational status of the layout + const auto op_domain_ratio_pruning_and_simulation = operational_domain_ratio( + lyt, std::vector{create_nand_tt()}, parameter_point({5.6, 5.0, -0.28}), op_ratio_params); + + // only pruning to determine the operational status of the layout + op_ratio_params.op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + const auto op_domain_ratio_only_pruning = operational_domain_ratio( + lyt, std::vector{create_nand_tt()}, parameter_point({5.6, 5.0, -0.28}), op_ratio_params); + + CHECK_THAT(op_domain_ratio_pruning_and_simulation, + Catch::Matchers::WithinAbs(0.11918914799573235, physical_constants::POP_STABILITY_ERR)); + + CHECK_THAT(op_domain_ratio_only_pruning, + Catch::Matchers::WithinAbs(0.11918914799573235, physical_constants::POP_STABILITY_ERR)); +} + +// to save runtime in the CI, this test is only run in RELEASE mode +#ifdef NDEBUG TEST_CASE("Bestagon AND gate", "[compute-operational-ratio]") { const auto lyt = blueprints::bestagon_and_gate(); @@ -116,11 +166,26 @@ TEST_CASE("Bestagon AND gate", "[compute-operational-ratio]") op_domain_params.sweep_dimensions[1].max = 6.0; op_domain_params.sweep_dimensions[1].step = 0.1; + const auto z_dimension = operational_domain_value_range{sweep_parameter::MU_MINUS, -0.32, -0.32, 0.01}; + SECTION("semi-operational domain") { - const auto z_dimension = operational_domain_value_range{sweep_parameter::MU_MINUS, -0.32, -0.32, 0.01}; + op_domain_params.sweep_dimensions.push_back(z_dimension); + + const operational_domain_ratio_params op_ratio_params{op_domain_params}; + + const auto op_domain_ratio = operational_domain_ratio(lyt, std::vector{create_and_tt()}, + parameter_point({5.6, 5.0, -0.32}), op_ratio_params); + + // check if the operational domain has the correct size + CHECK_THAT(op_domain_ratio - (23.0 / 121.0), + Catch::Matchers::WithinAbs(0.0, physical_constants::POP_STABILITY_ERR)); + } + SECTION("semi-operational domain, reject kinks") + { op_domain_params.sweep_dimensions.push_back(z_dimension); + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; const operational_domain_ratio_params op_ratio_params{op_domain_params}; @@ -128,7 +193,25 @@ TEST_CASE("Bestagon AND gate", "[compute-operational-ratio]") parameter_point({5.6, 5.0, -0.32}), op_ratio_params); // check if the operational domain has the correct size - CHECK_THAT(op_domain_ratio - 23.0 / 121.0, + CHECK_THAT(op_domain_ratio - (23.0 / 121.0), Catch::Matchers::WithinAbs(0.0, physical_constants::POP_STABILITY_ERR)); } + + SECTION( + "semi-operational domain, reject kinks, only pruning is used to determine the operational status of the layout") + { + op_domain_params.sweep_dimensions.push_back(z_dimension); + op_domain_params.operational_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; + op_domain_params.operational_params.strategy_to_analyze_operational_status = + is_operational_params::operational_analysis_strategy::FILTER_ONLY; + + const operational_domain_ratio_params op_ratio_params{op_domain_params}; + + const auto op_domain_ratio = operational_domain_ratio(lyt, std::vector{create_and_tt()}, + parameter_point({5.6, 5.0, -0.32}), op_ratio_params); + + // check if the operational domain has the correct size + CHECK(op_domain_ratio >= (23.0 / 121.0)); + } } +#endif diff --git a/test/algorithms/simulation/sidb/quickexact.cpp b/test/algorithms/simulation/sidb/quickexact.cpp index 16b140774..611467b00 100644 --- a/test/algorithms/simulation/sidb/quickexact.cpp +++ b/test/algorithms/simulation/sidb/quickexact.cpp @@ -113,7 +113,8 @@ TEMPLATE_TEST_CASE( TEMPLATE_TEST_CASE( "four SiDBs QuickExact simulation with one negatively charge defect (changed epsilon_r) in proximity", "[quickexact]", (sidb_defect_surface), - (charge_distribution_surface>)) + charge_distribution_surface>, + sidb_defect_surface>) { TestType lyt{}; lyt.assign_cell_type({-2, 0, 1}, TestType::cell_type::NORMAL); diff --git a/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp b/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp index f1272ab13..789b85c78 100644 --- a/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp +++ b/test/algorithms/simulation/sidb/random_sidb_layout_generator.cpp @@ -478,19 +478,6 @@ TEST_CASE("Random siqad::coord_t layout generation", "[random-sidb-layout-genera CHECK(cell.y < 91); CHECK(cell.z <= 1); }); - // check if all cells are not closer than two cells (Euclidean distance). - result_lyt.foreach_cell( - [&result_lyt](const auto& cell_one) - { - result_lyt.foreach_cell( - [&cell_one, &result_lyt](const auto& cell_two) - { - if (cell_one != cell_two) - { - CHECK(euclidean_distance(result_lyt, cell_one, cell_two) >= 2); - } - }); - }); } SECTION("given previous layouts") diff --git a/test/algorithms/simulation/sidb/verify_logic_match.cpp b/test/algorithms/simulation/sidb/verify_logic_match.cpp index 9b55d5c34..4d942f6e0 100644 --- a/test/algorithms/simulation/sidb/verify_logic_match.cpp +++ b/test/algorithms/simulation/sidb/verify_logic_match.cpp @@ -110,7 +110,7 @@ TEST_CASE("AND gate mirrored on the x-axis on the H-Si 111 surface", REQUIRE(!gs.empty()); is_operational_params op_params{}; - op_params.op_condition = operational_condition::REJECT_KINKS; + op_params.op_condition = is_operational_params::operational_condition::REJECT_KINKS; SECTION("Correct index") { diff --git a/test/benchmark/simulation.cpp b/test/benchmark/simulation.cpp index 4b3e8cc71..62ffff90c 100644 --- a/test/benchmark/simulation.cpp +++ b/test/benchmark/simulation.cpp @@ -72,22 +72,9 @@ TEST_CASE("Benchmark simulators", "[benchmark]") return quicksim(lyt, quicksim_params); }; } -// Mac M1, Ventura 13.0, Apple clang version 14.0.0 (24.07.24) +// Mac M1, Sequoia 15.2, Apple clang version 14.0.3 (16.12.24) // -// Before PR #483: -// benchmark name samples iterations est run time -// mean low mean high mean -// std dev low std dev high std dev -// ----------------------------------------------------------------------------- -// QuickExact 100 1 1.7502 m -// 1.06044 s 1.05536 s 1.06813 s -// 31.4551 ms 23.1414 ms 44.911 ms -// -// QuickSim 100 1 570.56 ms -// 6.02459 ms 5.92537 ms 6.12262 ms -// 505.788 us 450.737 us 574.389 us - -// PR #483: +// Before PR #602: // benchmark name samples iterations est run time // mean low mean high mean // std dev low std dev high std dev @@ -99,3 +86,16 @@ TEST_CASE("Benchmark simulators", "[benchmark]") // QuickSim 100 1 492.005 ms // 4.85343 ms 4.80376 ms 4.98192 ms // 381.332 us 184.008 us 801.102 us +// +// PR #602: +// benchmark name samples iterations est run time +// mean low mean high mean +// std dev low std dev high std dev +// ------------------------------------------------------------------------------- +// QuickExact 100 1 1.68503 m +// 1.01966 s 1.01725 s 1.02271 s +// 13.7569 ms 11.465 ms 18.3512 ms +// +// QuickSim 100 1 445.639 ms +// 4.50754 ms 4.47813 ms 4.54016 ms +// 158.347 us 137.998 us 187.498 us diff --git a/test/layouts/cell_level_layout.cpp b/test/layouts/cell_level_layout.cpp index c7b705f89..b527e9884 100644 --- a/test/layouts/cell_level_layout.cpp +++ b/test/layouts/cell_level_layout.cpp @@ -115,8 +115,24 @@ TEST_CASE("Cell technology", "[cell-level-layout]") CHECK(tech_impl_name == std::string{"SiDB"}); - CHECK(has_sidb_technology_v); - CHECK(has_sidb_technology_v); + CHECK(has_sidb_technology_v); + CHECK(has_sidb_technology_v); + + sidb_cell_clk_lyt_siqad lyt{{15, 6}}; + lyt.assign_cell_type({0, 0}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({2, 1}, sidb_technology::cell_type::INPUT); + lyt.assign_cell_type({6, 2}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({8, 3}, sidb_technology::cell_type::NORMAL); + lyt.assign_cell_type({12, 4}, sidb_technology::cell_type::LOGIC); + lyt.assign_cell_type({14, 5}, sidb_technology::cell_type::OUTPUT); + + const auto input_cells = lyt.get_cells_by_type(sidb_technology::cell_type::INPUT); + const auto output_cells = lyt.get_cells_by_type(sidb_technology::cell_type::OUTPUT); + const auto logic_cells = lyt.get_cells_by_type(sidb_technology::cell_type::LOGIC); + + CHECK(input_cells.size() == 2); + CHECK(output_cells.size() == 1); + CHECK(logic_cells.size() == 1); } } @@ -285,7 +301,7 @@ TEST_CASE("Clock zone assignment to cells", "[cell-level-layout]") { using clk_cell_lyt = cell_level_layout>>; - clk_cell_lyt layout{clk_cell_lyt::aspect_ratio{4, 4, 0}, twoddwave_clocking(), "Lyt", 2, 2}; + const clk_cell_lyt layout{clk_cell_lyt::aspect_ratio{4, 4, 0}, twoddwave_clocking(), "Lyt", 2, 2}; CHECK(layout.get_clock_number({0, 0}) == 0); CHECK(layout.get_clock_number({0, 1}) == 0); diff --git a/test/utils/blueprints/layout_blueprints.hpp b/test/utils/blueprints/layout_blueprints.hpp index c4c7f1854..92ef4a286 100644 --- a/test/utils/blueprints/layout_blueprints.hpp +++ b/test/utils/blueprints/layout_blueprints.hpp @@ -833,6 +833,43 @@ Lyt siqad_or_gate() noexcept return lyt; }; +/** + * This layout represents the NAND Gate, as proposed in the paper titled \"SiQAD: A Design and Simulation Tool for + * Atomic Silicon Quantum Dot Circuits\" by Samuel Sze Hang Ng, Jacob Retallick, Hsi Nien Chiu, Robert Lupoiu, Lucian + * Livadaru, Taleana Huff, Mohammad Rashidi, Wyatt Vine, Thomas Dienel, Robert A. Wolkow, and Konrad Walus in IEEE + * TRANSACTIONS ON NANOTECHNOLOGY, Volume 19, 2020. + */ +template +Lyt siqad_nand_gate() noexcept +{ + static_assert(fiction::is_cell_level_layout_v, "Lyt is not a cell-level layout"); + static_assert(fiction::has_sidb_technology_v, "Lyt is not an SiDB layout"); + static_assert(fiction::has_siqad_coord_v, "Lyt is not based on SiQAD coordinates"); + + Lyt lyt{}; + + lyt.assign_cell_type({0, 0, 1}, Lyt::cell_type::INPUT); + lyt.assign_cell_type({2, 1, 1}, Lyt::cell_type::INPUT); + + lyt.assign_cell_type({20, 0, 1}, Lyt::cell_type::INPUT); + lyt.assign_cell_type({18, 1, 1}, Lyt::cell_type::INPUT); + + lyt.assign_cell_type({4, 2, 1}, Lyt::cell_type::NORMAL); + lyt.assign_cell_type({6, 3, 1}, Lyt::cell_type::NORMAL); + + lyt.assign_cell_type({16, 2, 1}, Lyt::cell_type::NORMAL); + lyt.assign_cell_type({14, 3, 1}, Lyt::cell_type::NORMAL); + + lyt.assign_cell_type({10, 4, 1}, Lyt::cell_type::LOGIC); + lyt.assign_cell_type({10, 5, 1}, Lyt::cell_type::LOGIC); + + lyt.assign_cell_type({10, 8, 0}, Lyt::cell_type::OUTPUT); + lyt.assign_cell_type({10, 9, 1}, Lyt::cell_type::OUTPUT); + + lyt.assign_cell_type({10, 12, 0}, Lyt::cell_type::NORMAL); + + return lyt; +}; /** * This layout represents the AND Gate, as proposed in the paper * titled \"Hexagons are the Bestagons: Design Automation for Silicon Dangling Bond Logic\" by @@ -855,10 +892,10 @@ Lyt bestagon_and_gate() noexcept lyt.assign_cell_type({38, 0, 0}, Lyt::cell_type::INPUT); lyt.assign_cell_type({0, 0, 0}, Lyt::cell_type::INPUT); - lyt.assign_cell_type({23, 9, 0}, Lyt::cell_type::NORMAL); - lyt.assign_cell_type({18, 11, 1}, Lyt::cell_type::NORMAL); - lyt.assign_cell_type({18, 9, 0}, Lyt::cell_type::NORMAL); - lyt.assign_cell_type({19, 8, 0}, Lyt::cell_type::NORMAL); + lyt.assign_cell_type({23, 9, 0}, Lyt::cell_type::LOGIC); + lyt.assign_cell_type({18, 11, 1}, Lyt::cell_type::LOGIC); + lyt.assign_cell_type({18, 9, 0}, Lyt::cell_type::LOGIC); + lyt.assign_cell_type({19, 8, 0}, Lyt::cell_type::LOGIC); lyt.assign_cell_type({20, 14, 0}, Lyt::cell_type::NORMAL); lyt.assign_cell_type({19, 13, 0}, Lyt::cell_type::NORMAL);