From 146e7b806b63a17b2c01b05f513abf5c6dda0849 Mon Sep 17 00:00:00 2001 From: Kevin Chern Date: Wed, 10 Jul 2024 15:59:40 -0700 Subject: [PATCH 1/5] Make return type of maybe_equals consistently int --- dwave/optimization/model.pyx | 12 ++++++++---- dwave/optimization/symbols.pyx | 9 ++++++--- tests/test_symbols.py | 10 ++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/dwave/optimization/model.pyx b/dwave/optimization/model.pyx index 246257f9..f57913b1 100644 --- a/dwave/optimization/model.pyx +++ b/dwave/optimization/model.pyx @@ -1358,7 +1358,10 @@ cdef class Symbol: inc(it) def maybe_equals(self, other): - """Compare to another node. + """Compare to another node. This method exists because a complete equality test can be expensive. + A return value of ``0`` guarantees the two nodes are not equal. + A return value of ``1`` is indecisive and a more expensive, complete equality test is necessary. + A return value of ``2`` guarantees the two nodes are equal. Args: other: Another node in the model's directed acyclic graph. @@ -1682,11 +1685,12 @@ cdef class ArraySymbol(Symbol): 0 """ cdef Py_ssize_t maybe = super().maybe_equals(other) - if maybe != 1: - return True if maybe else False - cdef Py_ssize_t NOT = 0 cdef Py_ssize_t MAYBE = 1 + cdef Py_ssize_t DEFINITELY = 2 + + if maybe != 1: + return DEFINITELY if maybe else NOT if not isinstance(other, ArraySymbol): return NOT diff --git a/dwave/optimization/symbols.pyx b/dwave/optimization/symbols.pyx index 8369760b..6e58371c 100644 --- a/dwave/optimization/symbols.pyx +++ b/dwave/optimization/symbols.pyx @@ -858,13 +858,16 @@ cdef class Constant(ArraySymbol): def maybe_equals(self, other): cdef Py_ssize_t maybe = super().maybe_equals(other) - if maybe != 1: - return True if maybe else False + cdef Py_ssize_t NOT = 0 + cdef Py_ssize_t MAYBE = 1 + cdef Py_ssize_t DEFINITELY = 2 + if maybe != MAYBE: + return DEFINITELY if maybe else NOT # avoid NumPy deprecation warning by casting to bool. But also # `bool` in this namespace is a C++ class so we do an explicit if else equal = (np.asarray(self) == np.asarray(other)).all() - return True if equal else False + return DEFINITELY if equal else NOT def state(self, Py_ssize_t index=0, *, bool copy = True): """Return the state of the constant symbol. diff --git a/tests/test_symbols.py b/tests/test_symbols.py index e2cbc9bc..914d1975 100644 --- a/tests/test_symbols.py +++ b/tests/test_symbols.py @@ -56,17 +56,19 @@ def assertLessEqual(self, *args, **kwargs): ... def assertTrue(self, *args, **kwargs): ... def test_equality(self): + DEFINITELY = 2 for x in self.generate_symbols(): - self.assertTrue(x.maybe_equals(x)) + self.assertEqual(DEFINITELY, x.maybe_equals(x)) self.assertTrue(x.equals(x)) for x, y in zip(self.generate_symbols(), self.generate_symbols()): - self.assertTrue(x.maybe_equals(y)) + self.assertTrue(DEFINITELY, x.maybe_equals(y)) self.assertTrue(x.equals(y)) def test_inequality(self): + MAYBE = 1 for x, y in itertools.combinations(self.generate_symbols(), 2): - self.assertLessEqual(x.maybe_equals(y), 1) + self.assertLessEqual(x.maybe_equals(y), MAYBE) self.assertFalse(x.equals(y)) def test_iter_symbols(self): @@ -115,7 +117,7 @@ def test_state_serialization(self): states.append(sym.state()) else: states.append(None) - + with model.states.to_file() as f: new = Model.from_file(f) From 484193a56c0638b3a18bc9bdc2bf14e23110c270 Mon Sep 17 00:00:00 2001 From: Kevin Chern Date: Thu, 11 Jul 2024 08:49:22 -0700 Subject: [PATCH 2/5] Remove docstring for ArraySymbol.maybe_equals --- dwave/optimization/model.pyx | 47 ++++++++++++++---------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/dwave/optimization/model.pyx b/dwave/optimization/model.pyx index f57913b1..e4bf4b48 100644 --- a/dwave/optimization/model.pyx +++ b/dwave/optimization/model.pyx @@ -1359,9 +1359,6 @@ cdef class Symbol: def maybe_equals(self, other): """Compare to another node. This method exists because a complete equality test can be expensive. - A return value of ``0`` guarantees the two nodes are not equal. - A return value of ``1`` is indecisive and a more expensive, complete equality test is necessary. - A return value of ``2`` guarantees the two nodes are equal. Args: other: Another node in the model's directed acyclic graph. @@ -1369,9 +1366,24 @@ cdef class Symbol: Returns: integer Supported return values are: - * ``0``---Not equal. - * ``1``---Might be equal. - * ``2``---Are equal. + * ``0``---Not equal (with certainty) + * ``1``---Might be equal (no guarantees); a complete equality test is necessary + * ``2``---Are equal (with certainty) + + Examples: + This example compares + :class:`~dwave.optimization.symbols.IntegerVariable` symbols + of different sizes. + + >>> from dwave.optimization import Model + >>> model = Model() + >>> i = model.integer(3, lower_bound=0, upper_bound=20) + >>> j = model.integer(3, lower_bound=-10, upper_bound=10) + >>> k = model.integer(5, upper_bound=55) + >>> i.maybe_equals(j) + 1 + >>> i.maybe_equals(k) + 0 """ cdef Py_ssize_t NOT = 0 cdef Py_ssize_t MAYBE = 1 @@ -1661,29 +1673,6 @@ cdef class ArraySymbol(Symbol): return Max(self) def maybe_equals(self, other): - """Compare to another symbol. - - Args: - other: Another symbol in the model. - - Returns: - True if the two symbols might be equal. - - Examples: - This example compares - :class:`~dwave.optimization.symbols.IntegerVariable` symbols - of different sizes. - - >>> from dwave.optimization import Model - >>> model = Model() - >>> i = model.integer(3, lower_bound=0, upper_bound=20) - >>> j = model.integer(3, lower_bound=-10, upper_bound=10) - >>> k = model.integer(5, upper_bound=55) - >>> i.maybe_equals(j) - 1 - >>> i.maybe_equals(k) - 0 - """ cdef Py_ssize_t maybe = super().maybe_equals(other) cdef Py_ssize_t NOT = 0 cdef Py_ssize_t MAYBE = 1 From 96f7cddc5f024af1c95da4faea1d9c4c2ecb7942 Mon Sep 17 00:00:00 2001 From: Kevin Chern Date: Thu, 11 Jul 2024 08:56:14 -0700 Subject: [PATCH 3/5] Add release note for maybe_equals return type fix --- .../notes/maybe_equals-return-type-dd06bd6f8b2f31f3.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 releasenotes/notes/maybe_equals-return-type-dd06bd6f8b2f31f3.yaml diff --git a/releasenotes/notes/maybe_equals-return-type-dd06bd6f8b2f31f3.yaml b/releasenotes/notes/maybe_equals-return-type-dd06bd6f8b2f31f3.yaml new file mode 100644 index 00000000..92f80d94 --- /dev/null +++ b/releasenotes/notes/maybe_equals-return-type-dd06bd6f8b2f31f3.yaml @@ -0,0 +1,3 @@ +--- +fixes: + - Fix return type of ``Symbol.maybe_equals()`` to be integer instead of boolean. See `#23 `_. \ No newline at end of file From a3792473069a6f11bf96428a0ef42122f22fa85e Mon Sep 17 00:00:00 2001 From: Kevin Chern <32395608+kevinchern@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:39:05 -0700 Subject: [PATCH 4/5] Format doc string for maybe_equals Co-authored-by: Alexander Condello --- dwave/optimization/model.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwave/optimization/model.pyx b/dwave/optimization/model.pyx index e4bf4b48..6639e2df 100644 --- a/dwave/optimization/model.pyx +++ b/dwave/optimization/model.pyx @@ -1358,7 +1358,9 @@ cdef class Symbol: inc(it) def maybe_equals(self, other): - """Compare to another node. This method exists because a complete equality test can be expensive. + """Compare to another node. + + This method exists because a complete equality test can be expensive. Args: other: Another node in the model's directed acyclic graph. From 6e954c47aff031a4b688ff0a156c9d2754e15019 Mon Sep 17 00:00:00 2001 From: Kevin Chern <32395608+kevinchern@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:39:53 -0700 Subject: [PATCH 5/5] Note docstring for maybe_equals is inherited Co-authored-by: Alexander Condello --- dwave/optimization/model.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/dwave/optimization/model.pyx b/dwave/optimization/model.pyx index 6639e2df..2c951bf3 100644 --- a/dwave/optimization/model.pyx +++ b/dwave/optimization/model.pyx @@ -1675,6 +1675,7 @@ cdef class ArraySymbol(Symbol): return Max(self) def maybe_equals(self, other): + # note: docstring inherited from Symbol.maybe_equal() cdef Py_ssize_t maybe = super().maybe_equals(other) cdef Py_ssize_t NOT = 0 cdef Py_ssize_t MAYBE = 1