Code Monkey home page Code Monkey logo

fiction's Introduction

fiction – Design Automation for Field-coupled Nanotechnologies

Ubuntu CI macOS CI Windows CI Python Bindings Docker Image CodeQL Documentation Status codecov License PyPI Release arXiv

This code base provides a C++17 framework for field-coupled technology-independent open nanocomputing developed as part of the Munich Nanotech Toolkit (MNT) by the Chair for Design Automation at the Technical University of Munich. Within fiction, algorithms for logic synthesis, placement, routing, clocking, verification, and simulation for Field-coupled Nanocomputing (FCN) technologies are implemented.

To this end, most physical design tasks can be performed on generic data structures that abstract from particular technology or cell design. Using an extensible set of gate libraries, technologies, and cell types, these can easily be compiled down to any desired FCN technology for physical simulation.

For these use cases, fiction provides a C++ header-only library as well as Python bindings that provide data types and algorithms for recurring tasks, e.g., logic network and layout types on different abstraction levels, clocking schemes, gate libraries, design automation algorithms, etc. Additionally, fiction comes with an ABC-like CLI tool that allows quick access to its core functionality.

Documentation

If you have any questions, feel free to contact us via [email protected] or by creating an issue on GitHub.

Quick Start (C++)

Clone the repository and its submodules:

git clone --recursive https://github.com/cda-tum/fiction.git

The CLI

Inside the newly cloned fiction folder, trigger the build process:

cmake . -B build
cd build
cmake --build . -j4

Run the CLI tool:

cli/fiction

Here is an example of running fiction to perform a full physical design flow on a QCA circuit layout that can afterward be simulated in QCADesigner:

CLI example

The Header-only Library

Add fiction as a sub-directory to your CMake project and link against libfiction (assuming your project is called fanfiction):

add_subdirectory(fiction/)
target_link_libraries(fanfiction libfiction)

Include the headers you need:

#include <fiction/layouts/cell_level_layout.hpp>
#include <fiction/layouts/clocking_scheme.hpp>
#include <fiction/technology/qca_one_library.hpp>
#include <fiction/io/write_qca_layout.hpp>
#include <fiction/...>

Quick Start (Python)

Install the Python bindings from PyPI:

pip install mnt.pyfiction

Import the bindings:

from mnt import pyfiction

For a full getting started guide, please refer to the documentation.

Supported Technologies

Physical design in fiction can be performed technology-independent. Only if resulted layouts are to be physically, simulated, a specific technology implementation is required. To this end, fiction supports various potential FCN implementations together with gate libraries to compile gate-level layout abstractions down to the cell level. Additionally, output formats for external physical simulator engines are also supported.

Quantum-dot Cellular Automata (QCA)

QCA cells

Gate libraries:

File formats:

Many thanks to Frank Sill Torres for his support with the QCADesigner format, to Willem Lambooy for his support with the QCA-STACK format, and to Sophia Kuhn for implementing the SVG writer!

in-plane Nanomagnet Logic (iNML)

iNML cells

Gate libraries:

File formats:

Many thanks to Umberto Garlando, Fabrizio Riente, and Giuliana Beretta for their support!

Silicon Dangling Bonds (SiDBs)

SiDB cells

Gate libraries:

File formats:

Many thanks to Samuel Sze Hang Ng for his support!

Implemented Design Automation Algorithms

The fiction framework provides implementations of state-of-the-art design automation algorithms for FCN technologies. These algorithms can be used in evaluation scripts to perform logic synthesis, physical design, layout verification, and physical simulation.

Logic Synthesis

For logic synthesis, fiction relies on the mockturtle library that offers a multitude of logic network types and optimization algorithms. Logic synthesis can be performed in external tools and resulting Verilog/AIGER/BLIF/... files can be parsed by fiction. Alternatively, since mockturtle is included in fiction, synthesis can be applied in the same evaluation script.

Physical Design

For automatic FCN layout obtainment, fiction provides algorithms that receive mockturtle logic networks as input specification and output placed, routed, and clocked generic FCN circuits.

QCA Layout

Among these algorithms are

plus several path-finding algorithms that work on generic layouts:

On top, there is a hexagonalization algorithm to transform Cartesian layouts suitable for QCA into hexagonal layouts suitable for SiDBs, and multiple algorithms to optimize gate-level layouts post-placement:

Verification

Layout correctness can be validated 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:

Clocking Schemes

Regular clocking schemes have been proposed in the FCN literature, which can be used as a floor plans for physical design. However, sometimes it can make sense to have more freedom and assign clock numbers on the fly. That is why fiction supports both regular and irregular clocking schemes with variable amounts of clock numbers as QCA for instance uses four clock phases but iNML needs only three.

