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

✨ Added timeout for QuickSim #654

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
9477cf8
:memo: Update pyfiction docstrings
actions-user Dec 12, 2024
35fed45
Merge branch 'cda-tum:main' into main
Drewniok Dec 26, 2024
bc905b7
Merge branch 'cda-tum:main' into main
Drewniok Jan 2, 2025
f188a69
Merge branch 'cda-tum:main' into main
Drewniok Jan 7, 2025
65ec0a5
Merge branch 'cda-tum:main' into main
Drewniok Jan 8, 2025
24c2421
Merge branch 'cda-tum:main' into main
Drewniok Jan 9, 2025
4113659
:memo: Update pyfiction docstrings
actions-user Jan 9, 2025
72e08f4
Merge branch 'cda-tum:main' into main
Drewniok Jan 11, 2025
dfb71fb
Merge branch 'cda-tum:main' into main
Drewniok Jan 12, 2025
d5c5ff5
Merge branch 'cda-tum:main' into main
Drewniok Jan 13, 2025
9d45592
Merge branch 'cda-tum:main' into main
Drewniok Jan 13, 2025
dfaf039
Merge branch 'cda-tum:main' into main
Drewniok Jan 14, 2025
af15694
Merge branch 'cda-tum:main' into main
Drewniok Jan 15, 2025
74dfaab
Merge branch 'cda-tum:main' into main
Drewniok Jan 19, 2025
5598bce
Merge branch 'cda-tum:main' into main
Drewniok Jan 23, 2025
7a1cde8
Merge branch 'cda-tum:main' into main
Drewniok Jan 24, 2025
ecb1fbb
Merge branch 'cda-tum:main' into main
Drewniok Jan 25, 2025
8c6bd0a
Merge branch 'cda-tum:main' into main
Drewniok Jan 28, 2025
fc8e96d
Merge branch 'cda-tum:main' into main
Drewniok Jan 30, 2025
9f1d8a1
Merge branch 'cda-tum:main' into main
Drewniok Jan 31, 2025
34fb825
Merge branch 'cda-tum:main' into main
Drewniok Feb 1, 2025
bf2b216
:sparkles: add timeout feature to quicksim.
Drewniok Feb 1, 2025
aeeb5a0
:memo: Update pyfiction docstrings
actions-user Feb 1, 2025
7f3dd43
:memo: update docu.
Drewniok Feb 1, 2025
1a019ee
:memo: small fix.
Drewniok Feb 1, 2025
143a3f6
:art: fix clang-tidy warnings.
Drewniok Feb 1, 2025
16efd53
:art: fix clang-tidy warnings.
Drewniok Feb 1, 2025
0945b45
:art: small fix.
Drewniok Feb 1, 2025
1690cea
:white_check_mark: small fix.
Drewniok Feb 3, 2025
39fa9c4
:memo: small fix.
Drewniok Feb 3, 2025
2adc077
Merge branch 'main' into add_timout_for_quicksim
Drewniok Feb 3, 2025
88028b2
:art: small fix.
Drewniok Feb 3, 2025
eed8faa
:memo: Update pyfiction docstrings
actions-user Feb 3, 2025
8165114
:art: small fix.
Drewniok Feb 3, 2025
568780f
:white_check_mark: use more margin.
Drewniok Feb 3, 2025
d6a3b35
Merge branch 'main' into add_timout_for_quicksim
Drewniok Feb 4, 2025
27a0b28
:white_check_mark: increase time margin.
Drewniok Feb 5, 2025
afe1338
Merge branch 'main' into add_timout_for_quicksim
Drewniok Feb 5, 2025
9281c53
:art: implement Marcel's feedback.
Drewniok Feb 9, 2025
db26065
:art: use optional as return for quicksim.
Drewniok Feb 11, 2025
10ac019
Merge branch 'main' into add_timout_for_quicksim
Drewniok Feb 11, 2025
2da2e2d
:memo: Update pyfiction docstrings
actions-user Feb 11, 2025
7f9fda3
:art: small fix.
Drewniok Feb 11, 2025
9d8c7bd
:art: remove unused header.
Drewniok Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ inline void quicksim(pybind11::module& m)
.def_readwrite("alpha", &fiction::quicksim_params::alpha, DOC(fiction_quicksim_params_alpha))
.def_readwrite("number_threads", &fiction::quicksim_params::number_threads,
DOC(fiction_quicksim_params_number_threads))
.def_readwrite("timeout", &fiction::quicksim_params::timeout, DOC(fiction_quicksim_params_timeout));

