Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wlambooy opened this issue Mar 1, 2024 · 6 comments · Fixed by #388
Closed
Assignees
Labels
bug Something isn't working

Comments

@wlambooy
Copy link
Contributor

wlambooy commented Mar 1, 2024

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

@wlambooy wlambooy added the bug Something isn't working label Mar 1, 2024
@Drewniok
Copy link
Collaborator

Drewniok commented Mar 1, 2024

Thank you for this interesting discovery! 🙏
At the moment, I suspect that this is indeed due to a bug in the 3-state simulation.
I will keep you posted and take a look as soon as possible.

@wlambooy
Copy link
Contributor Author

wlambooy commented Mar 1, 2024

Here's a more minimal example, and interestingly enough, QuickExact find one physically valid layout with a positive charge, though I have another physically valid layout that it didn't find that does not have a positive charge. One to grind the gears on for sure, though I bet 3 DBs is a lot more managable to work with :)

This one operates on all default parameters much like the former.

lyt.assign_cell_type({2, 1, 0}, sidb_lyt::cell_type::NORMAL);
lyt.assign_cell_type({1, 1, 1}, sidb_lyt::cell_type::NORMAL);
lyt.assign_cell_type({1, 2, 0}, sidb_lyt::cell_type::NORMAL);


charge_distribution_layout charge_lyt{lyt};


// QuickExact finds this one
charge_lyt.assign_charge_state({2, 1, 0}, sidb_charge_state::NEGATIVE);
charge_lyt.assign_charge_state({1, 1, 1}, sidb_charge_state::POSITIVE);
charge_lyt.assign_charge_state({1, 2, 0}, sidb_charge_state::NEGATIVE);

charge_lyt.update_after_charge_change();

CHECK(charge_lyt.is_physically_valid());


// Though not this one
charge_lyt.assign_charge_state({2, 1, 0}, sidb_charge_state::NEUTRAL);
charge_lyt.assign_charge_state({1, 1, 1}, sidb_charge_state::NEGATIVE);
charge_lyt.assign_charge_state({1, 2, 0}, sidb_charge_state::NEUTRAL);

charge_lyt.update_after_charge_change();

CHECK(charge_lyt.is_physically_valid());


CHECK(quickexact(lyt).charge_distributions.size() == 1);

@wlambooy
Copy link
Contributor Author

wlambooy commented Mar 1, 2024

I should add: ExGS does not give the same issue (as one would hope)

@Drewniok
Copy link
Collaborator

Drewniok commented Mar 1, 2024

Thank you for your investigation! I think I have a strong feeling where the bug is. QuickExact finds the first solution with base number = 3, and the other only if the base number is 2, which is not the desired behavior. I will use this layout for debugging. Thank you!

@wlambooy
Copy link
Contributor Author

wlambooy commented Mar 1, 2024

I found another issue with QuickExact results. (do I open a different issue for this one?)

The test case below shows evidence of QuickExact returning duplicate results for this layout. I haven't been able to find a more minimal example thus far unfortunately.

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


    const sidb_simulation_result<sidb_lyt>& qe_res = quickexact(lyt);

    REQUIRE(qe_res.charge_distributions.size() == 4);

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

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

@Drewniok
Copy link
Collaborator

Drewniok commented Mar 1, 2024

Thank you so much!
Yes, please open a new issue.

@wlambooy wlambooy changed the title 🐛 21 Physically valid layouts (for the same layout) not detected by QuickExact 🐛 21 Physically valid layouts (for the same layout) not detected by QuickExact Mar 1, 2024
@marcelwa marcelwa assigned Drewniok and unassigned marcelwa Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants