1313import puan
1414import puan .ndarray as pnd
1515import puan_rspy as pr
16- import dictdiffer
1716import more_itertools
1817from dataclasses import dataclass
1918from collections import Counter
@@ -107,10 +106,10 @@ def __init__(self, value: int, propositions: typing.List[typing.Union[str, puan.
107106 raise Exception (f"`sign` of AtLeast proposition must be either -1 or 1, got: { sign } " )
108107
109108
110- propositions_list = list (propositions )
111- if propositions is None or len (propositions_list ) == 0 :
109+ if propositions is None :
112110 raise Exception ("Sub propositions cannot be `None`" )
113111
112+ propositions_list = list (propositions )
114113 self .propositions = sorted (
115114 itertools .chain (
116115 filter (
@@ -1074,8 +1073,9 @@ def from_short(short: typing.Tuple[str, puan.Sign, typing.List[str], int, typing
10741073 def solve (
10751074 self ,
10761075 objectives : typing .List [typing .Dict [typing .Union [str , puan .variable ], int ]],
1077- solver : typing .Callable [[pnd .ge_polyhedron , typing .Dict [ str , int ]], typing .Iterable [typing .Tuple [np .ndarray , int , int ]]] = None ,
1076+ solver : typing .Callable [[pnd .ge_polyhedron , typing .Iterable [ np . ndarray ]], typing .Iterable [typing .Tuple [typing . Optional [ np .ndarray ], typing . Optional [ int ] , int ]]] = None ,
10781077 try_reduce_before : bool = False ,
1078+ include_virtual_variables : bool = False ,
10791079 ) -> itertools .starmap :
10801080
10811081 """
@@ -1109,6 +1109,9 @@ def solve(
11091109 If true, then methods will be applied to try and reduce size of this model before
11101110 running solve function.
11111111
1112+ include_virtual_variables : bool = False
1113+ If true, the virtual/artificial variables that has automatically been generated creating the model will be included in solutions
1114+
11121115 Examples
11131116 --------
11141117 >>> dummy_solver = lambda x,y: list(map(lambda v: (v, 0, 5), y))
@@ -1132,35 +1135,57 @@ def solve(
11321135 if solver is None :
11331136 pyrs_theory , variable_id_map = self ._to_pyrs_theory ()
11341137 id_map = dict (variable_id_map .values ())
1135- return list (
1136- itertools . starmap (
1137- lambda solution , objective_value , status_code : (
1138- dict (
1139- itertools . starmap (
1140- lambda k , v : ( id_map [ k ]. id , v ),
1141- solution . items ()
1142- )
1143- ),
1144- objective_value , status_code
1145- ),
1146- pyrs_theory . solve (
1147- list (
1148- map (
1149- lambda objective : dict (
1150- zip (
1151- map (
1152- lambda k : variable_id_map [ k ][ 0 ] ,
1153- objective ,
1138+ return itertools . starmap (
1139+ lambda solution , objective_value , status_code : (
1140+ dict (
1141+ itertools . starmap (
1142+ lambda k , v : ( id_map [ k ]. id , v ),
1143+ filter (
1144+ maz . ifttt (
1145+ # If is puan.variable
1146+ lambda x : issubclass ( id_map [ x [ 0 ]]. __class__ , puan . variable ),
1147+
1148+ # then include it
1149+ lambda _ : True ,
1150+
1151+ # else if variable id is generated
1152+ maz . ifttt (
1153+ maz . compose (
1154+ operator . attrgetter ( "generated_id" ),
1155+ id_map . get ,
1156+ operator . itemgetter ( 0 ),
11541157 ),
1155- objective .values (),
1158+
1159+ # then also check that we should include those variables
1160+ lambda _ : include_virtual_variables ,
1161+
1162+ # else include it
1163+ lambda _ : True
11561164 )
11571165 ),
1158- objectives ,
1166+ solution .items ()
1167+ )
1168+ )
1169+ ),
1170+ objective_value , status_code
1171+ ),
1172+ pyrs_theory .solve (
1173+ list (
1174+ map (
1175+ lambda objective : dict (
1176+ zip (
1177+ map (
1178+ lambda k : variable_id_map [k ][0 ],
1179+ objective ,
1180+ ),
1181+ objective .values (),
1182+ )
11591183 ),
1184+ objectives ,
11601185 ),
1161- False ,
11621186 ),
1163- )
1187+ False ,
1188+ ),
11641189 )
11651190 else :
11661191 polyhedron = self .to_ge_polyhedron (
@@ -1176,13 +1201,36 @@ def solve(
11761201 return itertools .starmap (
11771202 lambda solution , objective_value , status_code : (
11781203 dict (
1179- zip (
1180- map (
1181- operator .attrgetter ("id" ),
1182- polyhedron .A .variables
1183- ),
1184- solution
1185- )
1204+ map (
1205+ lambda x : (x [0 ].id , x [1 ]),
1206+ filter (
1207+ maz .ifttt (
1208+ # if variable is a puan.variable
1209+ lambda x : issubclass (x [0 ].__class__ , puan .variable ),
1210+
1211+ # then keep it
1212+ lambda _ : True ,
1213+
1214+ # else if variable id is generated
1215+ maz .ifttt (
1216+ maz .compose (
1217+ operator .attrgetter ("generated_id" ),
1218+ operator .itemgetter (0 ),
1219+ ),
1220+
1221+ # then check also that we should include those variables
1222+ lambda _ : include_virtual_variables ,
1223+
1224+ # else include it
1225+ lambda _ : True
1226+ )
1227+ ),
1228+ zip (
1229+ polyhedron .A .variables ,
1230+ solution
1231+ )
1232+ )
1233+ ),
11861234 ) if solution is not None else {},
11871235 objective_value , status_code
11881236 ),
@@ -1784,8 +1832,8 @@ def __init__(self, *propositions, variable: typing.Union[str, puan.variable] = N
17841832 variable = variable ,
17851833 )
17861834
1787- @staticmethod
1788- def from_json (data : dict , class_map ) -> "Xor" :
1835+ @classmethod
1836+ def from_json (cls , data : dict , class_map ) -> "Xor" :
17891837 """
17901838 Convert from JSON data to a proposition.
17911839
@@ -1794,13 +1842,13 @@ def from_json(data: dict, class_map) -> "Xor":
17941842 out : :class:`Xor`
17951843 """
17961844 propositions = data .get ('propositions' , [])
1797- return Xor (
1845+ return cls (
17981846 * map (functools .partial (from_json , class_map = class_map ), propositions ),
17991847 variable = data .get ('id' , None )
18001848 )
18011849
1802- @staticmethod
1803- def from_list (propositions : typing .List [typing .Union ["AtLeast" , puan .variable ]], variable : typing .Union [str , puan .variable ] = None ) -> "Xor" :
1850+ @classmethod
1851+ def from_list (cls , propositions : typing .List [typing .Union ["AtLeast" , puan .variable ]], variable : typing .Union [str , puan .variable ] = None ) -> "Xor" :
18041852
18051853 """
18061854 Convert from list of propositions to an object of this proposition class.
@@ -1816,7 +1864,7 @@ def from_list(propositions: typing.List[typing.Union["AtLeast", puan.variable]],
18161864 out : :class:`Xor`
18171865 """
18181866
1819- return Xor (* propositions , variable = variable )
1867+ return cls (* propositions , variable = variable )
18201868
18211869 def to_json (self ) -> typing .Dict [str , typing .Any ]:
18221870
@@ -1841,6 +1889,10 @@ def to_json(self) -> typing.Dict[str, typing.Any]:
18411889 return d
18421890
18431891
1892+ class ExactlyOne (Xor ):
1893+ pass
1894+
1895+
18441896class Not ():
18451897
18461898 """
@@ -1971,7 +2023,7 @@ def to_json(self) -> typing.Dict[str, typing.Any]:
19712023 d ['id' ] = self .id
19722024 return d
19732025
1974- def from_json (data : dict , class_map : list = [puan .variable ,AtLeast ,AtMost ,All ,Any ,Xor ,Not ,XNor ,Imply ]) -> typing .Any :
2026+ def from_json (data : dict , class_map : list = [puan .variable ,AtLeast ,AtMost ,All ,Any ,Xor ,ExactlyOne , Not ,XNor ,Imply ]) -> typing .Any :
19752027
19762028 """
19772029 Convert from json data to a proposition.
0 commit comments