@@ -196,7 +196,8 @@ def flatten(self) -> list:
196196
197197 Examples
198198 --------
199- >>> proposition = AtLeast(1, [AtLeast(1, ["a", "b"], "B"), AtLeast(1, ["c", "d"], "C"), "e"], "A")
199+ >>> proposition = AtLeast(1, [AtLeast(1, ["a", "b"], "B"),
200+ ... AtLeast(1, ["c", "d"], "C"), "e"], "A")
200201 >>> proposition.flatten()
201202 [A: +(B,C,e)>=1, B: +(a,b)>=1, C: +(c,d)>=1, variable(id='a', bounds=Bounds(lower=0, upper=1)), variable(id='b', bounds=Bounds(lower=0, upper=1)), variable(id='c', bounds=Bounds(lower=0, upper=1)), variable(id='d', bounds=Bounds(lower=0, upper=1)), variable(id='e', bounds=Bounds(lower=0, upper=1))]
202203 """
@@ -515,6 +516,7 @@ def is_contradiction(self) -> bool:
515516 >>> model = AtMost(-1,["x","y"])
516517 >>> model.is_contradiction
517518 True
519+
518520
519521 Returns
520522 -------
@@ -523,60 +525,118 @@ def is_contradiction(self) -> bool:
523525 # When the highest sum from equation still not satisfied inequality, this is a contradition
524526 return self .equation_bounds [1 ] < 0
525527
526- def evaluate (self , interpretation : typing .List [ puan .SolutionVariable ]) -> bool :
528+ def evaluate (self , interpretation : typing .Dict [ typing . Union [ str , puan .variable ], int ]) -> bool :
527529
528530 """
529531 Evaluates interpretation on this model. It will evaluate sub propositions
530532 bottoms-up and propagate results upwards. This means that even though
531533 intermediate variables are not set in interpretation, they receive a value
532534 based on the evaluation of its propositions.
533535
536+ Parameters
537+ ----------
538+ interpretation: typing.Dict[typing.Union[str, puan.variable], int]
539+ the values of the variables in the model to evaluate it for
540+
534541 Examples
535542 --------
536- >>> All(*"xy", variable="A").evaluate([puan.SolutionVariable( "x", value=1)] )
543+ >>> All(*"xy", variable="A").evaluate({ "x": 1} )
537544 False
538545
539- >>> All(*"xy", variable="A").evaluate([puan.SolutionVariable("x", value=1), puan.SolutionVariable("y", value=1)])
546+ >>> All(*"xy", variable="A").evaluate(
547+ ... {puan.variable("x"): 1, puan.variable("y"): 1})
540548 True
541549
542- >>> AtLeast(propositions=[puan.variable("x", dtype="int")], value=10).evaluate([puan.SolutionVariable("x", value=9)])
550+ >>> AtLeast(propositions=[puan.variable("x", dtype="int")],
551+ ... value=10).evaluate({puan.variable("x"): 9})
543552 False
544553
545- >>> AtLeast(propositions=[puan.variable("x", dtype="int")], value=10).evaluate([puan.SolutionVariable("x", value=10)])
554+ >>> AtLeast(propositions=[puan.variable("x", dtype="int")],
555+ ... value=10).evaluate({puan.variable("x"): 10})
546556 True
557+
558+ See also
559+ --------
560+ evaluate_propositions : Evaluates propositions on this model given a dict with variables and their values.
561+
562+ Returns
563+ -------
564+ bool
547565 """
548566
549- interpretation_map = dict (
550- zip (
551- map (
552- operator .attrgetter ("id" ),
553- interpretation
554- ),
555- map (
556- operator .attrgetter ("value" ),
557- interpretation
558- ),
559- )
560- )
567+ return self .evaluate_propositions (interpretation )[self .variable .id ] > 0
561568
562- return (
563- sum (
564- map (
565- lambda x : interpretation_map .get (x .id , 0 )* self .sign if not type (x ) == bool else x * 1 ,
566- itertools .chain (
567- self .atomic_propositions ,
568- map (
569- operator .methodcaller (
570- "evaluate" ,
571- interpretation = interpretation ,
572- ),
573- self .compound_propositions
569+ def evaluate_propositions (self , interpretation : typing .Dict [typing .Union [str , puan .variable ], int ]) -> typing .Dict [typing .Union [str , puan .variable ], int ]:
570+ """
571+ Evaluates propositions on this model given a dict with variables and their values.
572+
573+ Parameters
574+ ----------
575+ interpretation: typing.Dict[typing.Union[str, puan.variable]
576+ the values of the variables in the model to evaluate it for
577+
578+ Notes
579+ -----
580+ Bounds and dtypes of puan.variables in the interpretation are neglected, those values are only considered for the variables of the model.
581+
582+ A variable which is not included in the initial dict is calculated from its sub propositions or
583+ defaulted to its lower bound (if variable doesn't have any subpropositions).
584+
585+ Examples
586+ --------
587+ >>> All(*"xy", variable="A").evaluate_propositions({"x": 1})
588+ {'x': 1, 'y': 0, 'A': 0}
589+
590+ >>> All(*"xy", variable="A").evaluate_propositions({"x": 1, "y": 1})
591+ {'x': 1, 'y': 1, 'A': 1}
592+
593+ >>> AtLeast(propositions=[puan.variable("x", dtype="int")],
594+ ... value=10).evaluate_propositions({"x": 9})
595+ {'x': 9, 'VARa6aef82726db5033e4b25e6fec8b5770cf89fe44ff8336731eef2bfa9a8ab35f': 0}
596+
597+ >>> AtLeast(propositions=[puan.variable("x", dtype="int")],
598+ ... value=10).evaluate_propositions({"x": 10})
599+ {'x': 10, 'VARa6aef82726db5033e4b25e6fec8b5770cf89fe44ff8336731eef2bfa9a8ab35f': 1}
600+
601+ See also
602+ --------
603+ evaluate : Evaluates interpretation on this model.
604+
605+ Returns
606+ -------
607+ out : typing.Dict[typing.Union[str, puan.variable], int]
608+ """
609+ def _check_variable_in_bounds (id , val , bounds ):
610+ if val not in range (bounds .lower , bounds .upper + 1 ):
611+ raise ValueError ("Variable {} is out of bounds, value: {}, bounds: {}" .format (id , val , bounds ))
612+ def _get_variable_val (variable , interpretation ):
613+ if not variable in interpretation .keys ():
614+ interpretation [variable .id ] = variable .bounds .lower
615+ return interpretation
616+ def _evaluate_propositions (prop , interpretation ):
617+ if not prop .variable .id in interpretation .keys ():
618+ # Calculate variable value and add to dict
619+ val = sum (
620+ itertools .chain (
621+ map (
622+ lambda x : _evaluate_propositions (x , interpretation )[x .id ]* prop .sign ,
623+ prop .compound_propositions
624+ ),
625+ map (
626+ lambda x : _get_variable_val (x , interpretation )[x .id ]* prop .sign ,
627+ prop .atomic_propositions
628+ )
629+ )
574630 )
631+ _check_variable_in_bounds (prop .variable .id , 1 * (val - prop .value >= 0 ), prop .variable .bounds )
632+ interpretation [prop .variable .id ] = 1 * (
633+ val - prop .value >= 0
575634 )
576- )
577- )- self .value
578- ) >= 0
579-
635+ return interpretation
636+ interpretation_map = dict (map (lambda x : (x .id , x ), filter (lambda x : type (x ) == puan .variable , self .flatten ())))
637+ for x , y in filter (lambda x : x [0 ] in interpretation_map , interpretation .items ()):
638+ _check_variable_in_bounds (x , y , interpretation_map [x ].bounds )
639+ return _evaluate_propositions (self , interpretation )
580640
581641 def to_short (self ) -> tuple :
582642
0 commit comments