From a46155c74d905a54a6d5ebaf6cb8f3f86d6b1db8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:16:29 +0100 Subject: [PATCH 01/24] :arrow_up: Bump the submodules group with 5 updates (#345) Bumps the submodules group with 5 updates: | Package | From | To | | --- | --- | --- | | [libs/Catch2](https://github.com/catchorg/Catch2) | ``de7ba4e`` | ``32d9ae2`` | | [libs/parallel-hashmap](https://github.com/greg7mdp/parallel-hashmap) | ``0046905`` | ``0f0ecf7`` | | [libs/tinyxml2](https://github.com/leethomason/tinyxml2) | ``e059560`` | ``c2d3087`` | | [libs/pybind11](https://github.com/pybind/pybind11) | ``dc9b395`` | ``a67d786`` | | [libs/json](https://github.com/nlohmann/json) | ``6eab7a2`` | ``9cca280`` | Updates `libs/Catch2` from `de7ba4e` to `32d9ae2` - [Release notes](https://github.com/catchorg/Catch2/releases) - [Commits](https://github.com/catchorg/Catch2/compare/de7ba4e8897bbe56c612b980d02f249a92d0eada...32d9ae24bc4a0fed9a136a73e116b4ded0ac84b7) Updates `libs/parallel-hashmap` from `0046905` to `0f0ecf7` - [Release notes](https://github.com/greg7mdp/parallel-hashmap/releases) - [Commits](https://github.com/greg7mdp/parallel-hashmap/compare/0046905791006b5a85dbf62f7a98c65987b72e68...0f0ecf724c91c7f6105555d2d11b00985c2feccd) Updates `libs/tinyxml2` from `e059560` to `c2d3087` - [Commits](https://github.com/leethomason/tinyxml2/compare/e05956094c27117f989d22f25b75633123d72a83...c2d30872e20621955ca7feb9168bad996d591a19) Updates `libs/pybind11` from `dc9b395` to `a67d786` - [Release notes](https://github.com/pybind/pybind11/releases) - [Commits](https://github.com/pybind/pybind11/compare/dc9b39596d986aeb061bd3debe52d30e2467dc48...a67d786571cc6d2c2ecec33985e00e6d22dc68e5) Updates `libs/json` from `6eab7a2` to `9cca280` - [Release notes](https://github.com/nlohmann/json/releases) - [Commits](https://github.com/nlohmann/json/compare/6eab7a2b187b10b2494e39c1961750bfd1bda500...9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03) --- updated-dependencies: - dependency-name: libs/Catch2 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/parallel-hashmap dependency-type: direct:production dependency-group: submodules - dependency-name: libs/tinyxml2 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/pybind11 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/json dependency-type: direct:production dependency-group: submodules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/Catch2 | 2 +- libs/json | 2 +- libs/parallel-hashmap | 2 +- libs/pybind11 | 2 +- libs/tinyxml2 | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/Catch2 b/libs/Catch2 index de7ba4e88..32d9ae24b 160000 --- a/libs/Catch2 +++ b/libs/Catch2 @@ -1 +1 @@ -Subproject commit de7ba4e8897bbe56c612b980d02f249a92d0eada +Subproject commit 32d9ae24bc4a0fed9a136a73e116b4ded0ac84b7 diff --git a/libs/json b/libs/json index 6eab7a2b1..9cca280a4 160000 --- a/libs/json +++ b/libs/json @@ -1 +1 @@ -Subproject commit 6eab7a2b187b10b2494e39c1961750bfd1bda500 +Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 diff --git a/libs/parallel-hashmap b/libs/parallel-hashmap index 004690579..0f0ecf724 160000 --- a/libs/parallel-hashmap +++ b/libs/parallel-hashmap @@ -1 +1 @@ -Subproject commit 0046905791006b5a85dbf62f7a98c65987b72e68 +Subproject commit 0f0ecf724c91c7f6105555d2d11b00985c2feccd diff --git a/libs/pybind11 b/libs/pybind11 index dc9b39596..a67d78657 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit dc9b39596d986aeb061bd3debe52d30e2467dc48 +Subproject commit a67d786571cc6d2c2ecec33985e00e6d22dc68e5 diff --git a/libs/tinyxml2 b/libs/tinyxml2 index e05956094..c2d30872e 160000 --- a/libs/tinyxml2 +++ b/libs/tinyxml2 @@ -1 +1 @@ -Subproject commit e05956094c27117f989d22f25b75633123d72a83 +Subproject commit c2d30872e20621955ca7feb9168bad996d591a19 From 4c66aa4895d47ccba5ef907852d31591f803f03a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:16:49 +0100 Subject: [PATCH 02/24] :arrow_up: Bump the documentation group in /docs with 1 update (#344) Bumps the documentation group in /docs with 1 update: [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme). - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.3.0...2.0.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production update-type: version-update:semver-major dependency-group: documentation ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 27464bbbe..ca589df9f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ sphinx==7.2.6 breathe==4.35.0 -sphinx-rtd-theme==1.3.0 +sphinx-rtd-theme==2.0.0 From 42b8e8809fdac06fa22a471177a3b3adf07f09aa Mon Sep 17 00:00:00 2001 From: simon1hofmann <119581649+simon1hofmann@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:21:38 +0100 Subject: [PATCH 03/24] =?UTF-8?q?=F0=9F=93=9D=20MNT=20Bench=20references?= =?UTF-8?q?=20(#348)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * Update input.rst * Update physical_simulation.rst * Create README.md * Typo * Update README.md * Apply suggestions from code review Co-authored-by: Marcel Walter * Update README.md Co-authored-by: Marcel Walter --------- Co-authored-by: Marcel Walter --- README.md | 8 ++++++++ benchmarks/README.md | 20 ++++++++++++++++++++ docs/io/input.rst | 2 ++ docs/io/physical_simulation.rst | 2 ++ 4 files changed, 32 insertions(+) create mode 100644 benchmarks/README.md diff --git a/README.md b/README.md index e3fb7a4a4..2e54fc6d8 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,14 @@ Cell-level layouts: - Bounding box - Area usage in nm² +## Benchmark Library + +To objectively evaluate and compare software and design automation tools, [MNT Bench](https://www.cda.cit.tum.de/mntbench/) provides gate-level +layouts for various gate libraries and clocking schemes, generated using the latest physical design and +optimization algorithms, with *fiction* offering the corresponding read and write utilities to generate gate-level layouts from gate-level layout files (``.fgl``) and vice versa. + +Additionally, the [benchmarks](https://github.com/cda-tum/fiction/tree/main/benchmarks) folder contains the function descriptions of frequently used benchmark sets in Verilog format (``.v``) provided by [MNT Bench](https://www.cda.cit.tum.de/mntbench/). + # Reference Since *fiction* is academic software, we would be thankful if you referred to it by citing the following publication: diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 000000000..406c1f77d --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,20 @@ +# Benchmarks + +To objectively evaluate and compare software and design automation tools for the physical design of FCN, diverse benchmark sets with function descriptions are needed. +[MNT Bench](/mntbench/) provides functions from the following benchmark sets in Verilog (``.v``) format: + +1. [Trindade16](https://ieeexplore.ieee.org/document/7724048) +2. [Fontes18](https://ieeexplore.ieee.org/document/8351001) +3. [ISCAS85](https://www.researchgate.net/publication/273775783_A_neutral_netlist_of_10_combinational_benchmark_circuits_and_a_targeted_translator_in_FORTRAN) +4. [EPFL](https://www.epfl.ch/labs/lsi/page-102566-en-html/benchmarks/) + +For convenience, they are also part of *fiction* to be directly used in experiments or the development of new physical design algorithms, optimizations, and simulators. + +# Gate-level Layouts + +Gate-level layouts generated with any of the physical design methods implemented in *fiction* can be stored in a human-readable file format (``.fgl``) with the +correponding [write](https://fiction.readthedocs.io/en/latest/io/physical_simulation.html#technology-independent-gate-level-layouts) function. + +[MNT Bench](https://www.cda.cit.tum.de/mntbench/) offers gate-level layouts spanning various gate libraries, clocking schemes, physical +design algorithms, and optimizations. These layouts can be [read](https://fiction.readthedocs.io/en/latest/io/input.html#gate-level-layouts) with *fiction* and used for testing new +post-layout optimization algorithms, creating cell-level layouts, or any other use case. diff --git a/docs/io/input.rst b/docs/io/input.rst index db789e901..fb3143918 100644 --- a/docs/io/input.rst +++ b/docs/io/input.rst @@ -22,6 +22,8 @@ Truth Tables Gate-level Layouts ################## +Can be used to read gate-level layout files (``.fgl``) as offered by `MNT Bench `_. + **Header:** ``fiction/io/read_fgl_layout.hpp`` .. doxygenfunction:: fiction::read_fgl_layout(std::istream& is, const std::string_view& name = "") diff --git a/docs/io/physical_simulation.rst b/docs/io/physical_simulation.rst index 3aa01efb7..a42574e9f 100644 --- a/docs/io/physical_simulation.rst +++ b/docs/io/physical_simulation.rst @@ -91,6 +91,8 @@ SiDB Operational Domain Technology-independent Gate-level Layouts ######################################### +Can be used to generate gate-level layout files (``.fgl``) as offered by `MNT Bench `_. + **Header:** ``fiction/io/write_fgl_layout.hpp`` .. doxygenfunction:: fiction::write_fgl_layout(const Lyt& lyt, std::ostream& os) From 3b0a7dfe483e143aaae3b263d70f23f8dcf9db57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:05:06 +0000 Subject: [PATCH 04/24] :arrow_up: Bump the github-actions group with 1 update (#351) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverage.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b07852217..61349a581 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -64,7 +64,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -yq libtbb-dev ${{matrix.compiler}} - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9.x' diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 828bb985e..6db779c6d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -48,7 +48,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -yq ${{matrix.compiler}} - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9.x' diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 553b37dde..b2b7e3971 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -77,7 +77,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -yq libtbb-dev ${{matrix.compiler}} - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9.x' From cad31b16ebb57a2e05118b620f4bf612d9a27554 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sat, 9 Dec 2023 19:30:14 +0100 Subject: [PATCH 05/24] :green_heart: Hotfix for the macOS TBB CI by disabling the installation of TBB under macOS 11 as it seems to no longer be supported (#352) --- .github/workflows/macos.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e453ecc48..933fb87ab 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -64,8 +64,9 @@ jobs: with: submodules: recursive - # Setup TBB for parallel STL algorithms via Homebrew - - name: Setup TBB + # Setup TBB for parallel STL algorithms via Homebrew (macOS 11 is no longer supported) + - if: matrix.os != macos-11 + name: Setup TBB run: brew install tbb # Use XCode 13.2 as a workaround because XCode 14.0 seems broken From 16c420a8ca39c52d4ceb6e579bb1ad39f92fb05f Mon Sep 17 00:00:00 2001 From: Jan Drewniok <97012901+Drewniok@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:12:48 +0100 Subject: [PATCH 06/24] :green_heart: add single quotes. (#353) --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 933fb87ab..ccaa1312b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -65,7 +65,7 @@ jobs: submodules: recursive # Setup TBB for parallel STL algorithms via Homebrew (macOS 11 is no longer supported) - - if: matrix.os != macos-11 + - if: matrix.os != 'macos-11' name: Setup TBB run: brew install tbb From 67023346bde640ace000266d9f6b6344fb81dbf9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:27:19 +0000 Subject: [PATCH 07/24] :arrow_up: Bump the submodules group with 3 updates (#350) --- libs/Catch2 | 2 +- libs/json | 2 +- libs/pybind11 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/Catch2 b/libs/Catch2 index 32d9ae24b..0520ff443 160000 --- a/libs/Catch2 +++ b/libs/Catch2 @@ -1 +1 @@ -Subproject commit 32d9ae24bc4a0fed9a136a73e116b4ded0ac84b7 +Subproject commit 0520ff44366d1ec4d9862763514cb40ca35582fd diff --git a/libs/json b/libs/json index 9cca280a4..3780b41dd 160000 --- a/libs/json +++ b/libs/json @@ -1 +1 @@ -Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 +Subproject commit 3780b41dd070436f3f55327b0a88f27a52e2dfa8 diff --git a/libs/pybind11 b/libs/pybind11 index a67d78657..68322895d 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit a67d786571cc6d2c2ecec33985e00e6d22dc68e5 +Subproject commit 68322895df3d02c94deba6f3a3f531dd5b566cee From 2cb1547ca960c469c9534f1a8d16500edc1f3f7d Mon Sep 17 00:00:00 2001 From: Jan Drewniok <97012901+Drewniok@users.noreply.github.com> Date: Wed, 13 Dec 2023 09:25:22 +0100 Subject: [PATCH 08/24] =?UTF-8?q?=F0=9F=90=9B=20Atomic=20defect=20can=20be?= =?UTF-8?q?=20updated=20or=20new=20one=20can=20be=20assigned=20at=20specif?= =?UTF-8?q?ic=20coordinate.=20(#354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :art: erase defect before new one is assigned. * :white_check_mark: add unit test * :art: change the aspect ratio. * :art: fix typo. --- include/fiction/technology/sidb_surface.hpp | 10 +++++----- test/technology/sidb_surface.cpp | 22 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/fiction/technology/sidb_surface.hpp b/include/fiction/technology/sidb_surface.hpp index 42ce5f522..f946e6790 100644 --- a/include/fiction/technology/sidb_surface.hpp +++ b/include/fiction/technology/sidb_surface.hpp @@ -125,11 +125,11 @@ class sidb_surface : public Lyt */ void assign_sidb_defect(const typename Lyt::coordinate& c, const sidb_defect& d) noexcept { - if (d.type == sidb_defect_type::NONE) // delete defect - { - strg->defective_coordinates.erase(c); - } - else if (strg->params.ignore.count(d.type) == 0) // add defect if this type is not ignored + // delete defect at the coordinate + strg->defective_coordinates.erase(c); + + if (d.type != sidb_defect_type::NONE && + strg->params.ignore.count(d.type) == 0) // add defect if this type is not ignored and is not NONE { strg->defective_coordinates.insert({c, d}); } diff --git a/test/technology/sidb_surface.cpp b/test/technology/sidb_surface.cpp index b83c80142..65b1597c2 100644 --- a/test/technology/sidb_surface.cpp +++ b/test/technology/sidb_surface.cpp @@ -108,6 +108,28 @@ TEMPLATE_TEST_CASE( CHECK(defect_layout.num_defects() == 0); } +TEMPLATE_TEST_CASE( + "Overwrite SiDB defect", "[sidb-surface]", + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>)) +{ + TestType lyt{{1, 1}}; + + sidb_surface defect_layout{lyt}; + + defect_layout.assign_sidb_defect({0, 0}, sidb_defect{sidb_defect_type::UNKNOWN, 1}); + CHECK(defect_layout.get_sidb_defect({0, 0}).type == sidb_defect_type::UNKNOWN); + CHECK(defect_layout.get_sidb_defect({0, 0}).charge == 1); + CHECK(defect_layout.num_defects() == 1); + + defect_layout.assign_sidb_defect({0, 0}, sidb_defect{sidb_defect_type::DB, -1}); + CHECK(defect_layout.get_sidb_defect({0, 0}).charge == -1); + CHECK(defect_layout.num_defects() == 1); +} + TEMPLATE_TEST_CASE( "Non-defective SiDB surface", "[sidb-surface]", (cell_level_layout>>), From 4745f7fe15cdb14c7cdbdfe4a609ee588813063a Mon Sep 17 00:00:00 2001 From: Willem Lambooy <35612535+wlambooy@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:42:34 +0100 Subject: [PATCH 09/24] :bug: `read_sqd_layout` now updates the aspect ratio properly for SiQAD-coordinate based layouts (#355) --- include/fiction/io/read_sqd_layout.hpp | 38 ++++++++++++++++---------- test/io/read_sqd_layout.cpp | 34 +++++++++++++++++------ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/include/fiction/io/read_sqd_layout.hpp b/include/fiction/io/read_sqd_layout.hpp index 92f20ba8c..f2c37a972 100644 --- a/include/fiction/io/read_sqd_layout.hpp +++ b/include/fiction/io/read_sqd_layout.hpp @@ -126,6 +126,26 @@ class read_sqd_layout_impl */ cell max_cell_pos{}; + /** + * Updates the bounding box given by the maximum position of a cell in the layout + * + * @param cell The cell to challenge the stored maximum position of a cell in the layout against. + */ + void update_bounding_box(const cell& cell) + { + if (cell.x > max_cell_pos.x) + { + max_cell_pos.x = cell.x; + } + if (cell.y > max_cell_pos.y) + { + max_cell_pos.y = cell.y; + } + if (cell.z > max_cell_pos.z) + { + max_cell_pos.z = cell.z; + } + } /** * Converts a dimer position to a cell position. Additionally updates the maximum cell position parsed so far. * @@ -147,19 +167,7 @@ class read_sqd_layout_impl const cell cell{n, m * 2 + l}; - // store latest cell position via bounding box - if (cell.x > max_cell_pos.x) - { - max_cell_pos.x = cell.x; - } - if (cell.y > max_cell_pos.y) - { - max_cell_pos.y = cell.y; - } - if (cell.z > max_cell_pos.z) - { - max_cell_pos.z = cell.z; - } + update_bounding_box(cell); return cell; } @@ -181,7 +189,9 @@ class read_sqd_layout_impl // special case for SiQAD coordinates if constexpr (has_siqad_coord_v) { - return cell{std::stoll(n), std::stoll(m), std::stoll(l)}; + cell cell{std::stoll(n), std::stoll(m), std::stoll(l)}; + update_bounding_box(cell); + return cell; } // Cartesian coordinates diff --git a/test/io/read_sqd_layout.cpp b/test/io/read_sqd_layout.cpp index a0933d9a0..7d66253e5 100644 --- a/test/io/read_sqd_layout.cpp +++ b/test/io/read_sqd_layout.cpp @@ -108,16 +108,34 @@ TEST_CASE("Read multi-dot SQD layout", "[sqd]") std::istringstream layout_stream{sqd_layout}; - using sidb_layout = cell_level_layout>>; - const auto layout = read_sqd_layout(layout_stream); + SECTION("Fiction coordinates") + { + using sidb_layout = cell_level_layout>>; + const auto layout = read_sqd_layout(layout_stream); - CHECK(layout.x() == 2); - CHECK(layout.y() == 5); + CHECK(layout.x() == 2); + CHECK(layout.y() == 5); - CHECK(layout.get_cell_type({0, 0}) == sidb_technology::cell_type::NORMAL); - CHECK(layout.get_cell_type({0, 1}) == sidb_technology::cell_type::NORMAL); - CHECK(layout.get_cell_type({2, 4}) == sidb_technology::cell_type::NORMAL); - CHECK(layout.get_cell_type({2, 5}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({0, 0}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({0, 1}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({2, 4}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({2, 5}) == sidb_technology::cell_type::NORMAL); + } + + SECTION("SiQAD coordinates") + { + using sqd_layout = cell_level_layout>>; + const auto layout = read_sqd_layout(layout_stream); + + CHECK(layout.x() == 2); + CHECK(layout.y() == 2); + CHECK(layout.z() == 1); + + CHECK(layout.get_cell_type({0, 0, 0}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({0, 0, 1}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({2, 2, 0}) == sidb_technology::cell_type::NORMAL); + CHECK(layout.get_cell_type({2, 2, 1}) == sidb_technology::cell_type::NORMAL); + } } TEST_CASE("Read multi-dot SQD layout with cell type definitions", "[sqd]") From 23b5e231877bdc4cccba040934a7d8e5815d77f3 Mon Sep 17 00:00:00 2001 From: Willem Lambooy <35612535+wlambooy@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:03:33 +0100 Subject: [PATCH 10/24] :bug: Bugfix related to charge index recomputation and minor bugfixes from #325 (#347) * :bug: reducing the charge index to a value with leading zeroes now gives correct behaviour (cherry picked from commit e1fe34a85e2666096322efd0f3798328d2c5b8c1) * :bug: `bounding_box` does not update the dimension sizes for cubic coordinates (cherry picked from commit fc01d74d0252bcfaea0fed425ab4ad732ca6efa5) * :zap: optimized increase_charge_index_by_one (with and enum this time) * :art: remove `const` specifier from functions that are not logically const * :white_check_mark: add `const` specifier from functions that are logically const when considering a change of charge index as such * :white_check_mark: add `const` specifier from functions that are logically const when considering a change of charge index as such * :white_check_mark: remove `const` specifier from functions that are not logically const when considering a change of charge index as such * :memo: implemented Jan's suggestions * :white_check_mark: remove `const` specifier * :bug: The TTS algorithm now sets the simulator name correctly for ExGS (to `ExGS`) --------- Co-authored-by: Marcel Walter --- .../simulation/sidb/time_to_solution.hpp | 8 +- include/fiction/layouts/bounding_box.hpp | 6 +- .../charge_distribution_surface.hpp | 117 ++++++++++-------- .../simulation/sidb/time_to_solution.cpp | 2 +- test/io/print_layout.cpp | 2 +- .../charge_distribution_surface.cpp | 65 +++++++++- 6 files changed, 139 insertions(+), 61 deletions(-) diff --git a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp index f42dca011..3cefa64d4 100644 --- a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp +++ b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp @@ -105,14 +105,14 @@ void time_to_solution(Lyt& lyt, const quicksim_params& quicksim_params, const ti sidb_simulation_result simulation_result{}; if (tts_params.engine == exhaustive_sidb_simulation_engine::QUICKEXACT) { + const quickexact_params params{quicksim_params.phys_params}; st.algorithm = "QuickExact"; - simulation_result = exhaustive_ground_state_simulation(lyt, quicksim_params.phys_params); + simulation_result = quickexact(lyt, params); } else { - const quickexact_params params{quicksim_params.phys_params}; - st.algorithm = "QuickExact"; - simulation_result = quickexact(lyt, params); + st.algorithm = "ExGS"; + simulation_result = exhaustive_ground_state_simulation(lyt, quicksim_params.phys_params); } st.single_runtime_exhaustive = mockturtle::to_seconds(simulation_result.simulation_runtime); diff --git a/include/fiction/layouts/bounding_box.hpp b/include/fiction/layouts/bounding_box.hpp index 34a6fed4c..721ed2737 100644 --- a/include/fiction/layouts/bounding_box.hpp +++ b/include/fiction/layouts/bounding_box.hpp @@ -133,10 +133,10 @@ class bounding_box_2d } } }); - - x_size = max.x - min.x; - y_size = max.y - min.y; } + + x_size = max.x - min.x; + y_size = max.y - min.y; } /** * Returns the minimum corner of the bounding box. diff --git a/include/fiction/technology/charge_distribution_surface.hpp b/include/fiction/technology/charge_distribution_surface.hpp index f007b2785..2035f0088 100644 --- a/include/fiction/technology/charge_distribution_surface.hpp +++ b/include/fiction/technology/charge_distribution_surface.hpp @@ -79,6 +79,22 @@ enum class charge_distribution_history NEGLECT }; +/** + * An enumeration of modes to specifying if the charge index should be recomputed fully. + */ +enum class charge_index_recomputation +{ + /** + * The charge index is recomputed from scratch. + */ + FROM_SCRATCH, + /** + * The charge index is recomputed with the leading zeroes ignored. This optimization can be applied if we know that + * the charge index was incremented after the last charge index computation. + */ + IGNORE_LEADING_ZEROES +}; + /** * A layout type to layer on top of any SiDB cell-level layout. It implements an interface to store and access * SiDBs' charge states. @@ -394,7 +410,7 @@ class charge_distribution_surface : public Lyt * @param update_charge_index `true` if the charge index should be changed, `false` otherwise. */ void assign_charge_state(const typename Lyt::cell& c, const sidb_charge_state cs, - const bool update_charge_index = true) const noexcept + const bool update_charge_index = true) noexcept { if (auto index = cell_to_index(c); index != -1) { @@ -412,7 +428,7 @@ class charge_distribution_surface : public Lyt * @param index The index of the cell. * @param cs The charge state to be assign to the cell. */ - void assign_charge_by_cell_index(const uint64_t index, const sidb_charge_state cs) const noexcept + void assign_charge_by_cell_index(const uint64_t index, const sidb_charge_state cs) noexcept { strg->cell_charge[index] = cs; this->charge_distribution_to_index(); @@ -1175,7 +1191,7 @@ class charge_distribution_surface : public Lyt } else { - this->index_to_charge_distribution(); + this->index_to_charge_distribution(charge_index_recomputation::IGNORE_LEADING_ZEROES); } this->update_after_charge_change(dependent_cell, energy_calculation_mode, history_mode); } @@ -1681,7 +1697,7 @@ class charge_distribution_surface : public Lyt } else { - this->index_to_charge_distribution(); + this->index_to_charge_distribution(charge_index_recomputation::IGNORE_LEADING_ZEROES); } this->update_after_charge_change(dependent_cell_fixed, recompute_system_energy, consider_history); } @@ -1856,7 +1872,7 @@ class charge_distribution_surface : public Lyt /** * Initializes the distance matrix between all the cells of the layout. */ - void initialize_nm_distance_matrix() const noexcept + void initialize_nm_distance_matrix() noexcept { strg->nm_dist_mat = std::vector>(this->num_cells(), std::vector(this->num_cells(), 0.0)); @@ -1873,7 +1889,7 @@ class charge_distribution_surface : public Lyt /** * Initializes the potential matrix between all the cells of the layout. */ - void initialize_potential_matrix() const noexcept + void initialize_potential_matrix() noexcept { strg->pot_mat = std::vector>(this->num_cells(), std::vector(this->num_cells(), 0.0)); @@ -1964,59 +1980,60 @@ class charge_distribution_surface : public Lyt } /** * The stored unique index is converted to a charge distribution. + * + * @param charge_index_recompuation_mode Flag that can be set to `IGNORE_LEADING_ZEROES` if the charge index was + * incremented before calling this function to enable an optimization. When set to `IGNORE_LEADING_ZEROES`, the + * leading zeroes of the charge index are ignored. */ - void index_to_charge_distribution() noexcept + void index_to_charge_distribution(const charge_index_recomputation charge_index_recomputation_mode = + charge_index_recomputation::FROM_SCRATCH) noexcept { - auto charge_quot = strg->charge_index_and_base.first; - const auto base = strg->charge_index_and_base.second; - const auto num_charges = this->num_cells(); - auto counter = num_charges - 1; - const auto dependent_cell_index = cell_to_index(strg->dependent_cell); - // A charge index of zero corresponds to a layout with all SiDBs set to negative. - if (charge_quot == 0) + if (strg->charge_index_and_base.first == 0) { this->assign_all_charge_states(sidb_charge_state::NEGATIVE); + return; } - else + + const auto dependent_cell_index = cell_to_index(strg->dependent_cell); + const bool has_dependent_cell = dependent_cell_index >= 0; + + const auto base = static_cast(strg->charge_index_and_base.second); + uint64_t charge_quot = strg->charge_index_and_base.first; + + auto counter = static_cast(this->num_cells() - 1); + + while (charge_quot > 0) { - while (charge_quot > 0) + const auto charge_state = sign_to_charge_state(static_cast(charge_quot % base) - 1); + + // Dependent-SiDB is skipped since its charge state is not changed based on the charge index. + + // If the counter is at the dependent-cell location, it is reduced by one to get to the next + // cell position. + if (has_dependent_cell && counter == dependent_cell_index) { - const auto charge_quot_int = static_cast(charge_quot); - const auto base_int = static_cast(base); - const int64_t quotient_int = charge_quot_int / base_int; - const int64_t remainder_int = charge_quot_int % base_int; - charge_quot = static_cast(quotient_int); - // Dependent-SiDB is skipped since its charge state is not changed based on the charge index. - - if (dependent_cell_index >= 0) - { - if (counter != static_cast(dependent_cell_index)) - { - const auto sign = sign_to_charge_state(static_cast(remainder_int - 1)); - this->assign_charge_state_by_cell_index(counter, sign, false); - counter -= 1; - } - // If the counter is at the dependent-cell location, it is reduced by one to get to the next - // cell position. - else - { - counter -= 1; - const auto sign = sign_to_charge_state(static_cast(remainder_int - 1)); - // The charge state is only changed (i.e., the function assign_charge_state_by_cell_index is - // called), if the nw charge state differs to the previous one. Only then will the cell be - // added to the charge_distribution_history. - this->assign_charge_state_by_cell_index(counter, sign, false); - counter -= 1; - } - } - else - { - const auto sign = sign_to_charge_state(static_cast(remainder_int - 1)); - this->assign_charge_state_by_cell_index(counter, sign, false); - counter -= 1; - } + // The charge state is only changed (i.e., the function assign_charge_state_by_cell_index is + // called), if the nw charge state differs to the previous one. Only then will the cell be + // added to the charge_distribution_history. + counter -= 1; } + + this->assign_charge_state_by_cell_index(static_cast(counter), charge_state, false); + + charge_quot /= base; + counter -= 1; + } + + if (charge_index_recomputation_mode == charge_index_recomputation::IGNORE_LEADING_ZEROES) + { + return; + } + + // If the counter is >= 0, then the first cells should be assigned a negative charge state. + for (uint64_t i = 0; static_cast(i) <= counter; ++i) + { + this->assign_charge_state_by_cell_index(i, sidb_charge_state::NEGATIVE, false); } } }; diff --git a/test/algorithms/simulation/sidb/time_to_solution.cpp b/test/algorithms/simulation/sidb/time_to_solution.cpp index 046ddca42..1d24e32d9 100644 --- a/test/algorithms/simulation/sidb/time_to_solution.cpp +++ b/test/algorithms/simulation/sidb/time_to_solution.cpp @@ -44,7 +44,7 @@ TEMPLATE_TEST_CASE( const time_to_solution_params tts_params_exgs{exhaustive_sidb_simulation_engine::EXGS}; time_to_solution(lyt, quicksim_params, tts_params_exgs, &tts_stat_exgs); - CHECK(tts_stat_exgs.algorithm == "QuickExact"); + CHECK(tts_stat_exgs.algorithm == "ExGS"); CHECK_THAT(tts_stat_exgs.acc, Catch::Matchers::WithinAbs(0.0, 0.00001)); CHECK_THAT(tts_stat_exgs.time_to_solution, Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); diff --git a/test/io/print_layout.cpp b/test/io/print_layout.cpp index cf16b8399..5ca099112 100644 --- a/test/io/print_layout.cpp +++ b/test/io/print_layout.cpp @@ -262,7 +262,7 @@ TEST_CASE("Print Bestagon OR-gate without defect", "[print-charge-layout]") layout.create_or({}, {}, {0, 0}); - const charge_distribution_surface cl{ + charge_distribution_surface cl{ convert_to_siqad_coordinates(apply_gate_library(layout)), sidb_simulation_parameters{3, -0.32}, sidb_charge_state::NEGATIVE}; diff --git a/test/technology/charge_distribution_surface.cpp b/test/technology/charge_distribution_surface.cpp index ca961fcc7..dda5fd6a3 100644 --- a/test/technology/charge_distribution_surface.cpp +++ b/test/technology/charge_distribution_surface.cpp @@ -55,7 +55,7 @@ TEMPLATE_TEST_CASE("Assign and delete charge states without defects", "[charge-d lyt.assign_cell_type({5, 4}, TestType::cell_type::NORMAL); lyt.assign_cell_type({5, 5}, TestType::cell_type::NORMAL); lyt.assign_cell_type({5, 6}, TestType::cell_type::NORMAL); - const charge_distribution_surface charge_layout{lyt, sidb_simulation_parameters{}}; + charge_distribution_surface charge_layout{lyt, sidb_simulation_parameters{}}; CHECK(charge_layout.cell_to_index({5, 4}) != charge_layout.cell_to_index({5, 5})); CHECK(charge_layout.cell_to_index({5, 6}) != charge_layout.cell_to_index({5, 5})); CHECK(charge_layout.index_to_cell(4) == (siqad::coord_t())); @@ -951,7 +951,7 @@ TEMPLATE_TEST_CASE("Assign and delete charge states without defects", "[charge-d lyt_new.assign_cell_type({1, 3, 0}, TestType::cell_type::NORMAL); lyt_new.assign_cell_type({10, 5, 1}, TestType::cell_type::NORMAL); - const charge_distribution_surface charge_layout_new{lyt_new, params}; + charge_distribution_surface charge_layout_new{lyt_new, params}; charge_layout_new.assign_charge_state({0, 0, 1}, sidb_charge_state::NEGATIVE); charge_layout_new.assign_charge_state({1, 3, 0}, sidb_charge_state::POSITIVE); @@ -2125,3 +2125,64 @@ TEMPLATE_TEST_CASE("Assign and delete charge states without defects, part two", Catch::Matchers::WithinAbs(0, physical_constants::POP_STABILITY_ERR)); } } + +TEMPLATE_TEST_CASE( + "Assign charge index", "[charge-distribution-surface]", + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>), + (cell_level_layout>>)) + +{ + TestType lyt{{1, 1}}; + + lyt.assign_cell_type({0, 0}, TestType::cell_type::NORMAL); + lyt.assign_cell_type({0, 1}, TestType::cell_type::NORMAL); + + SECTION("Base 2") + { + charge_distribution_surface charge_lyt{lyt, sidb_simulation_parameters{2}}; + + charge_lyt.assign_charge_index(0); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::NEGATIVE); + + charge_lyt.assign_charge_index(1); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::NEUTRAL); + + charge_lyt.assign_charge_index(3); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEUTRAL); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::NEUTRAL); + + charge_lyt.assign_charge_index(1); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::NEUTRAL); + } + + SECTION("Base 3") + { + charge_distribution_surface charge_lyt{lyt, sidb_simulation_parameters{3}}; + + charge_lyt.assign_charge_index(0); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::NEGATIVE); + + charge_lyt.assign_charge_index(2); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::POSITIVE); + + charge_lyt.assign_charge_index(8); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::POSITIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::POSITIVE); + + charge_lyt.assign_charge_index(5); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEUTRAL); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::POSITIVE); + + charge_lyt.assign_charge_index(2); + CHECK(charge_lyt.get_charge_state({0, 0}) == sidb_charge_state::NEGATIVE); + CHECK(charge_lyt.get_charge_state({0, 1}) == sidb_charge_state::POSITIVE); + } +} From 74173a3af9fa7691fdfba8d7189a45e51e4caa32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:05:45 +0100 Subject: [PATCH 11/24] :arrow_up: Bump the github-actions group with 2 updates (#357) Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/upload-artifact` from 3 to 4 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) Updates `github/codeql-action` from 2 to 3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/clang-tidy-review.yml | 2 +- .github/workflows/codeql-analysis.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 419718293..a3ef38323 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -47,7 +47,7 @@ jobs: ignore_case: true - name: Upload review artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: clang-tidy-review path: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 61349a581..c8282fb25 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -102,7 +102,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: .github/codeql-config.yml @@ -135,7 +135,7 @@ jobs: run: cmake --build . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: upload: false output: sarif-results @@ -151,6 +151,6 @@ jobs: output: sarif-results/${{ matrix.language }}.sarif - name: Upload SARIF to GitHub - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: sarif-results/${{ matrix.language }}.sarif From 1764242a18e666bdce48611a76a2ed25a89ac64b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:06:40 +0100 Subject: [PATCH 12/24] :arrow_up: Bump the submodules group with 4 updates (#356) Bumps the submodules group with 4 updates: [libs/Catch2](https://github.com/catchorg/Catch2), [libs/parallel-hashmap](https://github.com/greg7mdp/parallel-hashmap), [libs/pybind11](https://github.com/pybind/pybind11) and [libs/json](https://github.com/nlohmann/json). Updates `libs/Catch2` from `0520ff4` to `3acb8b3` - [Release notes](https://github.com/catchorg/Catch2/releases) - [Commits](https://github.com/catchorg/Catch2/compare/0520ff44366d1ec4d9862763514cb40ca35582fd...3acb8b30f1ce0c801de0e3880ea1f6467dd0105c) Updates `libs/parallel-hashmap` from `0f0ecf7` to `946ebad` - [Release notes](https://github.com/greg7mdp/parallel-hashmap/releases) - [Commits](https://github.com/greg7mdp/parallel-hashmap/compare/0f0ecf724c91c7f6105555d2d11b00985c2feccd...946ebad67a21212d11a0dd4deb7cdedc297d47bc) Updates `libs/pybind11` from `6832289` to `eeac2f4` - [Release notes](https://github.com/pybind/pybind11/releases) - [Commits](https://github.com/pybind/pybind11/compare/68322895df3d02c94deba6f3a3f531dd5b566cee...eeac2f45728633d7ee6fe792bea2890345697119) Updates `libs/json` from `3780b41` to `a259ecc` - [Release notes](https://github.com/nlohmann/json/releases) - [Commits](https://github.com/nlohmann/json/compare/3780b41dd070436f3f55327b0a88f27a52e2dfa8...a259ecc51e1951e12f757ce17db958e9881e9c6c) --- updated-dependencies: - dependency-name: libs/Catch2 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/parallel-hashmap dependency-type: direct:production dependency-group: submodules - dependency-name: libs/pybind11 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/json dependency-type: direct:production dependency-group: submodules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/Catch2 | 2 +- libs/json | 2 +- libs/parallel-hashmap | 2 +- libs/pybind11 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/Catch2 b/libs/Catch2 index 0520ff443..3acb8b30f 160000 --- a/libs/Catch2 +++ b/libs/Catch2 @@ -1 +1 @@ -Subproject commit 0520ff44366d1ec4d9862763514cb40ca35582fd +Subproject commit 3acb8b30f1ce0c801de0e3880ea1f6467dd0105c diff --git a/libs/json b/libs/json index 3780b41dd..a259ecc51 160000 --- a/libs/json +++ b/libs/json @@ -1 +1 @@ -Subproject commit 3780b41dd070436f3f55327b0a88f27a52e2dfa8 +Subproject commit a259ecc51e1951e12f757ce17db958e9881e9c6c diff --git a/libs/parallel-hashmap b/libs/parallel-hashmap index 0f0ecf724..946ebad67 160000 --- a/libs/parallel-hashmap +++ b/libs/parallel-hashmap @@ -1 +1 @@ -Subproject commit 0f0ecf724c91c7f6105555d2d11b00985c2feccd +Subproject commit 946ebad67a21212d11a0dd4deb7cdedc297d47bc diff --git a/libs/pybind11 b/libs/pybind11 index 68322895d..eeac2f457 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit 68322895df3d02c94deba6f3a3f531dd5b566cee +Subproject commit eeac2f45728633d7ee6fe792bea2890345697119 From 2e3446ca1a2c49600d4368ae33305fab3cd0f025 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Fri, 15 Dec 2023 14:16:07 +0100 Subject: [PATCH 13/24] :sparkles: Compile time flows and CLI simulation update (#349) * :sparkles: Added CMake options for select flow compilation * :art: Simplified layout printing from store * :sparkles: Added SQD reading and CDS printing to the CLI * :sparkles: Added QuickExact to the CLI * :sparkles: Added QuickSim to the CLI * :bug: Try to pacify MSVC * :bug: Removed base option for the quicksim CLI command * :art: Adjusted `minimum_energy` to be more C++-ish * :sparkles: Added a `minimum_energy_distribution` function to obtain the CDS with minimum energy from a range * :memo: Fixed a wrong header in the documentation and added a missing `:members:` statement * :art: Make the quicksim and quickexact commands pick the minimum energy state they found * :bug: Fixed cyclic dependency * :boom: Made `additional_simulation_parameters` in `sidb_simulation_result` an `std::unordered_map` * :sparkles: Made the quicksim CLI command log its additional parameters * :sparkles: Display proper cell type names in the CLI's `ps -c` and `store -c` commands * :white_check_mark: update unit tests after code changes. * :white_check_mark: update test after code changes. * :art: remove ``else`` after return. * :art: ClangFormat changes Signed-off-by: ClangFormat * :memo: Add documentation to the CLI chapter in the RTD pages. * :memo: revert wrong changes. * :art: three as a base number is not supported by quicksim. * :memo: change position of simulation paragraph. * :white_check_mark: update test due to code changes. * :green_heart: try to fix macos issue. * :art: add global potential and automatic base number detection. * :memo: Extended documentation on `minimum_energy` to reflect the error cases * :bug: Fixed unit * :memo: Small doc update * :memo: Revert Jan's change * :bug Add missing header * :art: delete automatic base number detection. * :bug: Set CMP0135 only if CMake >= 3.24 is used * :art: disable automatic base number detection in CLI. * :memo: add variable names. * :art: Cleaned up includes * :fire: Remove base toggle from quickexact and quicksim CLI commands * :art: Further header include fixes * :art: Adjustments to logging and [[nodiscard]] * :sparkles: Added Operational Domain computation to the CLI * :sparkles: Added Operational Domain computation to the CLI * :memo: Added Operational Domain CLI documentation * :bug: Added missing includes to `critical_temperature` * :memo: Small doc fix * :bug: Added missing includes to `quicksim` * :bug: Added missing includes to `quicksim`'s tests * :art: Code cleanup in `critical_temperature` * :art: Added physical parameters used for the simulation to the `critical_temperature_stats` * :bug: Fix logging for `quickexact` and `quicksim` CLI commands * :bug: Fix `opdom` error when no PI/PO cells are available * :sparkles: Added command `temp` for critical temperature SiDB simulation to the CLI * :art: Added missing header and `std::forward` * :bug: fix unit bug. * :art: set description to meV. --------- Signed-off-by: ClangFormat Co-authored-by: Drewniok Co-authored-by: ClangFormat --- cli/CMakeLists.txt | 20 + cli/cmd/io/read.hpp | 72 ++-- cli/cmd/simulation/opdom.hpp | 341 ++++++++++++++++++ cli/cmd/simulation/quickexact.hpp | 195 ++++++++++ cli/cmd/simulation/quicksim.hpp | 203 +++++++++++ cli/cmd/simulation/temp.hpp | 234 ++++++++++++ cli/commands.hpp | 19 + cli/stores.hpp | 15 +- cmake/PackageProject.cmake | 4 +- docs/algorithms/sidb_simulation.rst | 4 +- docs/cli.rst | 106 +++++- .../simulation/sidb/critical_temperature.hpp | 104 +++--- .../simulation/sidb/is_ground_state.hpp | 6 +- ...defect_influence_position_and_distance.hpp | 13 +- .../simulation/sidb/minimum_energy.hpp | 53 ++- .../simulation/sidb/operational_domain.hpp | 4 +- .../algorithms/simulation/sidb/quickexact.hpp | 10 +- .../algorithms/simulation/sidb/quicksim.hpp | 13 +- .../sidb/sidb_simulation_result.hpp | 6 +- .../simulation/sidb/time_to_solution.hpp | 1 - include/fiction/io/print_layout.hpp | 9 +- .../io/write_location_and_ground_state.hpp | 4 +- .../fiction/io/write_operational_domain.hpp | 4 +- include/fiction/layouts/cell_level_layout.hpp | 9 +- .../charge_distribution_surface.hpp | 12 +- include/fiction/types.hpp | 19 +- include/fiction/utils/layout_utils.hpp | 20 +- .../sidb/can_positive_charges_occur.cpp | 2 + .../simulation/sidb/critical_temperature.cpp | 246 +++++++------ .../simulation/sidb/minimum_energy.cpp | 17 +- .../algorithms/simulation/sidb/quickexact.cpp | 10 +- test/algorithms/simulation/sidb/quicksim.cpp | 33 +- test/io/write_sqd_sim_result.cpp | 43 ++- 33 files changed, 1567 insertions(+), 284 deletions(-) create mode 100644 cli/cmd/simulation/opdom.hpp create mode 100644 cli/cmd/simulation/quickexact.hpp create mode 100644 cli/cmd/simulation/quicksim.hpp create mode 100644 cli/cmd/simulation/temp.hpp diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 8f009b738..cef2d5faf 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -9,6 +9,26 @@ add_executable(fiction ${SOURCES}) # Link against the project settings, libfiction and alice target_link_libraries(fiction PRIVATE libfiction alice) +# Compile-time decisions on which flows to compile + +# Logic synthesis flow +option(FICTION_LOGIC_SYNTHESIS_FLOW "Enable the logic synthesis flow for the fiction CLI" ON) +if(FICTION_LOGIC_SYNTHESIS_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_LOGIC_SYNTHESIS_FLOW) +endif() + +# Physical design flow +option(FICTION_PHYSICAL_DESIGN_FLOW "Enable the physical design flow for the fiction CLI" ON) +if(FICTION_PHYSICAL_DESIGN_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_PHYSICAL_DESIGN_FLOW) +endif() + +# Physical simulation flow +option(FICTION_SIMULATION_FLOW "Enable the physical simulation flow for the fiction CLI" ON) +if(FICTION_SIMULATION_FLOW) + target_compile_definitions(fiction PRIVATE FICTION_SIMULATION_FLOW) +endif() + # Strip the executable if we are in Release mode if(CMAKE_BUILD_TYPE STREQUAL "Release") if(CMAKE_STRIP) diff --git a/cli/cmd/io/read.hpp b/cli/cmd/io/read.hpp index e28441c89..18901b79b 100644 --- a/cli/cmd/io/read.hpp +++ b/cli/cmd/io/read.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -23,9 +24,9 @@ namespace alice * * Currently parses Verilog, AIGER, and BLIF using the lorina parsers. * - * Parses FGL and FQCA via custom reader functions. - * * For more information see: https://github.com/hriener/lorina + * + * Parses FGL, SQD, and FQCA via custom reader functions. */ class read_command : public command { @@ -40,54 +41,57 @@ class read_command : public command "which will be put into the respective store. Current supported file types are:\n" "Logic networks: Verilog, AIGER, BLIF.\n" "Gate-level layouts: FGL.\n" - "Cell-level layouts: FQCA.\n" + "Cell-level layouts: SQD, FQCA.\n" "In a directory, only files with extension '.v', '.aig', '.blif' are considered.") { add_option("filename", filename, "Filename or directory")->required(); add_option("topology", topology, "Topology for gate-level layouts. Can be 'cartesian' or of the form " "'__'"); - add_flag("--aig,-a", "Parse file as AIG"); - add_flag("--xag,-x", "Parse file as XAG"); - add_flag("--mig,-m", "Parse file as MIG"); - add_flag("--tec,-t", "Parse file as technology network"); - add_flag("--fgl,-f", "Parse file as fiction gate-level layout"); - add_flag("--qca,-q", "Parse file as QCA cell-level layout"); - add_flag("--sort,-s", sort, "Sort networks in given directory by vertex count prior to storing them"); + add_flag("--aig,-a", "Parse Verilog file as AIG"); + add_flag("--xag,-x", "Parse Verilog file as XAG"); + add_flag("--mig,-m", "Parse Verilog file as MIG"); + add_flag("--tec,-t", "Parse Verilog file as technology network"); + add_flag("--fgl,-f", "Parse FGL file as fiction gate-level layout"); + add_flag("--sqd,-s", "Parse SQD file as SiDB cell-level layout"); + add_flag("--fqca,-q", "Parse FQCA file as QCA cell-level layout"); + add_flag("--sort", sort, "Sort networks in given directory by node count prior to storing them"); } protected: /** - * Function to perform the read call. Reads Verilog and creates a logic_network. + * Function to perform the read call. Reads a network or layout from a file. */ void execute() override { - const auto store_ntks = [&](auto&& reader) - { - for (const auto& ln : reader.get_networks(sort)) - { - store().extend() = ln; - } - }; - - if (!is_set("aig") && !is_set("xag") && !is_set("mig") && !is_set("tec") && !is_set("fgl") && !is_set("qca")) + if (!is_set("aig") && !is_set("xag") && !is_set("mig") && !is_set("tec") && !is_set("fgl") && !is_set("sqd") && + !is_set("fqca")) { env->out() << "[e] at least one network or layout type must be specified" << std::endl; } - else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("fql")) + else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("fgl")) { env->out() << "[e] cannot parse files as both logic networks and gate-level layouts" << std::endl; } - else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && is_set("qca")) + else if ((is_set("aig") || is_set("xag") || is_set("mig") || is_set("tec")) && + (is_set("sqd") || is_set("fqca"))) { env->out() << "[e] cannot parse files as both logic networks and cell-level layouts" << std::endl; } - else if (is_set("fql") && is_set("qca")) + else if (is_set("fgl") && (is_set("sqd") || is_set("fqca"))) { env->out() << "[e] cannot parse files as both gate-level and cell-level layouts" << std::endl; } else { + const auto store_ntks = [&](auto&& reader) + { + for (const auto& ln : reader.get_networks(sort)) + { + store().extend() = ln; + } + }; + try { if (is_set("aig")) @@ -114,7 +118,7 @@ class read_command : public command store_ntks(reader); } - if (is_set("fgl") || is_set("qca")) + if (is_set("fgl") || is_set("sqd") || is_set("fqca")) { if (std::filesystem::exists(filename)) { @@ -205,7 +209,23 @@ class read_command : public command << std::endl; } } - if (is_set("qca")) + else if (is_set("sqd")) + { + try + { + const auto layout_name = std::filesystem::path{filename}.stem().string(); + + store().extend() = + std::make_shared( + fiction::read_sqd_layout(filename, + layout_name)); + } + catch (const fiction::sqd_parsing_error& e) + { + env->out() << e.what() << std::endl; + } + } + else if (is_set("fqca")) { try { @@ -250,7 +270,7 @@ class read_command : public command } catch (...) { - env->out() << "[e] no networks or layouts were read" << std::endl; + env->out() << "[e] I/O error: no file could be read" << std::endl; } } diff --git a/cli/cmd/simulation/opdom.hpp b/cli/cmd/simulation/opdom.hpp new file mode 100644 index 000000000..d1e2b495a --- /dev/null +++ b/cli/cmd/simulation/opdom.hpp @@ -0,0 +1,341 @@ +// +// Created by marcel on 12.12.23. +// + +#ifndef FICTION_CMD_OPDOM_HPP +#define FICTION_CMD_OPDOM_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class opdom_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit opdom_command(const environment::ptr& e) : + command(e, "Opdom is a quick and exact electrostatic ground state simulation algorithm designed " + "specifically for SiDB layouts. It provides a significant performance advantage of more than " + "three orders of magnitude over ExGS from SiQAD.") + { + add_option("--random_sampling,-r", num_random_samples, + "Use random sampling instead of grid search with this many random samples"); + add_option("--flood_fill,-f", num_random_samples, + "Use flood fill instead of grid search with this many initial random samples"); + add_option("--contour_tracing,-c", num_random_samples, + "Use contour tracing instead of grid search with up to this many random " + "samples"); + + add_option("filename", filename, "CSV filename to write the operational domain to")->required(); + + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + + add_option("--x_sweep", x_sweep, "Sweep parameter of the x dimension [epsilon_r, lambda_tf, mu_minus]", true); + add_option("--y_sweep", y_sweep, "Sweep parameter of the y dimension [epsilon_r, lambda_tf, mu_minus]", true); + + add_option("--x_min", params.x_min, "Minimum value of the x dimension sweep", true); + add_option("--x_max", params.x_max, "Maximum value of the x dimension sweep", true); + add_option("--x_step", params.x_step, "Step size of the x dimension sweep", true); + add_option("--y_min", params.y_min, "Minimum value of the y dimension sweep", true); + add_option("--y_max", params.y_max, "Maximum value of the y dimension sweep", true); + add_option("--y_step", params.y_step, "Step size of the y dimension sweep", true); + } + + protected: + /** + * Function to perform the operational domain call. + */ + void execute() override + { + // reset operational domain and stats + op_domain = {}; + stats = {}; + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + // check for valid x and y parameter bounds + if (params.x_min >= params.x_max) + { + env->out() << "[e] x_min must be smaller than x_max" << std::endl; + reset_params(); + return; + } + if (params.y_min >= params.y_max) + { + env->out() << "[e] y_min must be smaller than y_max" << std::endl; + reset_params(); + return; + } + + // make sure that at most one algorithm is selected + const std::array algorithm_selections = {is_set("random_sampling"), is_set("flood_fill"), + is_set("contour_tracing")}; + if (std::count(algorithm_selections.cbegin(), algorithm_selections.cend(), true) > 1) + { + env->out() << "[e] only one algorithm can be selected at a time" << std::endl; + reset_params(); + return; + } + + auto& cs = store(); + + // error case: empty cell layout store + if (cs.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + auto& ts = store(); + + // error case: empty truth table store + if (ts.empty()) + { + env->out() << "[w] no truth table in store" << std::endl; + reset_params(); + return; + } + + // default sweep parameters + if (x_sweep.empty() && y_sweep.empty()) + { + x_sweep = "epsilon_r"; + y_sweep = "lambda_tf"; + } + else + { + // overwrite x and y sweep with their respective lower-case string representations + std::transform(x_sweep.begin(), x_sweep.end(), x_sweep.begin(), ::tolower); + std::transform(y_sweep.begin(), y_sweep.end(), y_sweep.begin(), ::tolower); + + static constexpr const std::array valid_sweep_params = {"epsilon_r", "lambda_tf", "mu_minus"}; + + // check if x sweep parameter is valid + if (std::find(valid_sweep_params.cbegin(), valid_sweep_params.cend(), x_sweep) == valid_sweep_params.cend()) + { + env->out() << "[e] invalid x sweep parameter \"" << x_sweep + << "\". Has to be one of [epsilon_r, lambda_tf, " + "mu_minus]" + << std::endl; + reset_params(); + return; + } + + // check if y sweep parameter is valid + if (std::find(valid_sweep_params.cbegin(), valid_sweep_params.cend(), y_sweep) == valid_sweep_params.cend()) + { + env->out() << "[e] invalid y sweep parameter \"" << y_sweep + << "\". Has to be one of [epsilon_r, lambda_tf, " + "mu_minus]" + << std::endl; + reset_params(); + return; + } + } + + // assign x sweep parameters + if (x_sweep == "epsilon_r") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::EPSILON_R; + } + else if (x_sweep == "lambda_tf") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::LAMBDA_TF; + } + else if (x_sweep == "mu_minus") + { + params.x_dimension = fiction::operational_domain::sweep_parameter::MU_MINUS; + } + + // assign y sweep parameters + if (y_sweep == "epsilon_r") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::EPSILON_R; + } + else if (y_sweep == "lambda_tf") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::LAMBDA_TF; + } + else if (y_sweep == "mu_minus") + { + params.y_dimension = fiction::operational_domain::sweep_parameter::MU_MINUS; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto opdom = [this, &ts, &get_name](auto&& lyt_ptr) + { + const auto tt_ptr = ts.current(); + + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if (lyt_ptr->num_pis() == 0 || lyt_ptr->num_pos() == 0) + { + env->out() << fmt::format("[e] {} requires primary input and output cells to simulate its " + "Boolean function", + get_name(lyt_ptr)) + << std::endl; + return; + } + + params.sim_params = physical_params; + + if (is_set("random_sampling")) + { + op_domain = fiction::operational_domain_random_sampling(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else if (is_set("flood_fill")) + { + op_domain = fiction::operational_domain_flood_fill(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else if (is_set("contour_tracing")) + { + op_domain = fiction::operational_domain_contour_tracing(*lyt_ptr, std::vector{*tt_ptr}, + num_random_samples, params, &stats); + } + else + { + op_domain = fiction::operational_domain_grid_search(*lyt_ptr, std::vector{*tt_ptr}, + params, &stats); + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(opdom, cs.current()); + + write_op_domain(); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * Operational domain parameters. + */ + fiction::operational_domain_params params{}; + /** + * Operational domain stats. + */ + fiction::operational_domain_stats stats{}; + /** + * Number of random samples. + */ + std::size_t num_random_samples{}; + /** + * User input for the x dimension sweep parameter. + */ + std::string x_sweep{}; + /** + * User input for the y dimension sweep parameter. + */ + std::string y_sweep{}; + /** + * CSV filename to write the operational domain to. + */ + std::string filename{}; + /** + * The operational domain. + */ + fiction::operational_domain op_domain{}; + + /** + * Writes the operational domain to the specified CSV file. + */ + void write_op_domain() const + { + static const fiction::write_operational_domain_params write_opdom_params{"1", "0"}; + try + { + fiction::write_operational_domain(op_domain, filename, write_opdom_params); + } + catch (const std::exception& e) + { + env->out() << fmt::format("[e] {}", e.what()) << std::endl; + } + } + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the operational domain. + */ + [[nodiscard]] nlohmann::json log() const override + { + return nlohmann::json{ + {"Algorithm name", "QuickExact"}, + {"Runtime in seconds", mockturtle::to_seconds(stats.time_total)}, + {"Number of simulator invocations", stats.num_simulator_invocations}, + {"Number of evaluated parameter combinations", stats.num_evaluated_parameter_combinations}, + {"Number of operational parameter combinations", stats.num_operational_parameter_combinations}, + {"Number of non-operational parameter combinations", stats.num_non_operational_parameter_combinations}}; + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + x_sweep = {}; + y_sweep = {}; + filename = {}; + } +}; + +ALICE_ADD_COMMAND(opdom, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_OPDOM_HPP diff --git a/cli/cmd/simulation/quickexact.hpp b/cli/cmd/simulation/quickexact.hpp new file mode 100644 index 000000000..9114f9cdd --- /dev/null +++ b/cli/cmd/simulation/quickexact.hpp @@ -0,0 +1,195 @@ +// +// Created by marcel on 06.12.23. +// + +#ifndef FICTION_CMD_QUICKEXACT_HPP +#define FICTION_CMD_QUICKEXACT_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class quickexact_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit quickexact_command(const environment::ptr& e) : + command(e, "QuickExact is a quick and exact electrostatic ground state simulation algorithm designed " + "specifically for SiDB layouts. It provides a significant performance advantage of more than " + "three orders of magnitude over ExGS from SiQAD.") + { + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + add_option("--global_potential,-g", params.global_potential, + "Global potential applied to the entire layout (unit: V)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset sim result + sim_result = {}; + min_energy = std::numeric_limits::infinity(); + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + auto& s = store(); + + // error case: empty cell layout store + if (s.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto quickexact = [this, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.physical_parameters = physical_params; + + sim_result = fiction::quickexact(*lyt_ptr, params); + + if (sim_result.charge_distributions.empty()) + { + env->out() << fmt::format("[e] ground state of {} could not be determined", get_name(lyt_ptr)) + << std::endl; + } + else + { + const auto min_energy_distr = fiction::minimum_energy_distribution( + sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()); + + min_energy = min_energy_distr->get_system_energy(); + + store().extend() = + std::make_shared(*min_energy_distr); + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(quickexact, s.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * QuickExact parameters. + */ + fiction::quickexact_params params{}; + /** + * Simulation result. + */ + fiction::sidb_simulation_result sim_result{}; + /** + * Minimum energy. + */ + double min_energy{std::numeric_limits::infinity()}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + try + { + return nlohmann::json{ + {"Algorithm name", sim_result.algorithm_name}, + {"Simulation runtime", sim_result.simulation_runtime.count()}, + {"Physical parameters", + {{"base", std::any_cast(sim_result.additional_simulation_parameters.at( + "base_number"))}, // fetch the automatically inferred base number + {"epsilon_r", sim_result.physical_parameters.epsilon_r}, + {"lambda_tf", sim_result.physical_parameters.lambda_tf}, + {"mu_minus", sim_result.physical_parameters.mu_minus}, + {"global_potential", + std::any_cast(sim_result.additional_simulation_parameters.at("global_potential"))}}}, + {"Ground state energy (eV)", min_energy}, + {"Number of stable states", sim_result.charge_distributions.size()}}; + } + catch (...) + { + return nlohmann::json{}; + } + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(quickexact, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_QUICKEXACT_HPP diff --git a/cli/cmd/simulation/quicksim.hpp b/cli/cmd/simulation/quicksim.hpp new file mode 100644 index 000000000..93acb708b --- /dev/null +++ b/cli/cmd/simulation/quicksim.hpp @@ -0,0 +1,203 @@ +// +// Created by marcel on 07.12.23. +// + +#ifndef FICTION_CMD_QUICKSIM_HPP +#define FICTION_CMD_QUICKSIM_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class quicksim_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit quicksim_command(const environment::ptr& e) : + command(e, + "The QuickSim algorithm is a heuristic electrostatic ground state simulation algorithm for SiDB " + "layouts. It determines physically valid charge configurations (with minimal energy). Depending on " + "the simulation parameters, the ground state is found with a certain probability after one run.") + { + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + add_option("--iterations,-i", params.interation_steps, "Number of iterations to run the simulation for", true); + add_option("--alpha,-a", params.alpha, + "alpha parameter (should be reduced if not charge distribution can be determined)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset sim result + sim_result = {}; + min_energy = std::numeric_limits::infinity(); + + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + if (params.alpha <= 0) + { + env->out() << "[e] alpha must be positive" << std::endl; + reset_params(); + return; + } + + auto& s = store(); + + // error case: empty cell layout store + if (s.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto quicksim = [this, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.phys_params = physical_params; + + sim_result = fiction::quicksim(*lyt_ptr, params); + + if (sim_result.charge_distributions.empty()) + { + env->out() << fmt::format("[e] no stable charge distribution could be determined for {}", + get_name(lyt_ptr)) + << std::endl; + } + else + { + const auto min_energy_distr = fiction::minimum_energy_distribution( + sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()); + + min_energy = min_energy_distr->get_system_energy(); + + store().extend() = + std::make_shared(*min_energy_distr); + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(quicksim, s.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * QuickSim parameters. + */ + fiction::quicksim_params params{}; + /** + * Simulation result. + */ + fiction::sidb_simulation_result sim_result{}; + /** + * Minimum energy. + */ + double min_energy{std::numeric_limits::infinity()}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + try + { + return nlohmann::json{ + {"Algorithm name", sim_result.algorithm_name}, + {"Simulation runtime", sim_result.simulation_runtime.count()}, + {"Physical parameters", + {{"epsilon_r", sim_result.physical_parameters.epsilon_r}, + {"lambda_tf", sim_result.physical_parameters.lambda_tf}, + {"mu_minus", sim_result.physical_parameters.mu_minus}}}, + {"Lowest state energy (eV)", min_energy}, + {"Number of stable states", sim_result.charge_distributions.size()}, + {"Iteration steps", + std::any_cast(sim_result.additional_simulation_parameters.at("iteration_steps"))}, + {"alpha", std::any_cast(sim_result.additional_simulation_parameters.at("alpha"))}}; + } + catch (...) + { + return nlohmann::json{}; + } + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(quicksim, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_QUICKSIM_HPP diff --git a/cli/cmd/simulation/temp.hpp b/cli/cmd/simulation/temp.hpp new file mode 100644 index 000000000..f19c6294a --- /dev/null +++ b/cli/cmd/simulation/temp.hpp @@ -0,0 +1,234 @@ +// +// Created by marcel on 06.12.23. +// + +#ifndef FICTION_CMD_TEMP_HPP +#define FICTION_CMD_TEMP_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace alice +{ +/** + * + */ +class temp_command : public command +{ + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ + explicit temp_command(const environment::ptr& e) : + command(e, "Temperature-aware simulation of SiDB layouts. Uses QuickExact to determine the critical " + "temperature at which the ground state of the current SiDB layout is populated with a " + "probability below a given confidence level. For gate-based simulation, the probability of " + "erroneous calculations of the gate are applied.") + { + add_option( + "--confidence_level,-c", params.confidence_level, + "Probability threshold for ground state population. The temperature at which the simulation finds the " + "ground state to be populated with a probability of less than the given percentage, is determined to be " + "the critical temperature. For gate-based simulation, this is the probability of erroneous calculations of " + "the gate.", + true); + add_option("--max_temperature,-t", params.max_temperature, + "Maximum simulation temperature beyond which no simulation will be conducted (~ 126 °C by default) " + "(unit: K)", + true); + add_flag("--gate_based,-g", + "Gate-based simulation that matches the current SiDB layout in store against the current truth table " + "in store and considers the probability of erroneous calculations of the gate."); + + add_option("--epsilon_r,-e", physical_params.epsilon_r, "Electric permittivity of the substrate (unit-less)", + true); + add_option("--lambda_tf,-l", physical_params.lambda_tf, "Thomas-Fermi screening distance (unit: nm)", true); + add_option("--mu_minus,-m", physical_params.mu_minus, "Energy transition level (0/-) (unit: eV)", true); + } + + protected: + /** + * Function to perform the simulation call. + */ + void execute() override + { + // reset statistics + stats = {}; + + if (params.confidence_level <= 0 || params.confidence_level > 1) + { + env->out() << "[e] confidence_level must be in (0, 1]" << std::endl; + reset_params(); + return; + } + if (physical_params.epsilon_r <= 0) + { + env->out() << "[e] epsilon_r must be positive" << std::endl; + reset_params(); + return; + } + if (physical_params.lambda_tf <= 0) + { + env->out() << "[e] lambda_tf must be positive" << std::endl; + reset_params(); + return; + } + + auto& cs = store(); + + // error case: empty cell layout store + if (cs.empty()) + { + env->out() << "[w] no cell layout in store" << std::endl; + reset_params(); + return; + } + + auto& ts = store(); + + // error case: empty truth table store + if (is_set("gate_based")) + { + if (ts.empty()) + { + env->out() << "[w] no truth table in store" << std::endl; + reset_params(); + return; + } + } + + const auto get_name = [](auto&& lyt_ptr) -> std::string { return fiction::get_name(*lyt_ptr); }; + + const auto temp = [this, &ts, &get_name](auto&& lyt_ptr) + { + using Lyt = typename std::decay_t::element_type; + + if constexpr (fiction::has_sidb_technology_v) + { + if constexpr (fiction::is_charge_distribution_surface_v) + { + env->out() << fmt::format( + "[w] {} already possesses a charge distribution; no simulation is conducted", + get_name(lyt_ptr)) + << std::endl; + } + else + { + params.physical_parameters = physical_params; + + if (is_set("gate_based")) + { + if (lyt_ptr->num_pis() == 0 || lyt_ptr->num_pos() == 0) + { + env->out() << fmt::format("[e] {} requires primary input and output cells to simulate its " + "Boolean function", + get_name(lyt_ptr)) + << std::endl; + return; + } + + const auto tt_ptr = ts.current(); + + fiction::critical_temperature_gate_based(*lyt_ptr, std::vector{*tt_ptr}, params, + &stats); + } + else + { + fiction::critical_temperature_non_gate_based(*lyt_ptr, params, &stats); + } + + if (stats.num_valid_lyt == 0) + { + env->out() << fmt::format("[e] ground state of {} could not be determined", get_name(lyt_ptr)) + << std::endl; + } + else + { + env->out() << fmt::format("[i] critical temperature of {} is {}{} K", get_name(lyt_ptr), + (stats.critical_temperature == params.max_temperature ? "> " : ""), + stats.critical_temperature) + << std::endl; + + if (stats.num_valid_lyt > 1) + { + env->out() << fmt::format( + "[i] energy between the ground state and the first erroneous is {} meV", + fiction::round_to_n_decimal_places( + stats.energy_between_ground_state_and_first_erroneous, 2)) + << std::endl; + } + } + } + } + else + { + env->out() << fmt::format("[e] {} is not an SiDB layout", get_name(lyt_ptr)) << std::endl; + } + }; + + std::visit(temp, cs.current()); + + reset_params(); + } + + private: + /** + * Physical parameters for the simulation. + */ + fiction::sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + /** + * Critical temperature parameters. + */ + fiction::critical_temperature_params params{}; + /** + * Critical temperature statistics. + */ + fiction::critical_temperature_stats stats{}; + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing details about the simulation. + */ + [[nodiscard]] nlohmann::json log() const override + { + return nlohmann::json{{"Algorithm name", stats.algorithm_name}, + {"Physical parameters", + {{"base", stats.physical_parameters.base}, + {"epsilon_r", stats.physical_parameters.epsilon_r}, + {"lambda_tf", stats.physical_parameters.lambda_tf}, + {"mu_minus", stats.physical_parameters.mu_minus}}}, + {"Critical temperature", stats.critical_temperature}, + {"Number of stable states", stats.num_valid_lyt}, + {"Energy difference between ground state and first erroneous state", + stats.energy_between_ground_state_and_first_erroneous}}; + } + /** + * Resets the parameters to their default values. + */ + void reset_params() + { + physical_params = fiction::sidb_simulation_parameters{2, -0.32, 5.6, 5.0}; + params = {}; + } +}; + +ALICE_ADD_COMMAND(temp, "Simulation") + +} // namespace alice + +#endif // FICTION_CMD_TEMP_HPP diff --git a/cli/commands.hpp b/cli/commands.hpp index 523d55ab8..8e37a7b96 100644 --- a/cli/commands.hpp +++ b/cli/commands.hpp @@ -5,8 +5,11 @@ #ifndef FICTION_COMMANDS_HPP #define FICTION_COMMANDS_HPP +// general commands #include "cmd/general/clear.hpp" #include "cmd/general/version.hpp" + +// input/output commands #include "cmd/io/blif.hpp" #include "cmd/io/fgl.hpp" #include "cmd/io/fqca.hpp" @@ -17,6 +20,9 @@ #include "cmd/io/sqd.hpp" #include "cmd/io/tt.hpp" #include "cmd/io/verilog.hpp" + +// logic synthesis commands +#ifdef FICTION_LOGIC_SYNTHESIS_FLOW #include "cmd/logic/akers.hpp" #include "cmd/logic/balance.hpp" #include "cmd/logic/fanouts.hpp" @@ -26,6 +32,10 @@ #include "cmd/logic/miginvprop.hpp" #include "cmd/logic/random.hpp" #include "cmd/logic/simulate.hpp" +#endif + +// physical design and validation commands +#ifdef FICTION_PHYSICAL_DESIGN_FLOW #include "cmd/physical_design/exact.hpp" #include "cmd/physical_design/hex.hpp" #include "cmd/physical_design/onepass.hpp" @@ -36,5 +46,14 @@ #include "cmd/technology/energy.hpp" #include "cmd/verification/check.hpp" #include "cmd/verification/equiv.hpp" +#endif + +// physical simulation commands +#ifdef FICTION_SIMULATION_FLOW +#include "cmd/simulation/opdom.hpp" +#include "cmd/simulation/quickexact.hpp" +#include "cmd/simulation/quicksim.hpp" +#include "cmd/simulation/temp.hpp" +#endif #endif // FICTION_COMMANDS_HPP diff --git a/cli/stores.hpp b/cli/stores.hpp index 0817dcfd4..9a0761632 100644 --- a/cli/stores.hpp +++ b/cli/stores.hpp @@ -24,6 +24,7 @@ namespace alice { + /** * Truth tables. * @@ -163,7 +164,7 @@ ALICE_ADD_STORE(fiction::gate_layout_t, "gate_layout", "g", "gate layout", "gate ALICE_PRINT_STORE(fiction::gate_layout_t, os, layout) { - const auto print = [&os](auto&& lyt_ptr) { fiction::print_gate_level_layout(os, *lyt_ptr); }; + const auto print = [&os](auto&& lyt_ptr) { fiction::print_layout(*lyt_ptr, os); }; std::visit(print, layout); } @@ -373,7 +374,7 @@ ALICE_ADD_STORE(fiction::cell_layout_t, "cell_layout", "c", "cell layout", "cell ALICE_PRINT_STORE(fiction::cell_layout_t, os, layout) { - const auto print = [&os](auto&& lyt_ptr) { fiction::print_cell_level_layout(os, *lyt_ptr); }; + const auto print = [&os](auto&& lyt_ptr) { fiction::print_layout(*lyt_ptr, os); }; std::visit(print, layout); } @@ -391,10 +392,10 @@ ALICE_DESCRIBE_STORE(fiction::cell_layout_t, layout) z = lyt_ptr->z() + 1; } - return fmt::format("{} ({}) - {} × {}{}, I/O: {}/{}, cells: {}", lyt_ptr->get_layout_name(), + return fmt::format("{} ({}) - {} × {}{}, I/O: {}/{}, {}: {}", lyt_ptr->get_layout_name(), fiction::tech_impl_name>, lyt_ptr->x() + 1, lyt_ptr->y() + 1, (z ? fmt::format(" × {}", z) : ""), lyt_ptr->num_pis(), lyt_ptr->num_pos(), - lyt_ptr->num_cells()); + fiction::tech_cell_name>, lyt_ptr->num_cells()); }; return std::visit(describe, layout); @@ -413,10 +414,10 @@ ALICE_PRINT_STORE_STATISTICS(fiction::cell_layout_t, os, layout) z = lyt_ptr->z() + 1; } - os << fmt::format("[i] {} ({}) - {} × {}{}, I/O: {}/{}, cells: {}\n", lyt_ptr->get_layout_name(), + os << fmt::format("[i] {} ({}) - {} × {}{}, I/O: {}/{}, {}: {}\n", lyt_ptr->get_layout_name(), fiction::tech_impl_name>, lyt_ptr->x() + 1, lyt_ptr->y() + 1, (z ? fmt::format(" × {}", z) : ""), lyt_ptr->num_pis(), lyt_ptr->num_pos(), - lyt_ptr->num_cells()); + fiction::tech_cell_name>, lyt_ptr->num_cells()); }; std::visit(print_statistics, layout); @@ -432,7 +433,7 @@ ALICE_LOG_STORE_STATISTICS(fiction::cell_layout_t, layout) {"technology", fiction::tech_impl_name>}, {"inputs", lyt_ptr->num_pis()}, {"outputs", lyt_ptr->num_pos()}, - {"cells", lyt_ptr->num_cells()}, + {fiction::tech_cell_name>, lyt_ptr->num_cells()}, {"layout", {{"x-size", lyt_ptr->x() + 1}, {"y-size", lyt_ptr->y() + 1}, diff --git a/cmake/PackageProject.cmake b/cmake/PackageProject.cmake index 6179d9005..24321c536 100644 --- a/cmake/PackageProject.cmake +++ b/cmake/PackageProject.cmake @@ -8,8 +8,8 @@ function(fiction_package_project) cmake_policy(SET CMP0103 NEW) # disallow multiple calls with the same NAME endif() - # if CMake version >= 3.23 - if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.23") + # if CMake version >= 3.24 + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24") cmake_policy(SET CMP0135 NEW) # enable new timestamp checking behavior for # fetching content endif() diff --git a/docs/algorithms/sidb_simulation.rst b/docs/algorithms/sidb_simulation.rst index 663995861..657905da1 100644 --- a/docs/algorithms/sidb_simulation.rst +++ b/docs/algorithms/sidb_simulation.rst @@ -71,6 +71,7 @@ Energy Calculation **Header:** ``fiction/algorithms/simulation/sidb/minimum_energy.hpp`` .. doxygenfunction:: fiction::minimum_energy +.. doxygenfunction:: fiction::minimum_energy_distribution **Header:** ``fiction/algorithms/simulation/sidb/is_ground_state.hpp`` @@ -90,7 +91,7 @@ Temperature Behavior .. doxygenfunction:: fiction::critical_temperature_gate_based .. doxygenfunction:: fiction::critical_temperature_non_gate_based -**Header:** ``fiction/algorithms/simulation/sidb/occupation_probability_excited_states.hpp`` +**Header:** ``fiction/algorithms/simulation/sidb/occupation_probability_of_excited_states.hpp`` .. doxygenfunction:: fiction::occupation_probability_gate_based .. doxygenfunction:: fiction::occupation_probability_non_gate_based @@ -129,6 +130,7 @@ Random SiDB Layout Generator **Header:** ``fiction/algorithms/simulation/sidb/random_sidb_layout_generator.hpp`` .. doxygenstruct:: fiction::generate_random_sidb_layout_params + :members: .. doxygenfunction:: fiction::generate_random_sidb_layout .. doxygenfunction:: fiction::generate_multiple_random_sidb_layouts diff --git a/docs/cli.rst b/docs/cli.rst index f50241ecd..44693edab 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -215,6 +215,7 @@ throughput (TP) and thereby, the amount of clock cycles the PIs need to be stall A ``network`` can also be simulated for comparison by using ``simulate -n``. + Equivalence checking (``equiv``) -------------------------------- @@ -263,6 +264,109 @@ simulators are currently supported: If no filename is given, the stored layout name will be used and the file will be written to the current folder. +Physical Simulation of SiDBs +---------------------------- + +Performing physical simulation of SiDB layouts is crucial for understanding layout behavior and +facilitating rapid prototyping, eliminating the need for expensive and time-intensive fabrication processes. +The command ``read --sqd`` (or ``read -s``) is used to import a SiDB layout from an sqd-file, a format compatible with `SiQAD `_. +The SiDB layout can be visualized using the ``print -c`` command. Currently, *fiction* provides two electrostatic physical simulators: +the exact one *QuickExact* and the scalable one *QuickSim*. + +QuickExact (``quickexact``) +########################### + +*QuickExact* serves as an exact simulator, meticulously determining all physically valid charge distributions. +It enumerates all possible charge distributions. However, by incorporating three techniques, namely +1.) Physically-informed Search Space Pruning, 2.) Partial Solution Caching, and 3.) Effective State Enumeration, it provides +a significant performance advantage of more than three orders of magnitude over ExGS from SiQAD. For additional details, +see `the paper `_. + +Most important parameters: + +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening length :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) + +See ``quickexact -h`` for a full list. + +The simulated ground state charge distribution can be printed with ``print -c``. + + +QuickSim (``quicksim``) +####################### + +*QuickSim* serves as a scalable simulator designed to determine the ground state charge distribution +for a given SiDB layout. To enhance efficiency, effective search space pruning techniques, such as +(`max-min diversity distributions `_), are integrated. +For more in-depth information, refer to `the paper `_. + +Most important parameters: + +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) +- Number of iterations (``-i``) +- :math:`\alpha` value (``-a``) + +The simulated ground state charge distribution can be printed with ``print -c``. + +Critical Temperature (``temp``) +############################### + +The critical temperature of an SiDB layout is the temperature at which the layout's ground state is populated with a +probability larger than a certain threshold. This threshold is specified as a confidence level :math:`1 - \eta`, where +:math:`\eta \in [0,1]`. The simulation can be conducted for gate-based SiDB layouts as well, where the gate is +simulated with respect to the stability of a given Boolean function in form of the current truth table in store. +For more in-depth information, refer to `the paper `_. + +Most important parameters: + +- Confidence level :math:`1 - \eta` (``-c``) +- Maximum temperature in K to explore (``-t``) +- Gate-based simulation toggle (``-g``) +- Relative permittivity :math:`\epsilon_r` (``-e``) +- Thomas-Fermi screening :math:`\lambda_{tf}` (``-l``) +- Energy transition level (0/-) :math:`\mu_-` (``-m``) + + +Operational Domain (``opdom``) +############################## + +Computes the operational domain of the current SiDB cell-level layout in store. The operational domain is the set of all +parameter combinations for which the layout is logically operational. Logical operation is defined as the layout +implementing the current truth table in store. The input BDL pairs of the layout are assumed to be in the same order as +the inputs of the truth table. +For more information, see `the paper `_. + +The command ``opdom`` writes the operational domain to a CSV file with the given filename from where it can be further +processed by other tools. + +The parameter space to sweep over can be specified by the user via the flags +- ``--x_sweep`` +- ``--y_sweep`` +which have to be either ``epsilon_r``, ``lambda_tf``, or ``mu_minus``. The default is ``epsilon_r`` for ``--x_sweep`` and +``lambda_tf`` for ``--y_sweep``. + +Additionally, min, max, and step size values can be specified for each parameter using the flags +- ``--x_min`` +- ``--x_max`` +- ``--x_step`` +- ``--y_min`` +- ``--y_max`` +- ``--y_step`` +respectively. The default values are 1, 10, and 0.1 on both axis, for min, max, and step, respectively. + +By default, grid search is applied to explore the operational domain. The algorithm can be changed by specifying one of +the following options: +- ``--random_sampling``/``-r`` +- ``--flood_fill``/``-f`` +- ``--contour_tracing``/``-c`` +each of which start from a set of random samples, whose number has to be passed as an argument to the flag. + +See ``opdom -h`` for a full list of arguments. + + Area usage (``area``) --------------------- @@ -365,7 +469,7 @@ Additionally, *fiction* itself can be part of a bash script. Consider the follow for filepath in ../benchmarks/TOY/*.v; do f="${filepath##*/}" - ./fiction -c "read $filepath; ortho; cell; qca ${f%.*}.qca" + ./fiction -c "read $filepath; ortho; cell; qca ${f%.*}.qca" done where the for-loop iterates over all Verilog files in the ``../benchmarks/TOY/`` folder. By using the flag ``-c``, a diff --git a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp index d54934b27..ef2b4c65b 100644 --- a/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp +++ b/include/fiction/algorithms/simulation/sidb/critical_temperature.hpp @@ -7,23 +7,18 @@ #include "fiction/algorithms/iter/bdl_input_iterator.hpp" #include "fiction/algorithms/simulation/sidb/calculate_energy_and_state_type.hpp" +#include "fiction/algorithms/simulation/sidb/can_positive_charges_occur.hpp" #include "fiction/algorithms/simulation/sidb/detect_bdl_pairs.hpp" #include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/exhaustive_ground_state_simulation.hpp" -#include "fiction/algorithms/simulation/sidb/is_operational.hpp" #include "fiction/algorithms/simulation/sidb/occupation_probability_of_excited_states.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/algorithms/simulation/sidb/quicksim.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/cell_technologies.hpp" -#include "fiction/technology/charge_distribution_surface.hpp" #include "fiction/technology/physical_constants.hpp" -#include "fiction/technology/sidb_charge_state.hpp" #include "fiction/traits.hpp" -#include "fiction/types.hpp" -#include "fiction/utils/hash.hpp" #include "fiction/utils/math_utils.hpp" -#include "fiction/utils/truth_table_utils.hpp" #include #include @@ -33,10 +28,10 @@ #include #include #include +#include #include #include -#include -#include +#include #include #include @@ -48,29 +43,10 @@ namespace fiction */ struct critical_temperature_params { - /** - * An enumeration of modes to use for the *Critical Temperature* Simulation. - */ - enum class critical_temperature_mode - { - /** - * The *Critical Temperature* is determined by considering the gate logic of the given layout. In this mode, it - * is distinguished between excited charge distributions that produce the correct output (with respect to a - * truth table) and those that do not. - */ - GATE_BASED_SIMULATION, - /** - * The *Critical Temperature* is determined by ignoring the gate logic of the given layout. This mode does not - * distinguish between excited charge distributions that produce the correct output (with respect to a truth - * table) and those that do not. - */ - NON_GATE_BASED_SIMULATION - }; - /** * An enumeration of simulation modes (exact vs. approximate) to use for the *Critical Temperature* Simulation. */ - enum class simulation_engine + enum class simulation_engine : uint8_t { /** * This simulation engine computes *Critical Temperature* values with 100 % accuracy. @@ -83,26 +59,35 @@ struct critical_temperature_params APPROXIMATE }; /** - * All Parameters for physical SiDB simulations. + * All parameters for physical SiDB simulations. */ - quicksim_params simulation_params{}; + sidb_simulation_parameters physical_parameters{}; /** * Simulation mode to determine the *Critical Temperature*. */ simulation_engine engine = simulation_engine::EXACT; /** - * Probability that the ground state is less populated due to temperature. For gate-based simulation, this is the - * probability of erroneous calculations of the gate. + * Probability threshold for ground state population. The temperature at which the simulation finds the ground state + * to be populated with a probability of less than the given percentage, is determined to be the critical + * temperature. For gate-based simulation, this is the probability of erroneous calculations of the gate. */ double confidence_level{0.99}; /** - * Simulation stops at max_temperature (~ 126 °C by default) (unit: K). + * Maximum simulation temperature beyond which no simulation will be conducted (~ 126 °C by default) (unit: K). */ double max_temperature{400}; /** * Parameters for the BDL pair detection algorithms. */ detect_bdl_pairs_params bdl_params{}; + /** + * Number of iteration steps for the *QuickSim* algorithm (only applicable if engine == APPROXIMATE). + */ + uint64_t iteration_steps{80}; + /** + * Alpha parameter for the *QuickSim* algorithm (only applicable if engine == APPROXIMATE). + */ + double alpha{0.7}; }; /** @@ -113,6 +98,10 @@ struct critical_temperature_params template struct critical_temperature_stats { + /** + * All parameters for physical SiDB simulations. + */ + sidb_simulation_parameters physical_parameters{}; /** * Name of the algorithm used to compute the physically valid charge distributions. */ @@ -126,7 +115,7 @@ struct critical_temperature_stats */ uint64_t num_valid_lyt{}; /** - * Energy difference between the ground state and the first (erroneous) excited state (unit: eV). + * Energy difference between the ground state and the first (erroneous) excited state (unit: meV). */ double energy_between_ground_state_and_first_erroneous = std::numeric_limits::infinity(); /** @@ -141,7 +130,7 @@ struct critical_temperature_stats if (num_valid_lyt != 0) { out << fmt::format("'# of physically valid charge configurations': {} | Energy between ground state and " - "first erroneous: {}\n", + "first erroneous in meV: {}\n", num_valid_lyt, energy_between_ground_state_and_first_erroneous); } else @@ -168,9 +157,10 @@ class critical_temperature_impl bii(bdl_input_iterator{layout, params.bdl_params}) { - stats.critical_temperature = params.max_temperature; + stats.physical_parameters = params.physical_parameters; stats.algorithm_name = (params.engine == critical_temperature_params::simulation_engine::EXACT) ? "QuickExact" : "QuickSim"; + stats.critical_temperature = params.max_temperature; } /** @@ -187,7 +177,8 @@ class critical_temperature_impl stats.critical_temperature = 0.0; return; } - else if (layout.num_cells() > 1) + + if (layout.num_cells() > 1) { const auto output_bdl_pairs = detect_bdl_pairs(layout, sidb_technology::cell_type::OUTPUT, params.bdl_params); @@ -196,7 +187,7 @@ class critical_temperature_impl for (auto i = 0u; i < spec.front().num_bits(); ++i, ++bii) { // if positively charged SiDBs can occur, the SiDB layout is considered as non-operational - if (can_positive_charges_occur(*bii, params.simulation_params.phys_params)) + if (can_positive_charges_occur(*bii, params.physical_parameters)) { stats.critical_temperature = 0.0; return; @@ -244,21 +235,23 @@ class critical_temperature_impl void non_gate_based_simulation() noexcept { sidb_simulation_result simulation_results{}; + if (params.engine == critical_temperature_params::simulation_engine::EXACT) { - stats.algorithm_name = "QuickExact"; + const quickexact_params qe_params{params.physical_parameters, + quickexact_params::automatic_base_number_detection::OFF}; + // All physically valid charge configurations are determined for the given layout (`QuickExact` simulation // is used to provide 100 % accuracy for the Critical Temperature). - const quickexact_params qe_params{params.simulation_params.phys_params, - quickexact_params::automatic_base_number_detection::OFF}; simulation_results = quickexact(layout, qe_params); } else { - stats.algorithm_name = "QuickSim"; - // All physically valid charge configurations are determined for the given layout (exhaustive ground state - // simulation is used to provide 100 % accuracy for the Critical Temperature). - simulation_results = quicksim(layout, params.simulation_params); + const quicksim_params qs_params{params.physical_parameters, params.iteration_steps, params.alpha}; + + // All physically valid charge configurations are determined for the given layout (probabilistic ground + // state simulation is used). + simulation_results = quicksim(layout, qs_params); } // The number of physically valid charge configurations is stored. @@ -417,19 +410,20 @@ class critical_temperature_impl [[nodiscard]] sidb_simulation_result physical_simulation_of_layout(const bdl_input_iterator& bdl_iterator) noexcept { - assert(params.simulation_params.phys_params.base == 2 && "base number is set to 3"); + assert(params.physical_parameters.base == 2 && "base number has to be 2"); + if (params.engine == critical_temperature_params::simulation_engine::EXACT) { // perform exact simulation - const quickexact_params quickexact_params{ - params.simulation_params.phys_params, - fiction::quickexact_params::automatic_base_number_detection::OFF}; - return quickexact(*bdl_iterator, quickexact_params); + const quickexact_params qe_params{ + params.physical_parameters, fiction::quickexact_params::automatic_base_number_detection::OFF}; + return quickexact(*bdl_iterator, qe_params); } if (params.engine == critical_temperature_params::simulation_engine::APPROXIMATE) { - return quicksim(*bdl_iterator, params.simulation_params); + const quicksim_params qs_params{params.physical_parameters, params.iteration_steps, params.alpha}; + return quicksim(*bdl_iterator, qs_params); } assert(false && "unsupported simulation engine"); @@ -467,8 +461,9 @@ double critical_temperature_gate_based(const Lyt& lyt, const std::vector& sp assert(!spec.empty()); // all elements in tts must have the same number of variables - assert(std::adjacent_find(spec.begin(), spec.end(), - [](const auto& a, const auto& b) { return a.num_vars() != b.num_vars(); }) == spec.end()); + assert(std::adjacent_find(spec.cbegin(), spec.cend(), + [](const auto& a, const auto& b) + { return a.num_vars() != b.num_vars(); }) == spec.cend()); critical_temperature_stats st{}; @@ -501,7 +496,6 @@ double critical_temperature_non_gate_based(const Lyt& lyt, const critical_temper { static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); static_assert(has_sidb_technology_v, "Lyt is not an SiDB layout"); - static_assert(has_siqad_coord_v, "Lyt is not based on SiQAD coordinates"); critical_temperature_stats st{}; diff --git a/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp b/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp index 085ef971e..4c62f11e8 100644 --- a/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp +++ b/include/fiction/algorithms/simulation/sidb/is_ground_state.hpp @@ -38,8 +38,10 @@ template return false; } - const auto min_energy_exact = minimum_energy(exhaustive_results.charge_distributions); - const auto min_energy_new_ap = minimum_energy(quicksim_results.charge_distributions); + const auto min_energy_exact = minimum_energy(exhaustive_results.charge_distributions.cbegin(), + exhaustive_results.charge_distributions.cend()); + const auto min_energy_new_ap = minimum_energy(exhaustive_results.charge_distributions.cbegin(), + exhaustive_results.charge_distributions.cend()); return round_to_n_decimal_places(std::abs(min_energy_exact - min_energy_new_ap), 6) == 0; } diff --git a/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp b/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp index d15001d90..ab258dc39 100644 --- a/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp +++ b/include/fiction/algorithms/simulation/sidb/maximum_defect_influence_position_and_distance.hpp @@ -6,6 +6,7 @@ #define FICTION_MAXIMUM_DEFECT_INFLUENCE_POSITION_AND_DISTANCE_HPP #include "fiction/algorithms/simulation/sidb/critical_temperature.hpp" +#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/layouts/bounding_box.hpp" #include "fiction/technology/sidb_defects.hpp" @@ -81,8 +82,10 @@ class maximum_defect_influence_position_and_distance_impl quickexact(layout, quickexact_params{params.physical_params, quickexact_params::automatic_base_number_detection::OFF}); - const auto min_energy = minimum_energy(simulation_results.charge_distributions); - uint64_t charge_index_layout = 0; + const auto min_energy = minimum_energy(simulation_results.charge_distributions.cbegin(), + simulation_results.charge_distributions.cend()); + + uint64_t charge_index_layout = 0; for (auto& lyt_result : simulation_results.charge_distributions) { @@ -109,8 +112,10 @@ class maximum_defect_influence_position_and_distance_impl // conduct simulation with defect auto simulation_result_defect = quickexact(lyt_defect, params_defect); - const auto min_energy_defect = minimum_energy(simulation_result_defect.charge_distributions); - uint64_t charge_index_defect_layout = 0; + const auto min_energy_defect = minimum_energy(simulation_result_defect.charge_distributions.cbegin(), + simulation_result_defect.charge_distributions.cend()); + + uint64_t charge_index_defect_layout = 0; // get the charge index of the ground state for (const auto& lyt_simulation_with_defect : simulation_result_defect.charge_distributions) diff --git a/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp b/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp index e57d797bb..9a37c1147 100644 --- a/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp +++ b/include/fiction/algorithms/simulation/sidb/minimum_energy.hpp @@ -5,30 +5,59 @@ #ifndef FICTION_MINIMUM_ENERGY_HPP #define FICTION_MINIMUM_ENERGY_HPP -#include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/traits.hpp" #include +#include #include -#include namespace fiction { /** - * Computes the minimum energy of a vector of charge_distribution_surface objects. + * Computes the minimum energy of a range of `charge_distribution_surface` objects. If the range is empty, infinity is + * returned. * - * @tparam Lyt Cell-level layout type. - * @param charge_lyts Vector of charge_distribution_surface objects. - * @return Value of the minimum energy found in the input vector (unit: eV). + * @tparam InputIt Must meet the requirements of `LegacyInputIterator`. + * @param first Begin of the range to examime. + * @param last End of the range to examine. + * @return Value of the minimum energy found in the input range (unit: eV), or infinity if the range is empty. */ -template -[[nodiscard]] double minimum_energy(const std::vector>& charge_lyts) noexcept +template +[[nodiscard]] double minimum_energy(const InputIt first, const InputIt last) noexcept { - static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); - static_assert(has_sidb_technology_v, "Lyt is not an SiDB layout"); + static_assert(std::is_base_of_v::iterator_category>, + "InputIt must meet the requirements of LegacyInputIterator"); + static_assert(is_charge_distribution_surface_v::value_type>, + "Range must be of charge_distribution_surface objects"); - return std::accumulate(charge_lyts.cbegin(), charge_lyts.cend(), std::numeric_limits::max(), - [](const double a, const auto& lyt) { return std::min(a, lyt.get_system_energy()); }); + if (first != last) + { + return minimum_energy_distribution(first, last)->get_system_energy(); + } + + return std::numeric_limits::infinity(); +} +/** + * Returns an iterator to the charge distribution of minimum energy contained in a range of + * `charge_distribution_surface` objects. If the range is empty, `last` is returned. + * + * @tparam InputIt Must meet the requirements of `LegacyInputIterator`. + * @param first Begin of the range to examime. + * @param last End of the range to examine. + * @return Iterator to the minimum energy charge distribution found in the input range, or `last` if the range is empty. + */ +template +[[nodiscard]] InputIt minimum_energy_distribution(const InputIt first, const InputIt last) noexcept +{ + static_assert(std::is_base_of_v::iterator_category>, + "InputIt must meet the requirements of LegacyInputIterator"); + static_assert(is_charge_distribution_surface_v::value_type>, + "Range must be of charge_distribution_surface objects"); + + return std::min_element(first, last, + [](const auto& cds1, const auto& cds2) + { return cds1.get_system_energy() < cds2.get_system_energy(); }); } } // namespace fiction diff --git a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp index ee6726dcd..b38d43c79 100644 --- a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp +++ b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp @@ -16,12 +16,12 @@ #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/layouts/cell_level_layout.hpp" +#include "fiction/technology/cell_technologies.hpp" #include "fiction/traits.hpp" #include "fiction/utils/execution_utils.hpp" #include "fiction/utils/hash.hpp" #include "fiction/utils/phmap_utils.hpp" -#include #include #include #include @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/fiction/algorithms/simulation/sidb/quickexact.hpp b/include/fiction/algorithms/simulation/sidb/quickexact.hpp index 138a0e326..a1cb738f8 100644 --- a/include/fiction/algorithms/simulation/sidb/quickexact.hpp +++ b/include/fiction/algorithms/simulation/sidb/quickexact.hpp @@ -6,15 +6,14 @@ #define FICTION_QUICKEXACT_HPP #include "fiction/algorithms/iter/gray_code_iterator.hpp" -#include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/technology/sidb_charge_state.hpp" +#include "fiction/technology/sidb_defects.hpp" #include "fiction/traits.hpp" -#include #include #include @@ -98,6 +97,7 @@ class quickexact_impl { result.algorithm_name = "QuickExact"; result.physical_parameters = params.physical_parameters; + result.additional_simulation_parameters.emplace("global_potential", params.global_potential); mockturtle::stopwatch<>::duration time_counter{}; { @@ -304,13 +304,13 @@ class quickexact_impl if (base_number == required_simulation_base_number::TWO) { - result.additional_simulation_parameters.emplace_back("base_number", uint64_t{2}); + result.additional_simulation_parameters.emplace("base_number", uint64_t{2}); two_state_simulation(charge_layout); } // If positively charged SiDBs can occur in the layout, 3-state simulation is conducted. else { - result.additional_simulation_parameters.emplace_back("base_number", uint64_t{3}); + result.additional_simulation_parameters.emplace("base_number", uint64_t{3}); three_state_simulation(charge_layout); } } diff --git a/include/fiction/algorithms/simulation/sidb/quicksim.hpp b/include/fiction/algorithms/simulation/sidb/quicksim.hpp index 771444da7..41b886ec2 100644 --- a/include/fiction/algorithms/simulation/sidb/quicksim.hpp +++ b/include/fiction/algorithms/simulation/sidb/quicksim.hpp @@ -5,20 +5,17 @@ #ifndef FICTION_QUICKSIM_HPP #define FICTION_QUICKSIM_HPP -#include "fiction/algorithms/simulation/sidb/energy_distribution.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" +#include "fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp" #include "fiction/technology/charge_distribution_surface.hpp" +#include "fiction/technology/sidb_charge_state.hpp" #include "fiction/traits.hpp" -#include #include #include #include #include -#include -#include #include #include #include @@ -57,7 +54,7 @@ struct quicksim_params * charge distribution layout. Depending on the simulation parameters, the ground state is found with a certain * probability after one run. * - * @tparam Lyt Cell-level layout type. + * @tparam Lyt SiDB cell-level layout type. * @param lyt The layout to simulate. * @param ps Physical parameters. They are material-specific and may vary from experiment to experiment. * @return sidb_simulation_result is returned with all results. @@ -75,8 +72,8 @@ sidb_simulation_result quicksim(const Lyt& lyt, const quicksim_params& ps = sidb_simulation_result st{}; st.algorithm_name = "QuickSim"; - st.additional_simulation_parameters.emplace_back("iteration_steps", ps.interation_steps); - st.additional_simulation_parameters.emplace_back("alpha", ps.alpha); + st.additional_simulation_parameters.emplace("iteration_steps", ps.interation_steps); + st.additional_simulation_parameters.emplace("alpha", ps.alpha); st.physical_parameters = ps.phys_params; st.charge_distributions.reserve(ps.interation_steps); diff --git a/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp b/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp index c90ed5c7f..792c73b7b 100644 --- a/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp +++ b/include/fiction/algorithms/simulation/sidb/sidb_simulation_result.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include namespace fiction @@ -57,9 +57,9 @@ struct sidb_simulation_result * Additional named simulation parameters. This is used to store algorithm-dependent parameters that are not part of * the `sidb_simulation_parameters` struct. * - * The first element of the pair is the name of the parameter, the second element is the value of the parameter. + * The key of the map is the name of the parameter, the element is the value of the parameter. */ - std::vector> additional_simulation_parameters{}; + std::unordered_map additional_simulation_parameters{}; }; } // namespace fiction diff --git a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp index 3cefa64d4..ac87768ad 100644 --- a/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp +++ b/include/fiction/algorithms/simulation/sidb/time_to_solution.hpp @@ -7,7 +7,6 @@ #include "fiction/algorithms/simulation/sidb/exhaustive_ground_state_simulation.hpp" #include "fiction/algorithms/simulation/sidb/is_ground_state.hpp" -#include "fiction/algorithms/simulation/sidb/minimum_energy.hpp" #include "fiction/algorithms/simulation/sidb/quickexact.hpp" #include "fiction/algorithms/simulation/sidb/quicksim.hpp" #include "fiction/algorithms/simulation/sidb/sidb_simulation_engine.hpp" diff --git a/include/fiction/io/print_layout.hpp b/include/fiction/io/print_layout.hpp index 7e9dfffb9..517323ac1 100644 --- a/include/fiction/io/print_layout.hpp +++ b/include/fiction/io/print_layout.hpp @@ -533,7 +533,14 @@ void print_layout(const Lyt& lyt, std::ostream& os = std::cout) { if constexpr (has_sidb_technology_v) { - print_sidb_layout(os, lyt); + if constexpr (has_siqad_coord_v) + { + print_sidb_layout(os, lyt); + } + else + { + print_sidb_layout(os, convert_to_siqad_coordinates(lyt)); + } } else { diff --git a/include/fiction/io/write_location_and_ground_state.hpp b/include/fiction/io/write_location_and_ground_state.hpp index ed6a1a2f0..6af35875a 100644 --- a/include/fiction/io/write_location_and_ground_state.hpp +++ b/include/fiction/io/write_location_and_ground_state.hpp @@ -39,8 +39,8 @@ class write_location_and_ground_state_impl void run() { // this part searches for the ground state(s) among all physically valid charge distributions - const auto min_energy = - round_to_n_decimal_places(minimum_energy(sim_result.charge_distributions), 6); + const auto min_energy = round_to_n_decimal_places( + minimum_energy(sim_result.charge_distributions.cbegin(), sim_result.charge_distributions.cend()), 6); std::vector> ground_state_layouts{}; for (const auto& valid_layout : sim_result.charge_distributions) diff --git a/include/fiction/io/write_operational_domain.hpp b/include/fiction/io/write_operational_domain.hpp index 2c9244204..8cd2f07ae 100644 --- a/include/fiction/io/write_operational_domain.hpp +++ b/include/fiction/io/write_operational_domain.hpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace fiction @@ -40,7 +41,8 @@ namespace detail * @param param The sweep parameter to be converted. * @return The string representation of the sweep parameter. */ -static inline std::string sweep_parameter_to_string(const operational_domain::sweep_parameter& param) noexcept +[[nodiscard]] static inline std::string +sweep_parameter_to_string(const operational_domain::sweep_parameter& param) noexcept { switch (param) { diff --git a/include/fiction/layouts/cell_level_layout.hpp b/include/fiction/layouts/cell_level_layout.hpp index aea701153..0fb1b99a5 100644 --- a/include/fiction/layouts/cell_level_layout.hpp +++ b/include/fiction/layouts/cell_level_layout.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -418,7 +419,7 @@ class cell_level_layout : public ClockedLayout using iterator_type = decltype(strg->cell_type_map.cbegin()); mockturtle::detail::foreach_element_transform( strg->cell_type_map.cbegin(), strg->cell_type_map.cend(), - [](const auto& ct) { return static_cast(ct.first); }, fn); + [](const auto& ct) { return static_cast(ct.first); }, std::forward(fn)); } /** * Applies a function to all cell positions in the layout, even empty ones. This function, thereby, renames @@ -445,7 +446,8 @@ class cell_level_layout : public ClockedLayout { using iterator_type = decltype(strg->inputs.cbegin()); mockturtle::detail::foreach_element_transform( - strg->inputs.cbegin(), strg->inputs.cend(), [](const auto& i) { return static_cast(i); }, fn); + strg->inputs.cbegin(), strg->inputs.cend(), [](const auto& i) { return static_cast(i); }, + std::forward(fn)); } /** * Applies a function to all primary output cells in the layout. @@ -459,7 +461,8 @@ class cell_level_layout : public ClockedLayout { using iterator_type = decltype(strg->outputs.cbegin()); mockturtle::detail::foreach_element_transform( - strg->outputs.cbegin(), strg->outputs.end(), [](const auto& o) { return static_cast(o); }, fn); + strg->outputs.cbegin(), strg->outputs.end(), [](const auto& o) { return static_cast(o); }, + std::forward(fn)); } #pragma endregion diff --git a/include/fiction/technology/charge_distribution_surface.hpp b/include/fiction/technology/charge_distribution_surface.hpp index 2035f0088..eaf86c938 100644 --- a/include/fiction/technology/charge_distribution_surface.hpp +++ b/include/fiction/technology/charge_distribution_surface.hpp @@ -14,7 +14,6 @@ #include "fiction/technology/sidb_defects.hpp" #include "fiction/technology/sidb_nm_position.hpp" #include "fiction/traits.hpp" -#include "fiction/types.hpp" #include #include @@ -24,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +129,7 @@ class charge_distribution_surface : public Lyt */ using distance_matrix = std::vector>; /** - * The potential matrix is a vector of vectors storing the chargless electrostatic potentials in Volt (V). + * The potential matrix is a vector of vectors storing the charge-less electrostatic potentials in Volt (V). */ using potential_matrix = std::vector>; /** @@ -287,11 +287,11 @@ class charge_distribution_surface : public Lyt /** * Copy constructor. * - * @param lyt charge_distribution_surface + * @param cds Other `charge_distribution_surface`. */ - explicit charge_distribution_surface(const charge_distribution_surface& lyt) : - Lyt(lyt), - strg{std::make_shared(*lyt.strg)} + charge_distribution_surface(const charge_distribution_surface& cds) : + Lyt(cds), + strg{std::make_shared(*cds.strg)} {} /** * Copy assignment operator. diff --git a/include/fiction/types.hpp b/include/fiction/types.hpp index c1e5415fe..5f5cd7966 100644 --- a/include/fiction/types.hpp +++ b/include/fiction/types.hpp @@ -15,6 +15,7 @@ #include "fiction/layouts/tile_based_layout.hpp" #include "fiction/networks/technology_network.hpp" #include "fiction/technology/cell_technologies.hpp" +#include "fiction/technology/charge_distribution_surface.hpp" #include "fiction/technology/sidb_surface.hpp" #include @@ -134,6 +135,16 @@ inline constexpr const char* tech_impl_name = std::is_same_v, std::is_same_v, sidb_technology> ? sidb_name : "?"; +constexpr const char* qca_cell_name = "cells"; +constexpr const char* inml_cell_name = "magnets"; +constexpr const char* sidb_cell_name = "dots"; + +template +inline constexpr const char* tech_cell_name = std::is_same_v, qca_technology> ? qca_cell_name : + std::is_same_v, inml_technology> ? inml_cell_name : + std::is_same_v, sidb_technology> ? sidb_cell_name : + "?"; + /** * FCN cell-level layouts. */ @@ -152,14 +163,18 @@ using inml_cell_clk_lyt_ptr = std::shared_ptr; using sidb_cell_clk_lyt = cell_level_layout>>; using sidb_cell_clk_lyt_ptr = std::shared_ptr; +using cds_sidb_cell_clk_lyt = + charge_distribution_surface>>>; +using cds_sidb_cell_clk_lyt_ptr = std::shared_ptr; + using sidb_cell_clk_lyt_siqad = cell_level_layout>>; using sidb_cell_clk_lyt_siqad_ptr = std::shared_ptr; using sidb_defect_cell_clk_lyt_siqad = sidb_surface; using sidb_defect_cell_clk_lyt_siqad_ptr = std::shared_ptr; -using cell_layout_t = - std::variant; +using cell_layout_t = std::variant; } // namespace fiction diff --git a/include/fiction/utils/layout_utils.hpp b/include/fiction/utils/layout_utils.hpp index 5c371908d..4a631528b 100644 --- a/include/fiction/utils/layout_utils.hpp +++ b/include/fiction/utils/layout_utils.hpp @@ -287,11 +287,12 @@ Lyt normalize_layout_coordinates(const Lyt& lyt) noexcept * coordinates is returned. * * @tparam Lyt Cell-level layout type based on fiction coordinates, e.g., `offset::ucoord_t` or `cube::coord_t`. + * @tparam TargetLyt Cell-level layout type based on SiQAD coordinates, i.e., `siqad::coord_t`. * @param lyt The layout that is to be converted to a new layout based on SiQAD coordinates. * @return A new equivalent layout based on SiQAD coordinates. */ template -sidb_cell_clk_lyt_siqad convert_to_siqad_coordinates(const Lyt& lyt) noexcept +auto convert_to_siqad_coordinates(const Lyt& lyt) noexcept { static_assert(is_cartesian_layout_v, "Lyt is not a Cartesian layout"); static_assert(is_cell_level_layout_v, "Lyt is not a cell-level layout"); @@ -310,7 +311,22 @@ sidb_cell_clk_lyt_siqad convert_to_siqad_coordinates(const Lyt& lyt) noexcept lyt_new.assign_cell_name(siqad::to_siqad_coord>(c), lyt.get_cell_name(c)); }); - return lyt_new; + if constexpr (is_charge_distribution_surface_v) + { + charge_distribution_surface lyt_new_cds{lyt_new}; + + lyt.foreach_cell( + [&lyt_new_cds, &lyt](const auto& c) + { lyt_new_cds.assign_charge_state(siqad::to_siqad_coord>(c), lyt.get_charge_state(c), false); }); + + lyt_new_cds.assign_physical_parameters(lyt.get_phys_params()); + + return lyt_new_cds; + } + else + { + return lyt_new; + } } /** diff --git a/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp b/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp index 1fd7822d1..9fe28d9aa 100644 --- a/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp +++ b/test/algorithms/simulation/sidb/can_positive_charges_occur.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include using namespace fiction; diff --git a/test/algorithms/simulation/sidb/critical_temperature.cpp b/test/algorithms/simulation/sidb/critical_temperature.cpp index bfa509d31..6ef56d35f 100644 --- a/test/algorithms/simulation/sidb/critical_temperature.cpp +++ b/test/algorithms/simulation/sidb/critical_temperature.cpp @@ -6,17 +6,19 @@ #include #include -#include -#include +#include #include #include #include -#include +#include #include +#include #include +#include #include #include +#include using namespace fiction; @@ -25,40 +27,50 @@ TEMPLATE_TEST_CASE( (cell_level_layout>>), (charge_distribution_surface>>>)) { + TestType lyt{}; + + critical_temperature_params params{}; + sidb_simulation_parameters physical_params{2, -0.32, 5.6, 5.0}; + + critical_temperature_stats critical_stats{}; + SECTION("No physically valid charge distribution could be found") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({6, 1, 0}, sidb_technology::cell_type::OUTPUT); lyt.assign_cell_type({8, 1, 0}, sidb_technology::cell_type::OUTPUT); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 0, 0.0}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); - CHECK(criticalstats.num_valid_lyt == 0); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 0; + params.alpha = 0.0; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 0); + CHECK(critical_stats.critical_temperature == 0.0); } - SECTION("One SiDB") + SECTION("No SiDB") { - TestType lyt{}; - - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{tt{}}, params, &criticalstats); - CHECK(criticalstats.num_valid_lyt == 0); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{tt{}}, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 0); + CHECK(critical_stats.critical_temperature == 0.0); } - SECTION("Not working diagonal Wire where positively charged SiDBs can occur") + SECTION("Not working diagonal wire where positively charged SiDBs can occur") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -80,37 +92,43 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); - CHECK(criticalstats.critical_temperature == 0.0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); + + CHECK(critical_stats.critical_temperature == 0.0); } SECTION("four SiDBs with two valid charge distributions, QuickExact") { - TestType lyt{}; lyt.assign_cell_type({0, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({4, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({2, 2}, TestType::cell_type::NORMAL); - critical_temperature_stats criticalstats_non_gate_based{}; - const critical_temperature_params params_non_gate_based{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, - 0.99, 350}; - critical_temperature_non_gate_based(lyt, params_non_gate_based, &criticalstats_non_gate_based); - CHECK(criticalstats_non_gate_based.num_valid_lyt == 2); - CHECK_THAT(std::abs(criticalstats_non_gate_based.energy_between_ground_state_and_first_erroneous), + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_non_gate_based(lyt, params, &critical_stats); + + CHECK(critical_stats.num_valid_lyt == 2); + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.01)); - CHECK(criticalstats_non_gate_based.critical_temperature == 350); + CHECK(critical_stats.critical_temperature == 350); } SECTION("Y-shape SiDB AND gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 1}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 1}, sidb_technology::cell_type::INPUT); @@ -128,20 +146,24 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({10, 9, 1}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.28}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); + physical_params.mu_minus = -0.28; + + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.01)); - CHECK(criticalstats.critical_temperature == 350); + CHECK(critical_stats.critical_temperature == 350); } SECTION("Bestagon AND gate, QuickExact") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -171,19 +193,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(26.02, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 59.19), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 59.19), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("Bestagon AND gate, QuickSim") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -213,18 +238,20 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 500, 0.6}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 350}; - critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &criticalstats); - CHECK(criticalstats.critical_temperature > 0); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 500; + params.alpha = 0.6; + + critical_temperature_gate_based(lyt, std::vector{create_and_tt()}, params, &critical_stats); + + CHECK(critical_stats.critical_temperature > 0); } SECTION("Bestagon FO2 gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -253,20 +280,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({2, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_fan_out_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous - 0.56), + critical_temperature_gate_based(lyt, std::vector{create_fan_out_tt()}, params, &critical_stats); + + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous - 0.56), Catch::Matchers::WithinAbs(0.00, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 1.46), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 1.46), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("Bestagon CX gate") { - TestType lyt{}; - lyt.assign_cell_type({36, 1, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -307,20 +336,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({2, 19, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_crossing_wire_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; - CHECK_THAT(std::fabs(criticalstats.energy_between_ground_state_and_first_erroneous - 0.32), + critical_temperature_gate_based(lyt, std::vector{create_crossing_wire_tt()}, params, &critical_stats); + + CHECK_THAT(std::fabs(critical_stats.energy_between_ground_state_and_first_erroneous - 0.32), Catch::Matchers::WithinAbs(0.00, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature - 0.85), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature - 0.85), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("OR gate") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({26, 0, 0}, sidb_technology::cell_type::INPUT); @@ -343,18 +374,22 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({24, 15, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.25}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_or_tt()}, params, &criticalstats); + physical_params.mu_minus = -0.25; + + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_or_tt()}, params, &critical_stats); - CHECK(criticalstats.critical_temperature < 350); + CHECK(critical_stats.critical_temperature < 350); } SECTION("Not working diagonal Wire") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::INPUT); lyt.assign_cell_type({2, 1, 0}, sidb_technology::cell_type::INPUT); @@ -374,22 +409,24 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({36, 19, 0}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}}, - critical_temperature_params::simulation_engine::EXACT, 0.99, 350}; - critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::EXACT; + params.confidence_level = 0.99; + params.max_temperature = 350; + params.iteration_steps = 80; + params.alpha = 0.7; + + critical_temperature_gate_based(lyt, std::vector{create_id_tt()}, params, &critical_stats); - CHECK(criticalstats.algorithm_name == "QuickExact"); + CHECK(critical_stats.algorithm_name == "QuickExact"); - CHECK_THAT(std::abs(criticalstats.energy_between_ground_state_and_first_erroneous), + CHECK_THAT(std::abs(critical_stats.energy_between_ground_state_and_first_erroneous), Catch::Matchers::WithinAbs(305.95, 0.01)); - CHECK_THAT(std::abs(criticalstats.critical_temperature), Catch::Matchers::WithinAbs(0.00, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature), Catch::Matchers::WithinAbs(0.00, 0.01)); } SECTION("nine SiDBs, QuickSim, non-gate-based") { - TestType lyt{}; - lyt.assign_cell_type({0, 0, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({3, 0, 0}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({6, 0, 0}, sidb_technology::cell_type::NORMAL); @@ -401,14 +438,17 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({9, 1, 1}, sidb_technology::cell_type::NORMAL); lyt.assign_cell_type({12, 1, 1}, sidb_technology::cell_type::NORMAL); - critical_temperature_stats criticalstats{}; - const critical_temperature_params params{quicksim_params{sidb_simulation_parameters{2, -0.32}, 500, 0.6}, - critical_temperature_params::simulation_engine::APPROXIMATE, 0.99, - 750}; - critical_temperature_non_gate_based(lyt, params, &criticalstats); + params.physical_parameters = physical_params; + params.engine = critical_temperature_params::simulation_engine::APPROXIMATE; + params.confidence_level = 0.99; + params.max_temperature = 750; + params.iteration_steps = 500; + params.alpha = 0.6; + + critical_temperature_non_gate_based(lyt, params, &critical_stats); - CHECK(criticalstats.algorithm_name == "QuickSim"); + CHECK(critical_stats.algorithm_name == "QuickSim"); - CHECK_THAT(std::abs(criticalstats.critical_temperature), Catch::Matchers::WithinAbs(11.55, 0.01)); + CHECK_THAT(std::abs(critical_stats.critical_temperature), Catch::Matchers::WithinAbs(11.55, 0.01)); } } diff --git a/test/algorithms/simulation/sidb/minimum_energy.cpp b/test/algorithms/simulation/sidb/minimum_energy.cpp index 937055ecc..904c94f52 100644 --- a/test/algorithms/simulation/sidb/minimum_energy.cpp +++ b/test/algorithms/simulation/sidb/minimum_energy.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include @@ -32,11 +32,12 @@ TEMPLATE_TEST_CASE( const charge_distribution_surface charge_layout{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.begin(), all_lyts.end()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); all_lyts.push_back(charge_layout); - CHECK(std::abs(minimum_energy(all_lyts) - 0) < 0.00000001); + CHECK(std::abs(minimum_energy(all_lyts.begin(), all_lyts.end()) - 0) < 0.00000001); } SECTION("layout with one SiDB placed") @@ -46,11 +47,12 @@ TEMPLATE_TEST_CASE( const charge_distribution_surface charge_layout{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); all_lyts.push_back(charge_layout); - CHECK(std::abs(minimum_energy(all_lyts) - 0) < 0.00000001); + CHECK(std::abs(minimum_energy(all_lyts.cbegin(), all_lyts.cend()) - 0) < 0.00000001); } SECTION("layout with three SiDBs placed") @@ -62,7 +64,8 @@ TEMPLATE_TEST_CASE( charge_distribution_surface charge_layout_first{lyt}; std::vector> all_lyts{}; - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(std::numeric_limits::max(), 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), + Catch::Matchers::WithinAbs(std::numeric_limits::infinity(), 0.00001)); charge_layout_first.assign_charge_state({0, 0}, sidb_charge_state::NEUTRAL); @@ -79,6 +82,6 @@ TEMPLATE_TEST_CASE( charge_layout_second.recompute_system_energy(); all_lyts.push_back(charge_layout_second); - CHECK_THAT(minimum_energy(all_lyts), Catch::Matchers::WithinAbs(0.0, 0.00001)); + CHECK_THAT(minimum_energy(all_lyts.cbegin(), all_lyts.cend()), Catch::Matchers::WithinAbs(0.0, 0.00001)); } } diff --git a/test/algorithms/simulation/sidb/quickexact.cpp b/test/algorithms/simulation/sidb/quickexact.cpp index 77517b5fa..9b5f3f9d7 100644 --- a/test/algorithms/simulation/sidb/quickexact.cpp +++ b/test/algorithms/simulation/sidb/quickexact.cpp @@ -1,5 +1,6 @@ // // Created by Jan Drewniok on 18.12.22. +// #include #include @@ -28,9 +29,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); CHECK(simulation_results.charge_distributions.empty()); - CHECK(simulation_results.additional_simulation_parameters.empty()); CHECK(simulation_results.algorithm_name == "QuickExact"); - CHECK(simulation_results.additional_simulation_parameters.empty()); } TEMPLATE_TEST_CASE( @@ -252,6 +251,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); REQUIRE(simulation_results.charge_distributions.size() == 1); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("global_potential")) == -0.26); CHECK(simulation_results.charge_distributions.front().get_charge_state_by_index(0) == sidb_charge_state::NEUTRAL); } @@ -1150,8 +1150,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); REQUIRE(!simulation_results.additional_simulation_parameters.empty()); - CHECK(simulation_results.additional_simulation_parameters[0].first == "base_number"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[0].second) == 3); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("base_number")) == 3); const quickexact_params params_new{sidb_simulation_parameters{2, -0.32}, quickexact_params::automatic_base_number_detection::OFF}; @@ -1159,8 +1158,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results_new = quickexact(lyt, params_new); REQUIRE(!simulation_results_new.additional_simulation_parameters.empty()); - CHECK(simulation_results_new.additional_simulation_parameters[0].first == "base_number"); - CHECK(std::any_cast(simulation_results_new.additional_simulation_parameters[0].second) == 2); + CHECK(std::any_cast(simulation_results_new.additional_simulation_parameters.at("base_number")) == 2); } TEMPLATE_TEST_CASE( diff --git a/test/algorithms/simulation/sidb/quicksim.cpp b/test/algorithms/simulation/sidb/quicksim.cpp index b8de465b7..6da36cd06 100644 --- a/test/algorithms/simulation/sidb/quicksim.cpp +++ b/test/algorithms/simulation/sidb/quicksim.cpp @@ -5,12 +5,19 @@ #include #include +#include #include +#include #include #include #include +#include #include +#include #include +#include + +#include using namespace fiction; @@ -46,10 +53,8 @@ TEMPLATE_TEST_CASE( CHECK(simulation_results.charge_distributions.empty()); REQUIRE(!simulation_results.additional_simulation_parameters.empty()); CHECK(simulation_results.algorithm_name == "QuickSim"); - CHECK(simulation_results.additional_simulation_parameters[0].first == "iteration_steps"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[0].second) == 80); - CHECK(simulation_results.additional_simulation_parameters[1].first == "alpha"); - CHECK(std::any_cast(simulation_results.additional_simulation_parameters[1].second) == 0.7); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("iteration_steps")) == 80); + CHECK(std::any_cast(simulation_results.additional_simulation_parameters.at("alpha")) == 0.7); CHECK(simulation_results.charge_distributions.empty()); } @@ -373,8 +378,8 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({10, 8, 1}, TestType::cell_type::NORMAL); lyt.assign_cell_type({16, 1, 0}, TestType::cell_type::NORMAL); - sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.28}; + const sidb_simulation_result quicksim_stats{}; + const sidb_simulation_parameters params{2, -0.28}; quicksim_params quicksim_params{params}; @@ -459,20 +464,19 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({6, 2, 0}, TestType::cell_type::NORMAL); lyt.assign_cell_type({8, 2, 0}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.25}; + const sidb_simulation_parameters params{2, -0.25}; quicksim_params quicksim_params{params}; REQUIRE(quicksim_params.phys_params.mu_minus == -0.25); - const auto check_charge_configuration = [](const sidb_simulation_result& stats) noexcept + const auto check_charge_configuration = [](const sidb_simulation_result& result) noexcept { - REQUIRE(!stats.charge_distributions.empty()); + REQUIRE(!result.charge_distributions.empty()); - REQUIRE(!energy_distribution(stats.charge_distributions).empty()); + REQUIRE(!energy_distribution(result.charge_distributions).empty()); - const auto& charge_lyt_first = stats.charge_distributions.front(); + const auto& charge_lyt_first = result.charge_distributions.front(); CHECK((((charge_lyt_first.get_charge_state({6, 2, 0}) == sidb_charge_state::NEGATIVE) && (charge_lyt_first.get_charge_state({8, 2, 0}) == sidb_charge_state::NEUTRAL)) || @@ -559,8 +563,7 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({8, 10, 1}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; - const sidb_simulation_parameters params{2, -0.32}; + const sidb_simulation_parameters params{2, -0.32}; quicksim_params quicksim_params{params}; @@ -668,7 +671,7 @@ TEMPLATE_TEST_CASE( lyt.assign_cell_type({30, 15, 0}, TestType::cell_type::NORMAL); - const sidb_simulation_result quicksimstats{}; + const sidb_simulation_result quicksim_stats{}; const sidb_simulation_parameters params{2, -0.32}; quicksim_params quicksim_params{params}; diff --git a/test/io/write_sqd_sim_result.cpp b/test/io/write_sqd_sim_result.cpp index f05a5b4cd..629a65b85 100644 --- a/test/io/write_sqd_sim_result.cpp +++ b/test/io/write_sqd_sim_result.cpp @@ -191,7 +191,7 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") CHECK(simulation_stream.str() == sim_result_str); } - SECTION("with additional parameters") + SECTION("with additional parameter (string)") { const std::string sim_result_str = fmt::format( "\n" @@ -209,9 +209,41 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") " {}\n" " {}\n" " value1\n" - " 2\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + FICTION_VERSION, FICTION_REPO, fmt::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(std::time(nullptr))), + sim_result.physical_parameters.lambda_tf, sim_result.physical_parameters.epsilon_r, + sim_result.physical_parameters.mu_minus); + + sim_result.additional_simulation_parameters.emplace("param1", "value1"); + + write_sqd_sim_result(sim_result, simulation_stream); + + CHECK(simulation_stream.str() == sim_result_str); + } + + SECTION("with additional parameters (double)") + { + const std::string sim_result_str = fmt::format( + "\n" + "\n" + " \n" + " TestSim\n" + " {}\n" + " {}\n" + " 0\n" + " {}\n" + " 42.0\n" + " \n" + " \n" + " {}\n" + " {}\n" + " {}\n" " 3.140000\n" - " c\n" " \n" " \n" " \n" @@ -222,10 +254,7 @@ TEST_CASE("Write empty simulation result", "[sqd-sim-result]") sim_result.physical_parameters.lambda_tf, sim_result.physical_parameters.epsilon_r, sim_result.physical_parameters.mu_minus); - sim_result.additional_simulation_parameters.emplace_back("param1", "value1"); - sim_result.additional_simulation_parameters.emplace_back("param2", 2); - sim_result.additional_simulation_parameters.emplace_back("param3", 3.14); - sim_result.additional_simulation_parameters.emplace_back("param4", 'c'); + sim_result.additional_simulation_parameters.emplace("param3", 3.14); write_sqd_sim_result(sim_result, simulation_stream); From a6001ac97d6f8eec4585896eeb51cf563eadd009 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 17 Dec 2023 21:52:29 +0100 Subject: [PATCH 14/24] :memo: Added an SiDB simulation figure to the README (#359) --- README.md | 2 ++ docs/_static/sidb_simulation.png | Bin 0 -> 48063 bytes 2 files changed, 2 insertions(+) create mode 100644 docs/_static/sidb_simulation.png diff --git a/README.md b/README.md index 2e54fc6d8..78495b479 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,8 @@ using ### Physical Simulation +SiDB simulation result + When a layout is compiled to the cell level via the application of a technology-dependent gate library, it can be simulated using a physical model. Currently, the following simulation algorithms are implemented in *fiction*: diff --git a/docs/_static/sidb_simulation.png b/docs/_static/sidb_simulation.png new file mode 100644 index 0000000000000000000000000000000000000000..7ed121afadbdcd74c5e0ed26c794bd17b8b675d4 GIT binary patch literal 48063 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 KPZ$2~bo*bNh;~;1 literal 0 HcmV?d00001 From 6d48b737eb51dbb0ec1a761380fd2c9dcdf86f47 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 17 Dec 2023 21:52:59 +0100 Subject: [PATCH 15/24] :sparkles: Added 'Ripple' to the list of supported clocking schemes (#358) --- README.md | 29 +-- cli/cmd/physical_design/exact.hpp | 2 +- docs/_static/ripple.png | Bin 0 -> 45582 bytes docs/_static/ripple.svg | 198 ++++++++++++++++++++ docs/layouts/clocking_scheme.rst | 8 + include/fiction/layouts/clocking_scheme.hpp | 30 +++ test/layouts/clocking_scheme.cpp | 83 ++++++++ 7 files changed, 336 insertions(+), 14 deletions(-) create mode 100644 docs/_static/ripple.png create mode 100644 docs/_static/ripple.svg diff --git a/README.md b/README.md index 78495b479..a6e7b0fbe 100644 --- a/README.md +++ b/README.md @@ -202,13 +202,12 @@ When a layout is compiled to the cell level via the application of a technology- simulated using a physical model. Currently, the following simulation algorithms are implemented in *fiction*: - Silicon Dangling Bonds (SiDBs) - - Electrostatic Ground State Simulation - - [*QuickExact*](https://arxiv.org/abs/2308.04487) - - [*QuickSim*](https://ieeexplore.ieee.org/document/10231266) - - [Exhaustive *(ExGS)*](https://open.library.ubc.ca/soa/cIRcle/collections/ubctheses/24/items/1.0392909) - - [Critical Temperature Simulation](https://ieeexplore.ieee.org/document/10231259) - - [Operational Domain Computation](https://www.cda.cit.tum.de/files/eda/2023_nanoarch_reducing_the_complexity_of_operational_domain_computation_in_silicon_dangling_bond_logic.pdf) - + - Electrostatic Ground State Simulation + - [*QuickExact*](https://arxiv.org/abs/2308.04487) + - [*QuickSim*](https://ieeexplore.ieee.org/document/10231266) + - [Exhaustive *(ExGS)*](https://open.library.ubc.ca/soa/cIRcle/collections/ubctheses/24/items/1.0392909) + - [Critical Temperature Simulation](https://ieeexplore.ieee.org/document/10231259) + - [Operational Domain Computation](https://www.cda.cit.tum.de/files/eda/2023_nanoarch_reducing_the_complexity_of_operational_domain_computation_in_silicon_dangling_bond_logic.pdf) ## Clocking Schemes @@ -228,9 +227,9 @@ Built-in schemes are |:--------------------------------------------------------:|:------------------------------------------------------------------------:|:---------------------------------------------------------------------------:| | USE | RES | ESR | -| [CFE](https://ietresearch.onlinelibrary.wiley.com/doi/10.1049/iet-cds.2019.0096) | [BANCS](https://ieeexplore.ieee.org/document/8533251) | -|:--------------------------------------------------------------------------------:|:------------------------------------------------------------:| -| CFE | BANCS | +| [CFE](https://ietresearch.onlinelibrary.wiley.com/doi/10.1049/iet-cds.2019.0096) | [Ripple](https://scholarworks.rit.edu/cgi/viewcontent.cgi?referer=&httpsredir=1&article=8266&context=theses) | [BANCS](https://ieeexplore.ieee.org/document/8533251) | +|:--------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------:| +| CFE | Ripple | BANCS | plus the mentioned irregular open clocking that works via a clock map instead of a regular extrapolated cutout. @@ -300,11 +299,15 @@ Cell-level layouts: ## Benchmark Library -To objectively evaluate and compare software and design automation tools, [MNT Bench](https://www.cda.cit.tum.de/mntbench/) provides gate-level +To objectively evaluate and compare software and design automation +tools, [MNT Bench](https://www.cda.cit.tum.de/mntbench/) provides gate-level layouts for various gate libraries and clocking schemes, generated using the latest physical design and -optimization algorithms, with *fiction* offering the corresponding read and write utilities to generate gate-level layouts from gate-level layout files (``.fgl``) and vice versa. +optimization algorithms, with *fiction* offering the corresponding read and write utilities to generate gate-level +layouts from gate-level layout files (``.fgl``) and vice versa. -Additionally, the [benchmarks](https://github.com/cda-tum/fiction/tree/main/benchmarks) folder contains the function descriptions of frequently used benchmark sets in Verilog format (``.v``) provided by [MNT Bench](https://www.cda.cit.tum.de/mntbench/). +Additionally, the [benchmarks](https://github.com/cda-tum/fiction/tree/main/benchmarks) folder contains the function +descriptions of frequently used benchmark sets in Verilog format (``.v``) provided +by [MNT Bench](https://www.cda.cit.tum.de/mntbench/). # Reference diff --git a/cli/cmd/physical_design/exact.hpp b/cli/cmd/physical_design/exact.hpp index 571085400..0922156b5 100644 --- a/cli/cmd/physical_design/exact.hpp +++ b/cli/cmd/physical_design/exact.hpp @@ -38,7 +38,7 @@ class exact_command : public command { add_option("--clk_scheme,-s", clocking, "Clocking scheme to use {OPEN[3|4], COLUMNAR[3|4], ROW[3|4] 2DDWAVE[3|4], 2DDWAVEHEX[3|4], USE, " - "RES, ESR, CFE, BANCS}", + "RES, ESR, CFE, RIPPLE, BANCS}", true); add_option("--upper_x", ps.upper_bound_x, "Number of FCN gate tiles to use at maximum in x-direction"); add_option("--upper_y", ps.upper_bound_y, "Number of FCN gate tiles to use at maximum in y-direction"); diff --git a/docs/_static/ripple.png b/docs/_static/ripple.png new file mode 100644 index 0000000000000000000000000000000000000000..08192c9fdcf5b62ff6aa03050faf8feed1ca7fba GIT binary patch literal 45582 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 literal 0 HcmV?d00001 diff --git a/docs/_static/ripple.svg b/docs/_static/ripple.svg new file mode 100644 index 000000000..2aeda7324 --- /dev/null +++ b/docs/_static/ripple.svg @@ -0,0 +1,198 @@ + + + + diff --git a/docs/layouts/clocking_scheme.rst b/docs/layouts/clocking_scheme.rst index 54cea7abc..a019b6416 100644 --- a/docs/layouts/clocking_scheme.rst +++ b/docs/layouts/clocking_scheme.rst @@ -80,6 +80,14 @@ CFE .. doxygenfunction:: fiction::cfe_clocking +Ripple +###### + +.. figure:: /_static/ripple.png + :width: 200 + +.. doxygenfunction:: fiction::ripple_clocking + BANCS ##### diff --git a/include/fiction/layouts/clocking_scheme.hpp b/include/fiction/layouts/clocking_scheme.hpp index 71ab3b430..6763bbe86 100644 --- a/include/fiction/layouts/clocking_scheme.hpp +++ b/include/fiction/layouts/clocking_scheme.hpp @@ -166,6 +166,7 @@ inline constexpr const char* USE = "USE"; inline constexpr const char* RES = "RES"; inline constexpr const char* ESR = "ESR"; inline constexpr const char* CFE = "CFE"; +inline constexpr const char* RIPPLE = "RIPPLE"; inline constexpr const char* BANCS = "BANCS"; } // namespace clock_name @@ -644,6 +645,34 @@ static auto cfe_clocking() noexcept // clang-format on } +/** + * Returns the Ripple clocking as defined in \"Ripple Clock Schemes for Quantum-dot Cellular Automata Circuits\" by + * Prafull Purohit, Master Thesis, Rochester Institute of Technology, 2012. + * + * @tparam Lyt Clocked layout type. + * @return Ripple clocking scheme. + */ +template +static auto ripple_clocking() noexcept +{ + // clang-format off + + static const typename clocking_scheme>::clock_function ripple_clock_function = + [](const clock_zone& cz) noexcept + { + static constexpr std::array>::clock_number, 4u>, 4u> cutout{ + {{{0, 1, 2, 3}}, + {{3, 2, 1, 0}}, + {{0, 1, 2, 3}}, + {{3, 2, 1, 0}}}}; + + return cutout[cz.y % 4ul][cz.x % 4ul]; + }; + + return clocking_scheme{clock_name::RIPPLE, ripple_clock_function, std::min(Lyt::max_fanin_size, 3u), 3u, 4u, true}; + + // clang-format on +} /** * Returns the BANCS clocking as defined in \"BANCS: Bidirectional Alternating Nanomagnetic Clocking Scheme\" by * Ruan Evangelista Formigoni, Omar P. Vilela Neto, and Jose Augusto M. Nacif in SBCCI 2018. @@ -738,6 +767,7 @@ std::optional>> get_clocking_scheme(const std::s {clock_name::RES, res_clocking()}, {clock_name::ESR, esr_clocking()}, {clock_name::CFE, cfe_clocking()}, + {clock_name::RIPPLE, ripple_clocking()}, {clock_name::BANCS, bancs_clocking()}}; std::string upper_name = name.data(); diff --git a/test/layouts/clocking_scheme.cpp b/test/layouts/clocking_scheme.cpp index 03b20deb7..97437e2f1 100644 --- a/test/layouts/clocking_scheme.cpp +++ b/test/layouts/clocking_scheme.cpp @@ -1697,6 +1697,86 @@ TEST_CASE("4-phase CFE", "[clocking-scheme]") CHECK(cfe4({3 + 4, 3 + 4}) == 2); } +TEST_CASE("4-phase Ripple", "[clocking-scheme]") +{ + using clk_lyt = clocked_layout>; + + const auto ripple4 = ripple_clocking(); + + CHECK(ripple4.num_clocks == 4u); + CHECK(ripple4.max_in_degree == 3u); + CHECK(ripple4.max_out_degree == 3u); + CHECK(ripple4.is_regular()); + + CHECK(ripple4({0, 0}) == 0); + CHECK(ripple4({0, 1}) == 3); + CHECK(ripple4({0, 2}) == 0); + CHECK(ripple4({0, 3}) == 3); + CHECK(ripple4({1, 0}) == 1); + CHECK(ripple4({1, 1}) == 2); + CHECK(ripple4({1, 2}) == 1); + CHECK(ripple4({1, 3}) == 2); + CHECK(ripple4({2, 0}) == 2); + CHECK(ripple4({2, 1}) == 1); + CHECK(ripple4({2, 2}) == 2); + CHECK(ripple4({2, 3}) == 1); + CHECK(ripple4({3, 0}) == 3); + CHECK(ripple4({3, 1}) == 0); + CHECK(ripple4({3, 2}) == 3); + CHECK(ripple4({3, 3}) == 0); + + CHECK(ripple4({0 + 4, 0}) == 0); + CHECK(ripple4({0 + 4, 1}) == 3); + CHECK(ripple4({0 + 4, 2}) == 0); + CHECK(ripple4({0 + 4, 3}) == 3); + CHECK(ripple4({1 + 4, 0}) == 1); + CHECK(ripple4({1 + 4, 1}) == 2); + CHECK(ripple4({1 + 4, 2}) == 1); + CHECK(ripple4({1 + 4, 3}) == 2); + CHECK(ripple4({2 + 4, 0}) == 2); + CHECK(ripple4({2 + 4, 1}) == 1); + CHECK(ripple4({2 + 4, 2}) == 2); + CHECK(ripple4({2 + 4, 3}) == 1); + CHECK(ripple4({3 + 4, 0}) == 3); + CHECK(ripple4({3 + 4, 1}) == 0); + CHECK(ripple4({3 + 4, 2}) == 3); + CHECK(ripple4({3 + 4, 3}) == 0); + + CHECK(ripple4({0, 0 + 4}) == 0); + CHECK(ripple4({0, 1 + 4}) == 3); + CHECK(ripple4({0, 2 + 4}) == 0); + CHECK(ripple4({0, 3 + 4}) == 3); + CHECK(ripple4({1, 0 + 4}) == 1); + CHECK(ripple4({1, 1 + 4}) == 2); + CHECK(ripple4({1, 2 + 4}) == 1); + CHECK(ripple4({1, 3 + 4}) == 2); + CHECK(ripple4({2, 0 + 4}) == 2); + CHECK(ripple4({2, 1 + 4}) == 1); + CHECK(ripple4({2, 2 + 4}) == 2); + CHECK(ripple4({2, 3 + 4}) == 1); + CHECK(ripple4({3, 0 + 4}) == 3); + CHECK(ripple4({3, 1 + 4}) == 0); + CHECK(ripple4({3, 2 + 4}) == 3); + CHECK(ripple4({3, 3 + 4}) == 0); + + CHECK(ripple4({0 + 4, 0 + 4}) == 0); + CHECK(ripple4({0 + 4, 1 + 4}) == 3); + CHECK(ripple4({0 + 4, 2 + 4}) == 0); + CHECK(ripple4({0 + 4, 3 + 4}) == 3); + CHECK(ripple4({1 + 4, 0 + 4}) == 1); + CHECK(ripple4({1 + 4, 1 + 4}) == 2); + CHECK(ripple4({1 + 4, 2 + 4}) == 1); + CHECK(ripple4({1 + 4, 3 + 4}) == 2); + CHECK(ripple4({2 + 4, 0 + 4}) == 2); + CHECK(ripple4({2 + 4, 1 + 4}) == 1); + CHECK(ripple4({2 + 4, 2 + 4}) == 2); + CHECK(ripple4({2 + 4, 3 + 4}) == 1); + CHECK(ripple4({3 + 4, 0 + 4}) == 3); + CHECK(ripple4({3 + 4, 1 + 4}) == 0); + CHECK(ripple4({3 + 4, 2 + 4}) == 3); + CHECK(ripple4({3 + 4, 3 + 4}) == 0); +} + TEST_CASE("3-phase BANCS", "[clocking-scheme]") { using clk_lyt = clocked_layout>; @@ -1928,6 +2008,7 @@ TEST_CASE("Clocking lookup", "[clocking-scheme]") check({"res", "RES", "rEs", "ReS"}, clock_name::RES); check({"esr", "ESR", "eSr", "EsR"}, clock_name::ESR); check({"cfe", "CFE", "cFe", "CfE"}, clock_name::CFE); + check({"ripple", "RIPPLE", "RiPpLe", "RIppLE"}, clock_name::RIPPLE); check({"bancs", "BANCS", "BaNCs", "banCS"}, clock_name::BANCS); CHECK(!get_clocking_scheme("").has_value()); @@ -1939,6 +2020,7 @@ TEST_CASE("Clocking lookup", "[clocking-scheme]") CHECK(!get_clocking_scheme("SER").has_value()); CHECK(!get_clocking_scheme("ERS").has_value()); CHECK(!get_clocking_scheme("CEF").has_value()); + CHECK(!get_clocking_scheme("RPIPLE").has_value()); CHECK(!get_clocking_scheme("BNCS").has_value()); } @@ -1956,5 +2038,6 @@ TEST_CASE("Linear schemes", "[clocking-scheme]") CHECK(!is_linear_scheme(*get_clocking_scheme(clock_name::RES))); CHECK(!is_linear_scheme(*get_clocking_scheme(clock_name::ESR))); CHECK(!is_linear_scheme(*get_clocking_scheme(clock_name::CFE))); + CHECK(!is_linear_scheme(*get_clocking_scheme(clock_name::RIPPLE))); CHECK(!is_linear_scheme(*get_clocking_scheme(clock_name::BANCS))); } From cd971f0d14ca086e16dbbcf0e28ac44a13401357 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Thu, 21 Dec 2023 13:32:05 +0100 Subject: [PATCH 16/24] :sparkles: Added an upper bound option for the total layout area to `exact` (#360) --- cli/cmd/physical_design/exact.hpp | 13 +++++--- .../algorithms/physical_design/exact.hpp | 26 +++++++++++---- test/algorithms/physical_design/exact.cpp | 33 +++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/cli/cmd/physical_design/exact.hpp b/cli/cmd/physical_design/exact.hpp index 0922156b5..d05e048a6 100644 --- a/cli/cmd/physical_design/exact.hpp +++ b/cli/cmd/physical_design/exact.hpp @@ -40,8 +40,9 @@ class exact_command : public command "Clocking scheme to use {OPEN[3|4], COLUMNAR[3|4], ROW[3|4] 2DDWAVE[3|4], 2DDWAVEHEX[3|4], USE, " "RES, ESR, CFE, RIPPLE, BANCS}", true); - add_option("--upper_x", ps.upper_bound_x, "Number of FCN gate tiles to use at maximum in x-direction"); - add_option("--upper_y", ps.upper_bound_y, "Number of FCN gate tiles to use at maximum in y-direction"); + add_option("--upper_area", ps.upper_bound_area, "Upper bound for the total number of tiles"); + add_option("--upper_x", ps.upper_bound_x, "Upper bound for the number of tiles in x-direction"); + add_option("--upper_y", ps.upper_bound_y, "Upper bound for the number of tiles in y-direction"); add_option("--fixed_size,-f", ps.fixed_size, "Execute only one iteration with the given number of upper bound tiles"); add_option("--timeout,-t", ps.timeout, "Timeout in seconds"); @@ -192,6 +193,7 @@ class exact_command : public command { fiction::exact_physical_design_params ps_dest{}; + ps_dest.upper_bound_area = ps_src.upper_bound_area; ps_dest.upper_bound_x = ps_src.upper_bound_x; ps_dest.upper_bound_y = ps_src.upper_bound_y; ps_dest.fixed_size = ps_src.fixed_size; @@ -229,7 +231,8 @@ class exact_command : public command if (clk_scheme_ptr == nullptr) { - env->out() << "[e] \"" << clocking << "\" does not refer to a supported clocking scheme" << std::endl; + env->out() << fmt::format("[e] \"{}\" does not refer to a supported clocking scheme", clocking) + << std::endl; reset_flags(); return; } @@ -256,7 +259,7 @@ class exact_command : public command } else { - env->out() << fmt::format("[e] impossible to place and route {} within the given parameters", + env->out() << fmt::format("[e] impossible to place and route '{}' within the given parameters", std::visit(get_name, ntk_ptr)) << std::endl; } @@ -267,7 +270,7 @@ class exact_command : public command } catch (...) { - env->out() << fmt::format("[e] an error occurred while placing and routing {} with the given parameters", + env->out() << fmt::format("[e] an error occurred while placing and routing '{}' with the given parameters", std::visit(get_name, ntk_ptr)) << std::endl; } diff --git a/include/fiction/algorithms/physical_design/exact.hpp b/include/fiction/algorithms/physical_design/exact.hpp index fdfd90fde..46dba939f 100644 --- a/include/fiction/algorithms/physical_design/exact.hpp +++ b/include/fiction/algorithms/physical_design/exact.hpp @@ -75,6 +75,14 @@ struct exact_physical_design_params */ std::shared_ptr> scheme = std::make_shared>(twoddwave_clocking()); + /** + * Number of total tiles to use as an upper bound. + * + * @note If `upper_bound_area` and (either) `upper_bound_x` or `upper_bound_y` are set, the imposed search space + * restrictions are cumulative. E.g., if `upper_bound_area == 20` and `upper_bound_x == 4`, all aspect ratios with + * an x-dimension of more than 4 *and* a total area of more than 20 will be skipped. + */ + uint16_t upper_bound_area = std::numeric_limits::max(); /** * Number of tiles to use as an upper bound in x direction. */ @@ -84,7 +92,9 @@ struct exact_physical_design_params */ uint16_t upper_bound_y = std::numeric_limits::max(); /** - * Investigate only aspect ratios with the number of tiles given as upper bound. + * Exclusively investigate aspect ratios that conform with the restrictions imposed by the upper bound options. + * E.g., if `fixed_size == true` *and* `upper_bound_area == 20`, only aspect ratios with exactly 20 tiles will be + * examined. Restricted imposed by the `upper_bound_x` and `upper_bound_y` flags additionally apply. */ bool fixed_size = false; /** @@ -180,7 +190,8 @@ class exact_impl // NOLINTNEXTLINE(*-prefer-member-initializer) ari = aspect_ratio_iterator{ - ps.fixed_size ? static_cast(ps.upper_bound_x * ps.upper_bound_y) : + ps.fixed_size ? std::min(static_cast(ps.upper_bound_area), + static_cast(ps.upper_bound_x * ps.upper_bound_y)) : static_cast(lower_bound)}; } @@ -270,7 +281,8 @@ class exact_impl [[nodiscard]] bool skippable(const typename Lyt::aspect_ratio& ar) const noexcept { // skip aspect ratios that extend beyond the specified upper bounds - if (ar.x >= params.upper_bound_x || ar.y >= params.upper_bound_y) + if ((ar.x + 1) * (ar.y + 1) > params.upper_bound_area || ar.x >= params.upper_bound_x || + ar.y >= params.upper_bound_y) { return true; } @@ -2849,7 +2861,7 @@ class exact_impl pst.num_aspect_ratios++; } - if (ar.x >= ps.upper_bound_x && ar.y >= ps.upper_bound_y) + if ((ar.x + 1) * (ar.y + 1) > ps.upper_bound_area || (ar.x >= ps.upper_bound_x && ar.y >= ps.upper_bound_y)) { return std::nullopt; } @@ -3034,8 +3046,10 @@ class exact_impl smt_handler handler{std::make_shared(), layout, *ntk, ps}; - for (; ari <= static_cast(ps.upper_bound_x) * static_cast(ps.upper_bound_y); - ++ari) // <= to prevent overflow + const auto upper_bound = std::min(static_cast(ps.upper_bound_area), + static_cast(ps.upper_bound_x * ps.upper_bound_y)); + + for (; ari <= upper_bound; ++ari) // <= to prevent overflow { #if (PROGRESS_BARS) diff --git a/test/algorithms/physical_design/exact.cpp b/test/algorithms/physical_design/exact.cpp index fd234cff0..982d5ac9d 100644 --- a/test/algorithms/physical_design/exact.cpp +++ b/test/algorithms/physical_design/exact.cpp @@ -663,6 +663,39 @@ TEST_CASE("High degree input networks", "[exact]") res(configuration()))); } +TEST_CASE("Exact physical design with upper bounds", "[exact]") +{ + auto upper_bound_config = twoddwave(crossings(configuration())); + + SECTION("total area") + { + upper_bound_config.upper_bound_area = 5u; // allow only 5 tiles total; this will fail (and is tested for) + + const auto half_adder = blueprints::half_adder_network(); + const auto layout = exact(half_adder, upper_bound_config); + + // since a half adder cannot be synthesized on just 5 tiles, layout should not have a value + CHECK(!layout.has_value()); + } + SECTION("individual bounds") + { + upper_bound_config.upper_bound_y = 3u; // allow only 3 tiles in y direction; this will work + + const auto mux = blueprints::mux21_network(); + auto layout = exact(mux, upper_bound_config); + + REQUIRE(layout.has_value()); + + CHECK(layout->y() <= 3); + + upper_bound_config.upper_bound_x = 2u; // additionally, allow only 2 tiles in x direction; this will now fail + + layout = exact(mux, upper_bound_config); + + CHECK(!layout.has_value()); + } +} + TEST_CASE("Exact physical design timeout", "[exact]") { auto timeout_config = use(crossings(configuration())); From 3450f49a5bc1c3d772ea07d60415bdb3f474f9af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 07:10:01 +0000 Subject: [PATCH 17/24] :arrow_up: Bump the submodules group with 3 updates (#362) --- libs/Catch2 | 2 +- libs/pybind11 | 2 +- libs/tinyxml2 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/Catch2 b/libs/Catch2 index 3acb8b30f..863c662c0 160000 --- a/libs/Catch2 +++ b/libs/Catch2 @@ -1 +1 @@ -Subproject commit 3acb8b30f1ce0c801de0e3880ea1f6467dd0105c +Subproject commit 863c662c0eff026300f4d729a7054e90d6d12cdd diff --git a/libs/pybind11 b/libs/pybind11 index eeac2f457..f29def9ea 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit eeac2f45728633d7ee6fe792bea2890345697119 +Subproject commit f29def9ea467c7fde754440733047953fa4b990c diff --git a/libs/tinyxml2 b/libs/tinyxml2 index c2d30872e..321ea883b 160000 --- a/libs/tinyxml2 +++ b/libs/tinyxml2 @@ -1 +1 @@ -Subproject commit c2d30872e20621955ca7feb9168bad996d591a19 +Subproject commit 321ea883b7190d4e85cae5512a12e5eaa8f8731f From b5b3796a9ce8c6874dbf262e49eac5f3914632e0 Mon Sep 17 00:00:00 2001 From: Jan Drewniok <97012901+Drewniok@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:17:02 +0100 Subject: [PATCH 18/24] =?UTF-8?q?=F0=9F=90=9B=20Fix=20floating-point=20err?= =?UTF-8?q?ors.=20(#363)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :art: avoid floating-point errors. * :art: implement Marcel's suggestions. --- .../simulation/sidb/operational_domain.hpp | 13 +++++++++-- .../simulation/sidb/operational_domain.cpp | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp index b38d43c79..d141e8b17 100644 --- a/include/fiction/algorithms/simulation/sidb/operational_domain.hpp +++ b/include/fiction/algorithms/simulation/sidb/operational_domain.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -596,8 +597,16 @@ class operational_domain_impl */ [[nodiscard]] step_point to_step_point(const operational_domain::parameter_point& pp) const noexcept { - return {static_cast((pp.x - params.x_min) / params.x_step), - static_cast((pp.y - params.y_min) / params.y_step)}; + const auto it_x = std::lower_bound(x_values.cbegin(), x_values.cend(), pp.x); + const auto it_y = std::lower_bound(y_values.cbegin(), y_values.cend(), pp.y); + + assert(it_x != x_values.cend() && "parameter point is outside of the x range"); + assert(it_y != y_values.cend() && "parameter point is outside of the y range"); + + const auto x_dis = std::distance(x_values.cbegin(), it_x); + const auto y_dis = std::distance(y_values.cbegin(), it_y); + + return {static_cast(x_dis), static_cast(y_dis)}; } /** * Calculates the number of steps in the x dimension based on the provided parameters. diff --git a/test/algorithms/simulation/sidb/operational_domain.cpp b/test/algorithms/simulation/sidb/operational_domain.cpp index 8e5e33e1b..265178809 100644 --- a/test/algorithms/simulation/sidb/operational_domain.cpp +++ b/test/algorithms/simulation/sidb/operational_domain.cpp @@ -323,6 +323,28 @@ TEST_CASE("BDL wire operational domain computation", "[operational-domain]") CHECK(op_domain_stats.num_non_operational_parameter_combinations <= 25); } } + SECTION("floating-point error") + { + op_domain_params.x_min = 2.5; + op_domain_params.x_max = 4.5; + op_domain_params.x_step = 0.9; + + op_domain_params.y_min = 2.5; + op_domain_params.y_max = 2.6; + op_domain_params.y_step = 0.1; + + SECTION("flood_fill") + { + const auto op_domain = operational_domain_flood_fill(lyt, std::vector{create_id_tt()}, 10000, + op_domain_params, &op_domain_stats); + + // check if the operational domain has the correct size + CHECK(op_domain.operational_values.size() == 2); + + CHECK(op_domain_stats.num_operational_parameter_combinations == 1); + CHECK(op_domain_stats.num_non_operational_parameter_combinations == 1); + } + } SECTION("semi-operational area") { op_domain_params.x_min = 0.5; From 32ad36fdbcd16549b14658bb4a27a769055a1f1f Mon Sep 17 00:00:00 2001 From: Jan Drewniok <97012901+Drewniok@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:41:30 +0100 Subject: [PATCH 19/24] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20hop=20energy=20cal?= =?UTF-8?q?culation=20from=20neutral=20to=20positive=20SiDB=20(#364)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: Hop energy of electron hops from neutrally to positively charged SiDBs is calculated correctly. * :art: Helper functions for determining the number of neg, neu, pos charged SiDBs of cds. * :white_check_mark: add and update tests. * :art: small fix. * :art: Delete multiplication by one. --- .../charge_distribution_surface.hpp | 43 +++++++++-- .../algorithms/simulation/sidb/quickexact.cpp | 73 +++++++++++++------ 2 files changed, 87 insertions(+), 29 deletions(-) diff --git a/include/fiction/technology/charge_distribution_surface.hpp b/include/fiction/technology/charge_distribution_surface.hpp index eaf86c938..d55ebebdc 100644 --- a/include/fiction/technology/charge_distribution_surface.hpp +++ b/include/fiction/technology/charge_distribution_surface.hpp @@ -984,12 +984,7 @@ class charge_distribution_surface : public Lyt { const auto hop_del = [this](const uint64_t c1, const uint64_t c2) // energy change when charge hops between two SiDBs. - { - const int dn_i = (strg->cell_charge[c1] == sidb_charge_state::NEGATIVE) ? 1 : -1; - const int dn_j = -dn_i; - - return strg->local_pot[c1] * dn_i + strg->local_pot[c2] * dn_j - strg->pot_mat[c1][c2] * 1; - }; + { return strg->local_pot[c1] - strg->local_pot[c2] - strg->pot_mat[c1][c2]; }; uint64_t hop_counter = 0; for (uint64_t i = 0u; i < strg->local_pot.size(); ++i) @@ -1375,6 +1370,42 @@ class charge_distribution_surface : public Lyt return required; } + /** + * Counts the number of SiDBs with a negative charge state. + * + * @return The number of SiDBs with a negative charge state. + */ + [[nodiscard]] std::size_t num_negative_sidbs() const noexcept + { + uint64_t count_negative_sidbs = 0; + this->foreach_cell([this, &count_negative_sidbs](const auto& c) + { count_negative_sidbs += (get_charge_state(c) == sidb_charge_state::NEGATIVE) ? 1 : 0; }); + return count_negative_sidbs; + } + /** + * Counts the number of SiDBs with a neutral charge state. + * + * @return The number of SiDBs with a neutral charge state. + */ + [[nodiscard]] std::size_t num_neutral_sidbs() const noexcept + { + uint64_t count_neutral_sidbs = 0; + this->foreach_cell([this, &count_neutral_sidbs](const auto& c) + { count_neutral_sidbs += (get_charge_state(c) == sidb_charge_state::NEUTRAL) ? 1 : 0; }); + return count_neutral_sidbs; + } + /** + * Counts the number of SiDBs with a positive charge state. + * + * @return The number of SiDBs with a positive charge state. + */ + [[nodiscard]] std::size_t num_positive_sidbs() const noexcept + { + uint64_t count_positive_sidbs = 0; + this->foreach_cell([this, &count_positive_sidbs](const auto& c) + { count_positive_sidbs += (get_charge_state(c) == sidb_charge_state::POSITIVE) ? 1 : 0; }); + return count_positive_sidbs; + } /** * This functions returns all cells that could be positively charged. However, this must not be necessarily the case * in a physically valid layout. diff --git a/test/algorithms/simulation/sidb/quickexact.cpp b/test/algorithms/simulation/sidb/quickexact.cpp index 9b5f3f9d7..be7a6886a 100644 --- a/test/algorithms/simulation/sidb/quickexact.cpp +++ b/test/algorithms/simulation/sidb/quickexact.cpp @@ -928,27 +928,16 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); - REQUIRE(simulation_results.charge_distributions.size() == 3); + REQUIRE(simulation_results.charge_distributions.size() == 4); - auto energy_min = std::numeric_limits::max(); - for (const auto& layout : simulation_results.charge_distributions) - { - if (layout.get_system_energy() < energy_min) - { - energy_min = layout.get_system_energy(); - } - } + const auto ground_state = std::min_element( + simulation_results.charge_distributions.cbegin(), simulation_results.charge_distributions.cend(), + [](const auto& lhs, const auto& rhs) { return lhs.get_system_energy() < rhs.get_system_energy(); }); - for (const auto& layout : simulation_results.charge_distributions) - { - if (std::abs(layout.get_system_energy() - energy_min) < physical_constants::POP_STABILITY_ERR) - { - CHECK(layout.get_charge_state({1, 3, 0}) == sidb_charge_state::NEGATIVE); - CHECK(layout.get_charge_state({1, 3, 0}) == sidb_charge_state::NEGATIVE); - CHECK(layout.get_charge_state({2, 3, 0}) == sidb_charge_state::POSITIVE); - CHECK(layout.get_charge_state({3, 3, 0}) == sidb_charge_state::NEGATIVE); - } - } + CHECK(ground_state->get_charge_state({-1, 3, 0}) == sidb_charge_state::NEGATIVE); + CHECK(ground_state->get_charge_state({1, 3, 0}) == sidb_charge_state::POSITIVE); + CHECK(ground_state->get_charge_state({2, 3, 0}) == sidb_charge_state::NEGATIVE); + CHECK(ground_state->get_charge_state({3, 3, 0}) == sidb_charge_state::NEUTRAL); } TEMPLATE_TEST_CASE( @@ -991,7 +980,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); - REQUIRE(simulation_results.charge_distributions.size() == 2); + REQUIRE(simulation_results.charge_distributions.size() == 4); const auto& charge_lyt_first = simulation_results.charge_distributions.front(); CHECK_THAT(charge_lyt_first.get_system_energy(), Catch::Matchers::WithinAbs(0, physical_constants::POP_STABILITY_ERR)); @@ -1016,7 +1005,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); - REQUIRE(simulation_results.charge_distributions.size() == 3); + REQUIRE(simulation_results.charge_distributions.size() == 10); const auto& charge_lyt_first = simulation_results.charge_distributions.front(); CHECK(charge_lyt_first.get_system_energy() < 0.08); CHECK(charge_lyt_first.get_system_energy() > -2.74); @@ -1043,7 +1032,7 @@ TEMPLATE_TEST_CASE( const auto simulation_results = quickexact(lyt, params); - CHECK(simulation_results.charge_distributions.size() == 5); + CHECK(simulation_results.charge_distributions.size() == 17); } TEMPLATE_TEST_CASE( @@ -1109,7 +1098,26 @@ TEMPLATE_TEST_CASE( CHECK(lyt.num_cells() == 6); - CHECK(simulation_results.charge_distributions.size() == 1); + CHECK(simulation_results.charge_distributions.size() == 3); +} + +TEMPLATE_TEST_CASE( + "4 DBs close to each other", "[quickexact]", + (cell_level_layout>>), + (charge_distribution_surface>>>)) +{ + TestType lyt{}; + + lyt.assign_cell_type({0, 0, 1}, TestType::cell_type::NORMAL); + lyt.assign_cell_type({0, 0, 0}, TestType::cell_type::NORMAL); + lyt.assign_cell_type({3, 0, 1}, TestType::cell_type::NORMAL); + lyt.assign_cell_type({5, 0, 1}, TestType::cell_type::NORMAL); + + const quickexact_params params{sidb_simulation_parameters{3, -0.25}}; + + const auto simulation_results = quickexact(lyt, params); + + CHECK(simulation_results.charge_distributions.size() > 0); } TEMPLATE_TEST_CASE( @@ -1239,6 +1247,21 @@ TEMPLATE_TEST_CASE( CHECK(charge_index.size() == 1); } + SECTION("Add SiDBs which are positively charged in the ground state, layout does not fulfill the logic anymore.") + { + params.physical_parameters.base = 3; + lyt.assign_cell_type({15, 2, 1}, TestType::cell_type::NORMAL); + lyt.assign_cell_type({15, 2, 0}, TestType::cell_type::NORMAL); + + const auto simulation_results = quickexact(lyt, params); + // find the ground state, which is the charge distribution with the lowest energy + const auto ground_state = std::min_element( + simulation_results.charge_distributions.cbegin(), simulation_results.charge_distributions.cend(), + [](const auto& lhs, const auto& rhs) { return lhs.get_system_energy() < rhs.get_system_energy(); }); + + CHECK(ground_state->num_positive_sidbs() > 0); + } + SECTION("Standard Physical Parameters") { const auto simulation_results = quickexact(lyt, params); @@ -1408,6 +1431,10 @@ TEMPLATE_TEST_CASE( simulation_results.charge_distributions.cbegin(), simulation_results.charge_distributions.cend(), [](const auto& lhs, const auto& rhs) { return lhs.get_system_energy() < rhs.get_system_energy(); }); + CHECK(ground_state->num_negative_sidbs() == 5); + CHECK(ground_state->num_neutral_sidbs() == 4); + CHECK(ground_state->num_positive_sidbs() == 0); + // check that charge distribution is correct; binary 1 is propagated through the BDL wire CHECK(ground_state->get_charge_state({0, 0, 0}) == sidb_charge_state::NEGATIVE); CHECK(ground_state->get_charge_state({5, 0, 0}) == sidb_charge_state::NEUTRAL); From b0cf4bb510d26a48e7df7e1370b0588709c3ce7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:34:49 +0100 Subject: [PATCH 20/24] :arrow_up: Bump the github-actions group with 1 update (#366) Bumps the github-actions group with 1 update: [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review). Updates `ZedThree/clang-tidy-review` from 0.14.0 to 0.15.0 - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/clang-tidy-review-post.yml | 2 +- .github/workflows/clang-tidy-review.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review-post.yml b/.github/workflows/clang-tidy-review-post.yml index fc6c96e78..48f1f2b5d 100644 --- a/.github/workflows/clang-tidy-review-post.yml +++ b/.github/workflows/clang-tidy-review-post.yml @@ -41,7 +41,7 @@ jobs: # Posts the comments - name: Post review comments id: post-review - uses: ZedThree/clang-tidy-review/post@v0.14.0 + uses: ZedThree/clang-tidy-review/post@v0.15.0 # If there are any comments, fail the check - if: steps.post-review.outputs.total_comments > 0 diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index a3ef38323..d61389183 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -24,7 +24,7 @@ jobs: submodules: recursive - name: Review code with Clang-Tidy - uses: ZedThree/clang-tidy-review@v0.14.0 + uses: ZedThree/clang-tidy-review@v0.15.0 id: review with: cmake_command: > From 611b60c3eec89fb6bb587cb055be074833607713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:08:15 +0100 Subject: [PATCH 21/24] :arrow_up: Bump the submodules group with 1 update (#365) Bumps the submodules group with 1 update: [libs/pybind11](https://github.com/pybind/pybind11). Updates `libs/pybind11` from `f29def9` to `aec6cc5` - [Release notes](https://github.com/pybind/pybind11/releases) - [Commits](https://github.com/pybind/pybind11/compare/f29def9ea467c7fde754440733047953fa4b990c...aec6cc5406edb076f5a489c2d7f84bb07052c4a3) --- updated-dependencies: - dependency-name: libs/pybind11 dependency-type: direct:production dependency-group: submodules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/pybind11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/pybind11 b/libs/pybind11 index f29def9ea..aec6cc540 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit f29def9ea467c7fde754440733047953fa4b990c +Subproject commit aec6cc5406edb076f5a489c2d7f84bb07052c4a3 From 81a0a385051e3d76e677cea775f6f1ed71cd80d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 07:45:42 +0100 Subject: [PATCH 22/24] :arrow_up: Bump the github-actions group with 1 update (#368) Bumps the github-actions group with 1 update: [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review). Updates `ZedThree/clang-tidy-review` from 0.15.0 to 0.17.0 - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/clang-tidy-review-post.yml | 2 +- .github/workflows/clang-tidy-review.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review-post.yml b/.github/workflows/clang-tidy-review-post.yml index 48f1f2b5d..84638fc6d 100644 --- a/.github/workflows/clang-tidy-review-post.yml +++ b/.github/workflows/clang-tidy-review-post.yml @@ -41,7 +41,7 @@ jobs: # Posts the comments - name: Post review comments id: post-review - uses: ZedThree/clang-tidy-review/post@v0.15.0 + uses: ZedThree/clang-tidy-review/post@v0.17.0 # If there are any comments, fail the check - if: steps.post-review.outputs.total_comments > 0 diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index d61389183..33053b24f 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -24,7 +24,7 @@ jobs: submodules: recursive - name: Review code with Clang-Tidy - uses: ZedThree/clang-tidy-review@v0.15.0 + uses: ZedThree/clang-tidy-review@v0.17.0 id: review with: cmake_command: > From 1853689234b1dc4f21833b78716e78caec1511eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:10:10 +0100 Subject: [PATCH 23/24] :arrow_up: Bump the submodules group with 3 updates (#369) Bumps the submodules group with 3 updates: [libs/Catch2](https://github.com/catchorg/Catch2), [libs/pybind11](https://github.com/pybind/pybind11) and [libs/json](https://github.com/nlohmann/json). Updates `libs/Catch2` from `863c662` to `79205da` - [Release notes](https://github.com/catchorg/Catch2/releases) - [Commits](https://github.com/catchorg/Catch2/compare/863c662c0eff026300f4d729a7054e90d6d12cdd...79205da6a67f5ad3d7874cf1c6423283dfeab199) Updates `libs/pybind11` from `aec6cc5` to `869cc1f` - [Release notes](https://github.com/pybind/pybind11/releases) - [Commits](https://github.com/pybind/pybind11/compare/aec6cc5406edb076f5a489c2d7f84bb07052c4a3...869cc1ff085dd405635b00eb46e5c84f50f26099) Updates `libs/json` from `a259ecc` to `7efe875` - [Release notes](https://github.com/nlohmann/json/releases) - [Commits](https://github.com/nlohmann/json/compare/a259ecc51e1951e12f757ce17db958e9881e9c6c...7efe875495a3ed7d805ddbb01af0c7725f50c88b) --- updated-dependencies: - dependency-name: libs/Catch2 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/pybind11 dependency-type: direct:production dependency-group: submodules - dependency-name: libs/json dependency-type: direct:production dependency-group: submodules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- libs/Catch2 | 2 +- libs/json | 2 +- libs/pybind11 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/Catch2 b/libs/Catch2 index 863c662c0..79205da6a 160000 --- a/libs/Catch2 +++ b/libs/Catch2 @@ -1 +1 @@ -Subproject commit 863c662c0eff026300f4d729a7054e90d6d12cdd +Subproject commit 79205da6a67f5ad3d7874cf1c6423283dfeab199 diff --git a/libs/json b/libs/json index a259ecc51..7efe87549 160000 --- a/libs/json +++ b/libs/json @@ -1 +1 @@ -Subproject commit a259ecc51e1951e12f757ce17db958e9881e9c6c +Subproject commit 7efe875495a3ed7d805ddbb01af0c7725f50c88b diff --git a/libs/pybind11 b/libs/pybind11 index aec6cc540..869cc1ff0 160000 --- a/libs/pybind11 +++ b/libs/pybind11 @@ -1 +1 @@ -Subproject commit aec6cc5406edb076f5a489c2d7f84bb07052c4a3 +Subproject commit 869cc1ff085dd405635b00eb46e5c84f50f26099 From e172b81054634f750cb1b6f550089dec187cb840 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Fri, 19 Jan 2024 10:39:31 +0100 Subject: [PATCH 24/24] :zap: Increased parallelism for building and testing in the Ubuntu and Windows workflows to make use of the performance benefit of GitHub's updated runners. (#370) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverage.yml | 4 ++-- .github/workflows/ubuntu.yml | 4 ++-- .github/workflows/windows.yml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c8282fb25..dae5e733e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -132,7 +132,7 @@ jobs: - if: matrix.language == 'cpp' name: Build fiction working-directory: ${{github.workspace}}/build - run: cmake --build . + run: cmake --build . --config ${{matrix.build_type}} -j4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6db779c6d..3c75abc08 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -105,12 +105,12 @@ jobs: - name: Build working-directory: ${{github.workspace}}/build - run: cmake --build . --config $BUILD_TYPE + run: cmake --build . --config $BUILD_TYPE -j4 - name: Test working-directory: ${{github.workspace}}/build - run: ctest -C $BUILD_TYPE --verbose --output-on-failure --parallel 2 + run: ctest -C $BUILD_TYPE --verbose --output-on-failure --parallel 4 - name: Setup and run lcov run: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index b2b7e3971..edeb6a310 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -135,10 +135,10 @@ jobs: - name: Build fiction working-directory: ${{github.workspace}}/build - run: cmake --build . + run: cmake --build . --config ${{matrix.build_type}} -j4 - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C ${{matrix.build_type}} --verbose --output-on-failure --parallel 2 + run: ctest -C ${{matrix.build_type}} --verbose --output-on-failure --parallel 4 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9f0300f4f..cdae46afa 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -97,10 +97,10 @@ jobs: - name: Build working-directory: ${{github.workspace}}\build - run: cmake --build . --config ${{matrix.build_type}} -j2 + run: cmake --build . --config ${{matrix.build_type}} -j4 - name: Test working-directory: ${{github.workspace}}\build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C ${{matrix.build_type}} --verbose --output-on-failure --parallel 2 + run: ctest -C ${{matrix.build_type}} --verbose --output-on-failure --parallel 4