;
;

// NOTE be careful with the order of the following calls! Python will resolve the first matching overload!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,77 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

Drewniok marked this conversation as resolved.
Show resolved Hide resolved
#include <any>
#include <cstdint>
#include <stdexcept>
#include <string>
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
#include <unordered_map>

Drewniok marked this conversation as resolved.
Show resolved Hide resolved
namespace pyfiction
{

namespace detail
{

namespace py = pybind11;

// Helper function to convert std::any to Python objects
inline py::object convert_any_to_py(const std::any& value)
{
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
try
{
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
if (value.type() == typeid(int))
{
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
return py::int_(std::any_cast<int>(value));
}
else if (value.type() == typeid(double))
{
return py::float_(std::any_cast<double>(value));
}
else if (value.type() == typeid(bool))
{
return py::bool_(std::any_cast<bool>(value));
}
else if (value.type() == typeid(std::string))
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
{
return py::str(std::any_cast<std::string>(value));
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
}
else if (value.type() == typeid(uint64_t))
{
return pybind11::int_(std::any_cast<uint64_t>(value));
}
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
else
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
{
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
throw std::runtime_error(std::string("Unsupported type in std::any: ") + value.type().name());
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
}
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
}
catch (const std::exception& e)
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
{
throw std::runtime_error(std::string("Error in convert_any_to_py: ") + e.what());
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
}
}

inline py::dict convert_map_to_py(const std::unordered_map<std::string, std::any>& map)
{
pybind11::dict result;
for (const auto& [key, value] : map)
{
try
{
result[key.c_str()] = convert_any_to_py(value);
}
catch (const std::exception& e)
{
throw std::runtime_error(fmt::format("Error converting key: {}", e.what()));
}
}
return result;
}

template <typename Lyt>
void sidb_simulation_result(pybind11::module& m, const std::string& lattice = "")
{
namespace py = pybind11;
namespace py = pybind11;

py::class_<fiction::sidb_simulation_result<Lyt>>(m, fmt::format("sidb_simulation_result{}", lattice).c_str(),
DOC(fiction_sidb_simulation_result))
Expand All @@ -39,16 +97,17 @@ void sidb_simulation_result(pybind11::module& m, const std::string& lattice = ""
DOC(fiction_sidb_simulation_result_charge_distributions))
.def_readwrite("simulation_parameters", &fiction::sidb_simulation_result<Lyt>::simulation_parameters,
DOC(fiction_sidb_simulation_result_simulation_parameters))
.def_readwrite("additional_simulation_parameters",
&fiction::sidb_simulation_result<Lyt>::additional_simulation_parameters,
DOC(fiction_sidb_simulation_result_additional_simulation_parameters));
.def_property_readonly(
"additional_simulation_parameters", [](const fiction::sidb_simulation_result<Lyt>& self)
{ return convert_map_to_py(self.additional_simulation_parameters); },
DOC(fiction_sidb_simulation_result_additional_simulation_parameters));
}

} // namespace detail

inline void sidb_simulation_result(pybind11::module& m)
{
// NOTE be careful with the order of the following calls! Python will resolve the first matching overload!
// Define simulation result for specific lattices
detail::sidb_simulation_result<py_sidb_100_lattice>(m, "_100");
detail::sidb_simulation_result<py_sidb_111_lattice>(m, "_111");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18216,11 +18216,11 @@ Parameter ``lyt``:
The layout to simulate.

Parameter ``ps``:
Physical parameters. They are material-specific and may vary from
experiment to experiment.
QuickSim parameters.

Returns:
sidb_simulation_result is returned with all results.)doc";
`sidb_simulation_result` is returned if the simulation was
successful, otherwise `std::nullopt`.)doc";

