From a9efb79cf8cba1e28c59a4c0976245a9d21b5869 Mon Sep 17 00:00:00 2001 From: Moa Stenmark Date: Mon, 19 Dec 2022 13:42:50 +0100 Subject: [PATCH 1/5] Adding Equal class to plog --- docs/source/reference/puan.logic.plog.rst | 11 +++++++- puan/logic/plog/__init__.py | 31 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/source/reference/puan.logic.plog.rst b/docs/source/reference/puan.logic.plog.rst index cefa4dc..6af34eb 100644 --- a/docs/source/reference/puan.logic.plog.rst +++ b/docs/source/reference/puan.logic.plog.rst @@ -13,7 +13,9 @@ Data types :class:`puan.logic.plog.AtLeast` : ``AtLeast`` is a compound proposition which takes propositions and represents a lower bound on the result of those propositions. For example, select at least one of x, y and z would be defined as ``AtLeast(propositions=["x","y","z"], value=1)`` and represented by the linear inequality :math:`x+y+z \ge 1`. - :class:`puan.logic.plog.AtMost` : ``AtMost`` is a compound proposition which takes propositions and represents a lower bound on the result of those propositions. For example, select at most two of x, y and z would be defined as ``AtMost(propositions=["x","y","z"], value=2)`` and represented by the linear inequality :math:`-x-y-z \ge -2`. + :class:`puan.logic.plog.AtMost` : ``AtMost`` is a compound proposition which takes propositions and represents an upper bound on the result of those propositions. For example, select at most two of x, y and z would be defined as ``AtMost(propositions=["x","y","z"], value=2)`` and represented by the linear inequality :math:`-x-y-z \ge -2`. + + :class:`puan.logic.plog.Equal` : ``Equal`` is a compound proposition which takes propositions and represents an exact bound on the result of those propositions. For example, select propositions equal to two of x, y and z would be defined as ``Equal(propositions=["x","y","z"], value=2)`` and represented by the linear inequality :math:`x+y+z = -2`. :class:`puan.logic.plog.All` : ``All`` is a compound proposition representing a conjunction of all given propositions. ``All`` is represented by an ``AtLeast`` proposition with value set to the number of given propositions. For example, ``All("x","y","z")`` is equivalent to ``AtLeast(propositions=["x","y","z"], value=3)``. @@ -44,6 +46,13 @@ AtMost :undoc-members: :show-inheritance: +Equal +++++++ +.. autoclass:: Equal + :members: + :undoc-members: + :show-inheritance: + All +++ .. autoclass:: All diff --git a/puan/logic/plog/__init__.py b/puan/logic/plog/__init__.py index 29635fd..bbf9343 100644 --- a/puan/logic/plog/__init__.py +++ b/puan/logic/plog/__init__.py @@ -1476,6 +1476,7 @@ def to_json(self) -> typing.Dict[str, typing.Any]: d['value'] = -1*self.value return d + class All(AtLeast): """ @@ -1561,6 +1562,36 @@ def to_json(self) -> typing.Dict[str, typing.Any]: ) return d +class Equal(All): + + """ + ``Equal`` proposition is a combination of an :class:`AtLeast` proposition and an :class:`AtMost` proposition + (e.g. :math:`x+y+z-1 = 0`). Sub propositions may take on any value given by their equation bounds + + Parameters + ---------- + value : integer value constraint constant - right hand side of the equality + propositions : a list of :class:`puan.Proposition` instances or ``str`` + variable : variable connected to this proposition + + Notes + ----- + - Propositions may be of type ``str``, :class:`puan.variable` or :class:`AtLeast` (or other inheriting :class:`AtLeast`) + - Propositions list cannot be empty. + + Examples + -------- + Meaning exactly two of x, y and z. + >>> Equal(2, list("xyz"), variable='A').propositions + [A: +(x,y,z)>=2, A: -(x,y,z)>=-2] + """ + + def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.variable]], variable: typing.Union[str, puan.variable] = None): + super().__init__( + AtLeast(value=value, propositions=propositions, variable=variable), + AtMost(value=value, propositions=propositions, variable=variable)) + + class Any(AtLeast): """ From bb3adc838c39eaab5544c2b502ffdee9623dfada Mon Sep 17 00:00:00 2001 From: ourmoa <100134345+ourmoa@users.noreply.github.com> Date: Tue, 27 Dec 2022 10:50:48 +0100 Subject: [PATCH 2/5] Update puan/logic/plog/__init__.py Co-authored-by: ourmarina <121033780+ourmarina@users.noreply.github.com> --- puan/logic/plog/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puan/logic/plog/__init__.py b/puan/logic/plog/__init__.py index bbf9343..53b298e 100644 --- a/puan/logic/plog/__init__.py +++ b/puan/logic/plog/__init__.py @@ -1588,7 +1588,7 @@ class Equal(All): def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.variable]], variable: typing.Union[str, puan.variable] = None): super().__init__( - AtLeast(value=value, propositions=propositions, variable=variable), + AtLeast(value=value, propositions=propositions), AtMost(value=value, propositions=propositions, variable=variable)) From 05290cc116d33b847b952ce4869985daf4a99515 Mon Sep 17 00:00:00 2001 From: ourmoa <100134345+ourmoa@users.noreply.github.com> Date: Tue, 27 Dec 2022 10:51:01 +0100 Subject: [PATCH 3/5] Update puan/logic/plog/__init__.py Co-authored-by: ourmarina <121033780+ourmarina@users.noreply.github.com> --- puan/logic/plog/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puan/logic/plog/__init__.py b/puan/logic/plog/__init__.py index 53b298e..4eafb8a 100644 --- a/puan/logic/plog/__init__.py +++ b/puan/logic/plog/__init__.py @@ -1589,7 +1589,7 @@ class Equal(All): def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.variable]], variable: typing.Union[str, puan.variable] = None): super().__init__( AtLeast(value=value, propositions=propositions), - AtMost(value=value, propositions=propositions, variable=variable)) + AtMost(value=value, propositions=propositions), variable=variable) class Any(AtLeast): From 1af69666ff296a004cdd38cff0b4e8a8c822f6e8 Mon Sep 17 00:00:00 2001 From: Moa Stenmark Date: Tue, 27 Dec 2022 13:03:56 +0100 Subject: [PATCH 4/5] Review changes --- puan/logic/plog/__init__.py | 45 +++++++++++++++++++++++++++++++++++-- tests/test_puan.py | 3 +++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/puan/logic/plog/__init__.py b/puan/logic/plog/__init__.py index 4eafb8a..412223b 100644 --- a/puan/logic/plog/__init__.py +++ b/puan/logic/plog/__init__.py @@ -1582,8 +1582,10 @@ class Equal(All): Examples -------- Meaning exactly two of x, y and z. + >>> Equal(2, list("xyz"), variable='A') + A: +(VAR658adc74c6913fb83b42c3968866f4bdb967fcad34692fc71d3de5f9a94b6970,VARe4568f4a0e4e55b7e3afa57b3527153272b53fde10f6e292b510a5c3bf797d3d)>=2 >>> Equal(2, list("xyz"), variable='A').propositions - [A: +(x,y,z)>=2, A: -(x,y,z)>=-2] + [VAR658adc74c6913fb83b42c3968866f4bdb967fcad34692fc71d3de5f9a94b6970: -(x,y,z)>=-2, VARe4568f4a0e4e55b7e3afa57b3527153272b53fde10f6e292b510a5c3bf797d3d: +(x,y,z)>=2] """ def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.variable]], variable: typing.Union[str, puan.variable] = None): @@ -1591,6 +1593,45 @@ def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan. AtLeast(value=value, propositions=propositions), AtMost(value=value, propositions=propositions), variable=variable) + @staticmethod + def from_json(data: dict, class_map) -> "Equal": + """ + Convert from JSON data to a proposition. + + Returns + ------- + out : :class:`Equal` + """ + propositions = data.get('propositions', []) + return Equal( + value=data.get('value', 1), + propositions=list(map(functools.partial(from_json, class_map=class_map), propositions)), + variable=data.get('id', None) + ) + + def to_json(self) -> typing.Dict[str, typing.Any]: + + """ + Returns proposition as a readable JSON. + + Returns + ------- + out : Dict[str, Any] + """ + d = { + 'type': self.__class__.__name__, + 'value': self.propositions[0].sign*self.propositions[0].value, + 'propositions': list( + map( + operator.methodcaller("to_json"), + self.propositions[0].propositions + ) + ) if len(self.propositions) > 0 else [], + } + if not self.generated_id: + d['id'] = self.id + return d + class Any(AtLeast): @@ -2054,7 +2095,7 @@ def to_json(self) -> typing.Dict[str, typing.Any]: d['id'] = self.id return d -def from_json(data: dict, class_map: list = [puan.variable,AtLeast,AtMost,All,Any,Xor,ExactlyOne,Not,XNor,Imply]) -> typing.Any: +def from_json(data: dict, class_map: list = [puan.variable,AtLeast,AtMost,Equal,All,Any,Xor,ExactlyOne,Not,XNor,Imply]) -> typing.Any: """ Convert from json data to a proposition. diff --git a/tests/test_puan.py b/tests/test_puan.py index 2f0a17a..96a547d 100644 --- a/tests/test_puan.py +++ b/tests/test_puan.py @@ -1320,6 +1320,9 @@ def test_json_conversion(): json_model = {"type": "AtMost", "value": 1, "propositions": [{"id": "x", "bounds": {"lower": -10, "upper": 10}}]} assert json_model == pg.AtMost.from_json(json_model, [puan.variable]).to_json() + + json_model = {"type": "Equal", "value": 1, "id": "A", "propositions": [{"id": "x", "bounds": {"lower": -10, "upper": 10}}]} + assert pg.from_json(json_model).to_json() == pg.Equal.from_json(json_model, [puan.variable]).to_json() From 7664da4df60a5e768a4094eaa19c831024f099a6 Mon Sep 17 00:00:00 2001 From: Rikard Olsson Date: Thu, 19 Jan 2023 15:13:11 +0100 Subject: [PATCH 5/5] Set Equal to hyp testing --- puan/logic/plog/__init__.py | 4 +++- puan/modules/configurator/__init__.py | 1 + tests/test_puan.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/puan/logic/plog/__init__.py b/puan/logic/plog/__init__.py index 412223b..b201f47 100644 --- a/puan/logic/plog/__init__.py +++ b/puan/logic/plog/__init__.py @@ -1591,7 +1591,9 @@ class Equal(All): def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.variable]], variable: typing.Union[str, puan.variable] = None): super().__init__( AtLeast(value=value, propositions=propositions), - AtMost(value=value, propositions=propositions), variable=variable) + AtMost(value=value, propositions=propositions), + variable=variable, + ) @staticmethod def from_json(data: dict, class_map) -> "Equal": diff --git a/puan/modules/configurator/__init__.py b/puan/modules/configurator/__init__.py index 60010a7..690b104 100644 --- a/puan/modules/configurator/__init__.py +++ b/puan/modules/configurator/__init__.py @@ -380,6 +380,7 @@ def from_json(data: dict) -> "StingyConfigurator": pg.Not, pg.XNor, pg.Imply, + pg.Equal, ] return StingyConfigurator( *map( diff --git a/tests/test_puan.py b/tests/test_puan.py index 96a547d..9c4388b 100644 --- a/tests/test_puan.py +++ b/tests/test_puan.py @@ -134,6 +134,14 @@ def not_proposition_strategy(): atom_proposition_strategy(), ) +def equal_proposition_strategy(): + return strategies.builds( + pg.Equal, + propositions=atoms_propositions_strategy(), + variable=variable_boolean_proposition_strategy(), + value=strategies.integers(min_value=-5, max_value=5), + ) + def proposition_strategy(): return strategies.one_of( atleast_proposition_strategy(), @@ -144,6 +152,7 @@ def proposition_strategy(): xor_proposition_strategy(), xnor_proposition_strategy(), not_proposition_strategy(), + equal_proposition_strategy() ) def cc_proposition_strategy(): @@ -158,6 +167,7 @@ def cc_proposition_strategy(): xor_cc_proposition_strategy(), xnor_proposition_strategy(), not_proposition_strategy(), + equal_proposition_strategy() ) def propositions_strategy():