From dc623352740ecee1c36826853f0350a12f8e28eb Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:07:21 +0200 Subject: [PATCH 1/3] add operator overloads to UncertainValue bindings add test for new operators --- .../bindings/utils/uncertain_value.cpp | 41 ++++++++++++++++++- .../tests/test_uncertain_value.py | 27 ++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp index 1cfed9b268..cb94a86a12 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp @@ -52,7 +52,46 @@ void bind_uncertain_value(py::module_& m, std::string const& name) return self.get_distribution().get(); }, py::return_value_policy::reference_internal) - .def("draw_sample", &mio::UncertainValue::draw_sample); + .def("draw_sample", &mio::UncertainValue::draw_sample) + // operators: + // use lambdas for inplace operations, as in C++ they return the underlying type. adding inplace operators + // returning UncertainValue instead would cause ambiguities, as UncertainValue is implicitly castable to double + .def(py::self + double()) + .def(double() + py::self) + .def( + "__iadd__", + [](mio::UncertainValue& a, double b) { + a += b; + return a; + }, + py::is_operator()) + .def(py::self - double()) + .def(double() - py::self) + .def( + "__isub__", + [](mio::UncertainValue& a, double b) { + a -= b; + return a; + }, + py::is_operator()) + .def(py::self * double()) + .def(double() * py::self) + .def( + "__imul__", + [](mio::UncertainValue& a, double b) { + a *= b; + return a; + }, + py::is_operator()) + .def(py::self / double()) + .def(double() / py::self) + .def( + "__itruediv__", + [](mio::UncertainValue& a, double b) { + a /= b; + return a; + }, + py::is_operator()); } } // namespace pymio diff --git a/pycode/memilio-simulation/tests/test_uncertain_value.py b/pycode/memilio-simulation/tests/test_uncertain_value.py index 152b02f9ce..22d6b28955 100644 --- a/pycode/memilio-simulation/tests/test_uncertain_value.py +++ b/pycode/memilio-simulation/tests/test_uncertain_value.py @@ -32,6 +32,33 @@ def test_value(self): uv.value = 1.0 self.assertEqual(uv.value, 1.0) + def test_operators(self): + uv = mio.UncertainValue(2.0) + # addition + self.assertEqual(uv + 1.0, 3.0) + self.assertEqual(1.0 + uv, 3.0) + self.assertEqual(uv.value, 2.0) + uv += 1.0 + self.assertEqual(uv.value, 3.0) + # subtraction + self.assertEqual(uv - 1.0, 2.0) + self.assertEqual(1.0 - uv, -2.0) + self.assertEqual(uv.value, 3.0) + uv -= 1.0 + self.assertEqual(uv.value, 2.0) + # multiplication + self.assertEqual(uv * 3.0, 6.0) + self.assertEqual(3.0 * uv, 6.0) + self.assertEqual(uv.value, 2.0) + uv *= 3.0 + self.assertEqual(uv.value, 6.0) + # division + self.assertEqual(uv / 3.0, 2.0) + self.assertEqual(12.0 / uv, 2.0) + self.assertEqual(uv.value, 6.0) + uv /= 3.0 + self.assertEqual(uv.value, 2.0) + def test_distribution(self): """ """ uv = mio.UncertainValue(0) From 77b46189e6216d0644a0d058df7af9fefe99fdd6 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:38:31 +0200 Subject: [PATCH 2/3] ensure inplace operations return reference --- .../memilio/simulation/bindings/utils/uncertain_value.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp index cb94a86a12..e247cdfd4e 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/utils/uncertain_value.cpp @@ -60,7 +60,7 @@ void bind_uncertain_value(py::module_& m, std::string const& name) .def(double() + py::self) .def( "__iadd__", - [](mio::UncertainValue& a, double b) { + [](mio::UncertainValue& a, double b) -> auto& { a += b; return a; }, @@ -69,7 +69,7 @@ void bind_uncertain_value(py::module_& m, std::string const& name) .def(double() - py::self) .def( "__isub__", - [](mio::UncertainValue& a, double b) { + [](mio::UncertainValue& a, double b) -> auto& { a -= b; return a; }, @@ -78,7 +78,7 @@ void bind_uncertain_value(py::module_& m, std::string const& name) .def(double() * py::self) .def( "__imul__", - [](mio::UncertainValue& a, double b) { + [](mio::UncertainValue& a, double b) -> auto& { a *= b; return a; }, @@ -87,7 +87,7 @@ void bind_uncertain_value(py::module_& m, std::string const& name) .def(double() / py::self) .def( "__itruediv__", - [](mio::UncertainValue& a, double b) { + [](mio::UncertainValue& a, double b) -> auto& { a /= b; return a; }, From da6ee171de1cdf289d4f69df953209f363301da7 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:55:20 +0200 Subject: [PATCH 3/3] Update pycode/memilio-simulation/tests/test_uncertain_value.py Co-authored-by: Henrik Zunker <69154294+HenrZu@users.noreply.github.com> --- .../tests/test_uncertain_value.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pycode/memilio-simulation/tests/test_uncertain_value.py b/pycode/memilio-simulation/tests/test_uncertain_value.py index 22d6b28955..d73567e91d 100644 --- a/pycode/memilio-simulation/tests/test_uncertain_value.py +++ b/pycode/memilio-simulation/tests/test_uncertain_value.py @@ -59,6 +59,19 @@ def test_operators(self): uv /= 3.0 self.assertEqual(uv.value, 2.0) + def test_inplace_operators_keep_identity(self): + uv = mio.UncertainValue(2.0) + alias = uv + uv += 1.0 + self.assertIs(uv, alias) + uv -= 1.0 + self.assertIs(uv, alias) + uv *= 2.0 + self.assertIs(uv, alias) + uv /= 2.0 + self.assertIs(uv, alias) + self.assertEqual(alias.value, 2.0) + def test_distribution(self): """ """ uv = mio.UncertainValue(0)