static const char *__doc_fiction_quicksim_params = R"doc(This struct stores the parameters for the *QuickSim* algorithm.)doc";

Expand All @@ -18236,6 +18236,8 @@ the number of available hardware threads.)doc";

static const char *__doc_fiction_quicksim_params_simulation_parameters = R"doc(Simulation parameters for the simulation of the physical SiDB system.)doc";

static const char *__doc_fiction_quicksim_params_timeout = R"doc(Timeout limit (in ms).)doc";

static const char *__doc_fiction_random_coordinate =
R"doc(Generates a random coordinate within the region spanned by two given
coordinates. The two given coordinates form the top left corner and
Expand Down Expand Up @@ -19877,7 +19879,7 @@ R"doc(Default constructor. It only exists to allow for the use of

static const char *__doc_fiction_sidb_simulation_result_simulation_parameters = R"doc(Physical parameters used in the simulation.)doc";

static const char *__doc_fiction_sidb_simulation_result_simulation_runtime = R"doc(Total simulation runtime.)doc";
static const char *__doc_fiction_sidb_simulation_result_simulation_runtime = R"doc(Total simulation runtime in seconds.)doc";

static const char *__doc_fiction_sidb_skeleton_bestagon_library =
R"doc(This library contains SiDB I/O wires designed for both 1- and 2-input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ def test_perturber_and_sidb_pair_111(self):
self.assertEqual(groundstate[0].get_charge_state((2, 0)), sidb_charge_state.NEUTRAL)
self.assertEqual(groundstate[0].get_charge_state((3, 0)), sidb_charge_state.NEGATIVE)

# test timeout
params.timeout = 1
params.iteration_steps = 10000
params.number_threads = 1

# should return None since no solution can be found in 1 millisecond.
self.assertIsNone(quicksim(layout, params))


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,80 @@

class TestSiDBSimulationResult(unittest.TestCase):
def test_negative_and_neutral_layout_100_lattice(self):
# Use standard constructor.
result = sidb_simulation_result_100()

layout = sidb_100_lattice((2, 3))
layout.assign_cell_type((0, 1), sidb_technology.cell_type.NORMAL)
layout.assign_cell_type((0, 3), sidb_technology.cell_type.NORMAL)

cds_negative = charge_distribution_surface_100(layout)

cds_neutral = charge_distribution_surface_100(layout, sidb_simulation_parameters(), sidb_charge_state.NEUTRAL)

result.charge_distributions = [cds_negative, cds_neutral]

groundstate = groundstate_from_simulation_result(result)

self.assertEqual(len(groundstate), 1)
self.assertEqual(groundstate[0].get_charge_state((0, 1)), sidb_charge_state.NEUTRAL)
self.assertEqual(groundstate[0].get_charge_state((0, 3)), sidb_charge_state.NEUTRAL)
self.assertEqual(len(groundstate), 1, "Expected exactly one ground state.")
self.assertEqual(
groundstate[0].get_charge_state((0, 1)),
sidb_charge_state.NEUTRAL,
"Cell (0, 1) should have a NEUTRAL charge state.",
)
self.assertEqual(
groundstate[0].get_charge_state((0, 3)),
sidb_charge_state.NEUTRAL,
"Cell (0, 3) should have a NEUTRAL charge state.",
)

def test_negative_and_neutral_layout_111_lattice(self):
# Use standard constructor.
result = sidb_simulation_result_111()

layout = sidb_111_lattice((2, 3))
layout.assign_cell_type((0, 1), sidb_technology.cell_type.NORMAL)
layout.assign_cell_type((0, 3), sidb_technology.cell_type.NORMAL)

cds_negative = charge_distribution_surface_111(layout)

cds_neutral = charge_distribution_surface_111(layout, sidb_simulation_parameters(), sidb_charge_state.NEUTRAL)

result.charge_distributions = [cds_negative, cds_neutral]

groundstate = groundstate_from_simulation_result(result)