Built-in schemes are

Columnar Row 2DDWave
Columnar Row 2DDWave
USE RES ESR
USE RES ESR
CFE Ripple BANCS
CFE Ripple BANCS

plus the mentioned irregular open clocking that works via a clock map instead of a regular extrapolated cutout.

Wire Crossings

Second layer crossing

With many FCN technologies considered planar, wire crossings should be minimized if possible. However, there are some options in QCA where, using a second layer, crossings over short distances and co-planar rotated cells become possible. As both are just technical implementations of the same concept, fiction supports crossings as wires in a second grid layer in its data structures for all FCN technologies. They will also be represented as such in corresponding SVG and QCADesigner output. However, note that it is to be interpreted as the concept of crossings and could also be realized co-planar.

Wires are only allowed to cross other wires! Wires crossing gates is considered to lead to unstable signals.

Gate Pins vs. Designated I/Os

Input pin and cell output

In the literature, both are seen: having input cells (pins) directly located in the gate structure or using designated I/O elements that are located outside of gates. This distinction only makes sense on the gate level and fiction supports both approaches and offers usage in the implemented physical design algorithms.

Multi Wires

Multi wires

Gate-level abstraction has its limits. Often, chip area is wasted when only using a single wire per tile. In fiction, cell-level layouts allow for precise control over cell placement and can, thus, also create multiple wire segments per clock zone. Physical simulation can give an indication of whether the built structures are implementing the intended functionality.

Synchronization Elements

Synchronization element

A technology extension proposes to utilize the external clock signal generator in an unconventional way: by creating further asymmetric clock signals with extended Hold phases that are assigned to specific wire tiles, synchronization elements can be created that stall signals over multiple clock cycles. These artificial latches are able to feed information to any other clock number, but their usage reduces the overall throughput of the layout. In return, long wire detours for signal synchronization can be prevented.

Cost Metrics

Designed layouts can be evaluated with regard to several cost functions. The following metrics are currently implemented:

Gate-level layouts:

  • Circuit dimension in tiles
  • Number of gate tiles
  • Number of wire tiles
  • Number of wire crossings
  • Number of synchronization elements
  • Critical path
  • Throughput
  • Bounding box
  • Energy dissipation based on a physical model (QCA only)

Cell-level layouts:

  • Circuit dimension in cells
  • Number of cells
  • Bounding box
  • Area usage in nm²

Benchmark Library

To objectively evaluate and compare software and design automation tools, MNT Bench 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 folder contains the function descriptions of frequently used benchmark sets in Verilog format (.v) provided by MNT Bench.

Reference

Since fiction is academic software, we would be thankful if you referred to it by citing the following publication:

@misc{fiction,
      author = {Walter, Marcel and Wille, Robert and Sill Torres, Frank and Gro{\ss}e, Daniel and Drechsler, Rolf},
      title = {{fiction: An Open Source Framework for the Design of Field-coupled Nanocomputing Circuits}},
      archivePrefix = {arXiv},
      eprint = {1905.02477},
      note = {arXiv:1905.02477},
      year = {2019},
      month = {May}
}

Additionally, many algorithms implemented in fiction have been published individually. For a full list of publications, please refer to the documentation.

Acknowledgements

The Munich Nanotech Toolkit has been supported by the Bavarian State Ministry for Science and Arts through the Distinguished Professorship Program.

TUM Logo       Coat of Arms of Bavaria

fiction's People

Contributors

burgholzer avatar dependabot[bot] avatar drewniok avatar hibenj avatar lsschmid avatar marcelwa avatar mkay00 avatar pre-commit-ci[bot] avatar simon1hofmann avatar wlambooy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

fiction's Issues

🐛 CodeQL seems to run out of memory

fiction version

v0.6.2

OS

ubuntu-latest

Python version

3.10.x

C++ compiler

clang++-11

Additional environment information

No response

Description

The CodeQL action fails with the following error.

A fatal error occurred: Could not write a relation to a file
  (eventual cause: IOException "No space left on device")
  Error: Error running analysis for cpp: Encountered a fatal error while running "/opt/hostedtoolcache/CodeQL/2.17.3/x64/codeql/codeql database run-queries --ram=14567 --threads=4 /home/runner/work/_temp/codeql_databases/cpp --expect-discarded-cache --min-disk-free=1024 -v --intra-layer-parallelism". Exit code was 2 and error was: A fatal error occurred: Could not write a relation to a file
  (eventual cause: IOException "No space left on device") See the logs for more details.

Expected behavior

It should be able to run through without memory issues.

How to Reproduce

Rerun the CodeQL action on main

🐛 `QuickExact` returns duplicate charge configurations

fiction version

fiction main -- latest to date (62a132f)

OS

Ubuntu 22.04.2 LTS | Linux 5.15.133.1-microsoft-standard-WSL2 x86_64 GNU/Linux

Python version

3.11.2

C++ compiler

Ubuntu clang version 14.0.0-1ubuntu1.1

Additional environment information

No response

Description

As the title states, I found as 20 DB layout (unfortunately not a smaller one thus far) for which QuickExact returns 4 physically valid charge distributions, yet the test below shows evidence that there are actually 2 unique physically valid charge distributions.

Expected behavior

An SiDB simulator should not return duplicate results, this could indicate some hidden inefficiency too.

How to Reproduce

#include <fiction/algorithms/simulation/sidb/quickexact.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/types.hpp>

#include <cstdint>

TEST_CASE("QuickExact duplicate charge configurations", "[quickexact]")
{
    using sidb_lyt = sidb_cell_clk_lyt_siqad;
    sidb_lyt lyt{};

    lyt.assign_cell_type({3, 0, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({4, 0, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({13, 0, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({5, 1, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({22, 3, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({11, 5, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({2, 6, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({4, 6, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({23, 7, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({16, 8, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({8, 8, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({15, 9, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({1, 10, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({12, 10, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({14, 10, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({9, 11, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({24, 11, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({10, 11, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({13, 12, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({24, 12, 0}, sidb_lyt::cell_type::NORMAL);


    // not needed, but I'm giving all default parameters explicitly for the sake of the test
    const sidb_simulation_parameters params{3, -0.32, 5.6, 5.0, 3.84, 7.68, 2.25};

    const sidb_simulation_result<sidb_lyt>& qe_res = quickexact(lyt, quickexact_params<sidb_lyt>{params, quickexact_params<sidb_lyt>::automatic_base_number_detection::ON, {}, 0});

    // QuickExact returns 4 physically valid charge layout
    REQUIRE(qe_res.charge_distributions.size() == 4);

    // This loop asserts that the first two are equal
    for (uint64_t i = 0; i < 2; ++i)
    {
        CHECK(qe_res.charge_distributions[i].get_charge_state({3, 0, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({4, 0, 0}) == sidb_charge_state::POSITIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({13, 0, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({5, 1, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({22, 3, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({11, 5, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({2, 6, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({4, 6, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({23, 7, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({16, 8, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({8, 8, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({15, 9, 0}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({1, 10, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({12, 10, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({14, 10, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({9, 11, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({24, 11, 0}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({10, 11, 1}) == sidb_charge_state::POSITIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({13, 12, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({24, 12, 0}) == sidb_charge_state::NEGATIVE);
    }

    // This loop asserts that the last two are equal
    for (uint64_t i = 2; i < 4; ++i)
    {
        CHECK(qe_res.charge_distributions[i].get_charge_state({3, 0, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({4, 0, 0}) == sidb_charge_state::POSITIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({13, 0, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({5, 1, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({22, 3, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({11, 5, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({2, 6, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({4, 6, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({23, 7, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({16, 8, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({8, 8, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({15, 9, 0}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({1, 10, 1}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({12, 10, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({14, 10, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({9, 11, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({24, 11, 0}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({10, 11, 1}) == sidb_charge_state::NEUTRAL);
        CHECK(qe_res.charge_distributions[i].get_charge_state({13, 12, 0}) == sidb_charge_state::NEGATIVE);
        CHECK(qe_res.charge_distributions[i].get_charge_state({24, 12, 0}) == sidb_charge_state::NEGATIVE);
    }
}

Security warnings and notes

CodeQL found some security warnings and had some notes. Nothing critical, fortunately. Would be cool to address them anyway for good measure.

🐛 21 Physically valid layouts (for the same layout) not detected by `QuickExact`

fiction version

fiction main -- latest to date (62a132f)

OS

Ubuntu 22.04.2 LTS | Linux 5.15.133.1-microsoft-standard-WSL2 x86_64 GNU/Linux

Python version

3.11.2

C++ compiler

Ubuntu clang version 14.0.0-1ubuntu1.1

Additional environment information

No response

Description

There's not much more to say than the title states. I found a 20 DB layout for which QuickExact gives 0 physically valid layouts, while the 21 charge configurations in the "how to reproduce" section are actually physically valid as judged by charge_distribution_surface standard functionality. I suspect the presence of positive charges here brings up an issue. I'm very much curious to hear what the problem here is when the issue is discovered.

To be sure there is no issue with (physical) parameters, I have specified them explicitly, although the issue is reproduced when all parameters (of charge_distribution_surface and quickexact) are defaulted.

You will have to take my word for that the charge configurations below are all unique (either that or check it yourself I suppose). Though even if they weren't and only one physically valid charge distribution is not detected by QuickExact, the issue claim here would still be valid.

Expected behavior

QuickExact should find all physically valid layouts, hence also these ones. Either that or these layouts should not be judged physically valid, but I suspect the former is more likely.

How to Reproduce

Insert the following Catch2 test into a test file of choice and run it. On my systems the test passes, meaning all of these 21 charge distributions are physically valid, and QuickExact finds precisely 0 physically valid charge distributions for the same layout.

#include <fiction/algorithms/simulation/sidb/quickexact.hpp>
#include <fiction/algorithms/simulation/sidb/sidb_simulation_parameters.hpp>
#include <fiction/technology/charge_distribution_surface.hpp>
#include <fiction/types.hpp>

TEST_CASE("21 Physically valid charge distributions for the same layout not detected by QuickExact", "[quickexact]")
{
    using sidb_lyt = sidb_cell_clk_lyt_siqad;
    sidb_lyt lyt{};

    lyt.assign_cell_type({22, 1, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({24, 2, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({23, 3, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({13, 4, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({10, 4, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({1, 5, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({0, 6, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({1, 6, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({24, 6, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({4, 6, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({3, 7, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({0, 8, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({1, 8, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({9, 9, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({24, 9, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({22, 9, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({13, 10, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({14, 10, 0}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({1, 11, 1}, sidb_lyt::cell_type::NORMAL);
    lyt.assign_cell_type({17, 11, 1}, sidb_lyt::cell_type::NORMAL);

    // not needed, but I'm giving all default parameters explicitly for the sake of the test
    const sidb_simulation_parameters params{3, -0.32, 5.6, 5.0, 3.84, 7.68, 2.25};

    charge_distribution_surface charge_lyt{lyt};
    charge_lyt.assign_physical_parameters(params);

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    charge_lyt.assign_charge_state({22, 1, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 2, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({23, 3, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({13, 4, 0}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({10, 4, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 5, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({0, 6, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({1, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 6, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({4, 6, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({3, 7, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({0, 8, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 8, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({9, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({24, 9, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({22, 9, 1}, sidb_charge_state::NEUTRAL);
    charge_lyt.assign_charge_state({13, 10, 0}, sidb_charge_state::POSITIVE);
    charge_lyt.assign_charge_state({14, 10, 0}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({1, 11, 1}, sidb_charge_state::NEGATIVE);
    charge_lyt.assign_charge_state({17, 11, 1}, sidb_charge_state::NEGATIVE);

    charge_lyt.update_after_charge_change();

    CHECK(charge_lyt.is_physically_valid());

    // QuickExact finds 0 physically valid charge distributions for this layout
    CHECK(quickexact(lyt,
                     quickexact_params<sidb_lyt>{params, quickexact_params<sidb_lyt>::automatic_base_number_detection::ON, {}, 0})
              .charge_distributions.size() == 0);
}

Permission denied error when building v.0.3.0

When I try to build the project with the "make" command, I get the following error:

Scanning dependencies of target z3
[ 3%] Generating z3/lib/libz3.so
/bin/sh: 1: scripts/mk_make.py: Permission denied
CMakeFiles/z3.dir/build.make:60: recipe for target 'z3/lib/libz3.so' failed
make[2]: *** [z3/lib/libz3.so] Error 126
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/z3.dir/all' failed
make[1]: *** [CMakeFiles/z3.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

I tried it in WSL and in a docker container, both with Ubuntu 18.04. If I checkout an older version (v.0.2.1) the build works after a submodule update.

🐛 `is_ground_state` does not use `QuickSim`'s results

fiction version

latest

OS

doesn't matter

Python version

No response

C++ compiler

No response

Additional environment information

No response

Description

It's clear to see what the problem is here:

template <typename Lyt>
[[nodiscard]] bool is_ground_state(const sidb_simulation_result<Lyt>& quicksim_results,
                                   const sidb_simulation_result<Lyt>& exhaustive_results) noexcept
{
    static_assert(is_cell_level_layout_v<Lyt>, "Lyt is not a cell-level layout");
    static_assert(has_sidb_technology_v<Lyt>, "Lyt is not an SiDB layout");

    if (exhaustive_results.charge_distributions.empty())
    {
        return false;
    }

    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;
}

I got a warning for an unused parameter (quicksim_results), so I thought I would put a small fix in the ClusterComplete PR, but apparently TTS fails now, which is unrelated. So I took out the change there and made this issue instead, highlighting the problem.

Expected behavior

No response

How to Reproduce

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.