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..e247cdfd4e 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) -> auto& { + a += b; + return a; + }, + py::is_operator()) + .def(py::self - double()) + .def(double() - py::self) + .def( + "__isub__", + [](mio::UncertainValue& a, double b) -> auto& { + a -= b; + return a; + }, + py::is_operator()) + .def(py::self * double()) + .def(double() * py::self) + .def( + "__imul__", + [](mio::UncertainValue& a, double b) -> auto& { + a *= b; + return a; + }, + py::is_operator()) + .def(py::self / double()) + .def(double() / py::self) + .def( + "__itruediv__", + [](mio::UncertainValue& a, double b) -> auto& { + 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..d73567e91d 100644 --- a/pycode/memilio-simulation/tests/test_uncertain_value.py +++ b/pycode/memilio-simulation/tests/test_uncertain_value.py @@ -32,6 +32,46 @@ 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_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)