self.assertEqual(len(groundstate), 1, "Expected exactly one ground state.")
self.assertEqual(
groundstate[0].get_charge_state((0, 1)),
sidb_charge_state.NEUTRAL,
"Cell (0, 1) should have a NEUTRAL charge state.",
)
self.assertEqual(
groundstate[0].get_charge_state((0, 3)),
sidb_charge_state.NEUTRAL,
"Cell (0, 3) should have a NEUTRAL charge state.",
)

def test_empty_layout_100_lattice(self):
result = sidb_simulation_result_100()

layout = sidb_100_lattice((0, 0)) # Empty layout

cds_empty = charge_distribution_surface_100(layout)

result.charge_distributions = [cds_empty]
groundstate = groundstate_from_simulation_result(result)

self.assertEqual(len(groundstate), 1)

def test_empty_layout_111_lattice(self):
result = sidb_simulation_result_111()

layout = sidb_111_lattice((0, 0)) # Empty layout

cds_empty = charge_distribution_surface_111(layout)

result.charge_distributions = [cds_empty]
groundstate = groundstate_from_simulation_result(result)

self.assertEqual(len(groundstate), 1)
self.assertEqual(groundstate[0].get_charge_state((0, 1)), sidb_charge_state.NEUTRAL)
self.assertEqual(groundstate[0].get_charge_state((0, 3)), sidb_charge_state.NEUTRAL)


if __name__ == "__main__":
Expand Down
10 changes: 9 additions & 1 deletion cli/cmd/simulation/quicksim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,15 @@ class quicksim_command : public command
// To aid the compiler
if constexpr (fiction::has_sidb_technology_v<Lyt> && !fiction::is_charge_distribution_surface_v<Lyt>)
{
sim_result = fiction::quicksim(*lyt_ptr, qs_params);
if (const auto result = fiction::quicksim(*lyt_ptr, qs_params); result.has_value())
Drewniok marked this conversation as resolved.
Show resolved Hide resolved
{
sim_result = *result;
}
else
{
env->out() << fmt::format("[e] no stable charge distribution could be determined for '{}'\n",
get_name(lyt_ptr));
}

if constexpr (fiction::is_sidb_lattice_100_v<Lyt>)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,14 @@

// All physically valid charge configurations are determined for the given layout (probabilistic ground
// state simulation is used).
simulation_results = quicksim(layout, qs_params);
if (const auto result = quicksim(layout, qs_params); result.has_value())
{
simulation_results = result.value();
}
else
{
return;

Check warning on line 274 in include/fiction/algorithms/simulation/sidb/critical_temperature.hpp

View check run for this annotation

Codecov / codecov/patch

include/fiction/algorithms/simulation/sidb/critical_temperature.hpp#L274

Added line #L274 was not covered by tests
}
}
else
{
Expand Down Expand Up @@ -466,7 +473,12 @@

const quicksim_params qs_params{params.operational_params.simulation_parameters, params.iteration_steps,
params.alpha};
return quicksim(*bdl_iterator, qs_params);

if (const auto result = quicksim<Lyt>(*bdl_iterator, qs_params))
{
return result.value();
}
return sidb_simulation_result<Lyt>{}; // return empty result if no valid charge distribution was found
}

assert(false && "unsupported simulation engine");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,12 @@ class is_operational_impl

// perform QuickSim heuristic simulation
const quicksim_params qs_params{parameters.simulation_parameters, 500, 0.6};
return quicksim(*bdl_iterator, qs_params);

if (const auto qs_result = quicksim(*bdl_iterator, qs_params); qs_result.has_value())
{
return qs_result.value();
}
return sidb_simulation_result<Lyt>{}; // return empty result if no valid charge distribution was found
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,15 @@ class operational_domain_impl
{
// perform a heuristic simulation
const quicksim_params qs_params{simulation_parameters, 500, 0.6};
sim_results = quicksim(lyt, qs_params);

if (const auto result = quicksim(lyt, qs_params); result.has_value())
{
sim_results = result.value();
}
else
{
continue;
}
}
else
{
Expand Down
Loading
Loading