Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion docs/source/reference/puan.logic.plog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)``.

Expand Down Expand Up @@ -44,6 +46,13 @@ AtMost
:undoc-members:
:show-inheritance:

Equal
++++++
.. autoclass:: Equal
:members:
:undoc-members:
:show-inheritance:

All
+++
.. autoclass:: All
Expand Down
76 changes: 75 additions & 1 deletion puan/logic/plog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,7 @@ def to_json(self) -> typing.Dict[str, typing.Any]:
d['value'] = -1*self.value
return d


class All(AtLeast):

"""
Expand Down Expand Up @@ -1561,6 +1562,79 @@ 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')
A: +(VAR658adc74c6913fb83b42c3968866f4bdb967fcad34692fc71d3de5f9a94b6970,VARe4568f4a0e4e55b7e3afa57b3527153272b53fde10f6e292b510a5c3bf797d3d)>=2
>>> Equal(2, list("xyz"), variable='A').propositions
[VAR658adc74c6913fb83b42c3968866f4bdb967fcad34692fc71d3de5f9a94b6970: -(x,y,z)>=-2, VARe4568f4a0e4e55b7e3afa57b3527153272b53fde10f6e292b510a5c3bf797d3d: +(x,y,z)>=2]
"""

Copy link
Collaborator

@ourmarina ourmarina Dec 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to add here that you added the methods to/from json?

    Methods
    -------
    from_json
    to_json

And: Do you need the method from_list?

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,
)

@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):

"""
Expand Down Expand Up @@ -2023,7 +2097,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.
Expand Down
1 change: 1 addition & 0 deletions puan/modules/configurator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ def from_json(data: dict) -> "StingyConfigurator":
pg.Not,
pg.XNor,
pg.Imply,
pg.Equal,
]
return StingyConfigurator(
*map(
Expand Down
13 changes: 13 additions & 0 deletions tests/test_puan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -144,6 +152,7 @@ def proposition_strategy():
xor_proposition_strategy(),
xnor_proposition_strategy(),
not_proposition_strategy(),
equal_proposition_strategy()
)

def cc_proposition_strategy():
Expand All @@ -158,6 +167,7 @@ def cc_proposition_strategy():
xor_cc_proposition_strategy(),
xnor_proposition_strategy(),
not_proposition_strategy(),
equal_proposition_strategy()
)

def propositions_strategy():
Expand Down Expand Up @@ -1320,6 +1330,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()
Copy link
Collaborator

@ourmarina ourmarina Dec 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is json_model not a json to begin with? Can this not be the same as line 1322 assert json_model == ...?




Expand Down