this repo has no description
1##@mainpage MiniZinc Python Interface 2# @author Tai Tran 3# @supervisor Guido Tack 4 5 6#@Things to add: 7# Improve the interface so that there is no need to assign MZN_STDLIB_DIR when starting python 8# args_ret_dict: some functions type checking are not written 9# Hide some internal functions such as flatten or type_presentation 10 11#@Suggestion: 12# Remove the user-defined name when declaring variable 13# Consider replacing it with i, where i is the number of arguments created 14# for example: calling [a,b,c] = m.Variable(1,100,3) 15 16''' 17@Usage: 18definition: 19 lb: lower bound, 20 ub: upper bound, 21 set:minizinc.Model.Set) 22 23 24* Declaration * 25 26minizinc.Model: 27 accepts 1 optional argument: 28 a string or list of strings indicating libraries to be included. 29 returns: 30 model to be used, for example: 31 m = minizinc.Model 32 i = m.Variable 33 s = m.Set 34 m.Constraint 35 m.satisfy 36 etc.. 37minizinc.Model.Set: 38 accepts multiple arguments: 39 item1, item2, ... itemn 40 item?: a number - single element 41 or a list/tuple of 2 elements - elements ranging from lb to ub 42 returns: 43 an object to be used 44 functions: 45 push - push more elements onto the set 46 clear - clear all elements in the set 47minizinc.Model.Variable: 48 accepts up to 2 arguments: 49 <None> : a boolean variable 50 ub : a variable ranging from 0 to ub - 1 51 lb, ub : a variable ranging from lb to ub 52 set : a variable based on the set 53minizinc.Model.Array: 54minizinc.Model.Construct: 55 56 57 58 59* Functions * 60minizinc.Model.satisfy 61minizinc.Model.next 62''' 63 64import sys 65import minizinc_internal 66import predicate 67import annotation 68#import inspect 69 70if sys.version < '3': 71 integer_types = (int, long, ) 72 python_types = (int, long, float, bool, str) 73 int_t = long 74 def longify(x): 75 return long(x) 76else: 77 integer_types = (int, ) 78 python_types = (int, float, bool, str) 79 int_t = int 80 def longify(x): 81 return x 82 83# turns a complex structure of items into a list of items 84def flatten(x): 85 result = [] 86 for el in x: 87 if isinstance(el, (list, tuple)): 88 result.extend(flatten(el)) 89 else: 90 result.append(el) 91 return result 92 93# nicely presents the type of a variable 94def type_presentation(x): 95 if x is None: 96 return "Any_Type" 97 if type(x) is type: 98 name = x.__name__ 99 if name[:9] == 'minizinc.': 100 name = name[9:] 101 return name 102 else: 103 if type(x) is tuple: 104 ret = '(' 105 for i, t in enumerate(x): 106 if i != 0: 107 ret += ', ' 108 ret += type_presentation(t) 109 ret += ')' 110 elif type(x) is list: 111 ret = 'array' + str(len(x)) + 'd of ' + type_presentation(x[0]) 112 else: 113 raise TypeError('Type Presentation: Unexpected type: ' + type(x)) 114 return ret 115 116 117 118# All MiniZinc objects derived from here 119class Expression(object): 120 def __init__(self, model = None): 121 self.model = model 122 123 def __str__(self): 124 return str(self.get_value()) 125 126 def is_decl(self): 127 return isinstance(self, Declaration) 128 129 def is_pre(self): 130 return isinstance(self, Predicate) 131 132 def evaluate(self, var): 133 if isinstance(var, Expression): 134 ret = var.get_value() 135 if ret == None: 136 raise ValueError("'" + var.name + "'Variable value is not set") 137 return ret 138 else: 139 return var 140 141 def eval_type(self, args): 142 if isinstance(args, Expression): 143 return args.type 144 elif type(args) in integer_types: 145 return int_t 146 elif type(args) in (float, bool, str): 147 return type(args) 148 elif type(args) is list: 149 t = None 150 for i in args: 151 if t is None: 152 t = self.eval_type(i) 153 else: 154 type_i = self.eval_type(i) 155 if t != type_i: 156 raise TypeError("Type of arguments in an array must be the same: expected: " + 157 type_presentation((t,)) + ", received: " + type_presentation((type_i,)) ) 158 return [t] 159 elif type(args) is tuple: 160 t = [] 161 for i in args: 162 t.append(self.eval_type(i)) 163 return tuple(t) 164 else: 165 raise TypeError("Unexpected Type: " + type_presentation(type(args)) ) 166 167 def __and__(self, pred): 168 return And( self, pred ) 169 170 def __rand__(self, pred): 171 return And( self, pred ) 172 173 def __or__(self, pred): 174 return Or( self, pred ) 175 176 def __xor__(self, pred): 177 return Xor( self, pred ) 178 179 def __ror__(self, pred): 180 return Or( self, pred ) 181 182 def __add__(self, pred): 183 return Add( self, pred ) 184 185 def __radd__(self, pred): 186 return Add( pred, self ) 187 188 def __sub__(self, pred): 189 return Sub( self, pred ) 190 191 def __rsub__(self, pred): 192 return Sub( pred, self ) 193 194 def __div__(self, pred): 195 return Div( self, pred ) 196 197 def __rdiv__(self, pred): 198 return Div( pred, self ) 199 200 def __floordiv__(self, pred): 201 return FloorDiv( self, pred ) 202 203 def __rfloordiv__(self, pred): 204 return FloorDiv( pred, self ) 205 206 def __mul__(self, pred): 207 return Mul( self, pred ) 208 209 def __rmul__(self, pred): 210 return Mul( self, pred ) 211 212 def __mod__(self, pred): 213 return Mod( self, pred ) 214 215 def __rmod__(self, pred): 216 return Mod( pred, self ) 217 218 def __pow__(self, pred): 219 return Pow( self, pred ) 220 221 def __rpow__(self, pred): 222 return Pow( pred, self ) 223 224 def __eq__(self, pred): 225 return Eq( self, pred ) 226 227 def __ne__(self, pred): 228 return Ne( self, pred ) 229 230 def __lt__(self, pred): 231 return Lt( self, pred ) 232 233 def __gt__(self, pred): 234 return Gt( self, pred ) 235 236 def __le__(self, pred): 237 return Le( self, pred ) 238 239 def __ge__(self, pred): 240 return Ge( self, pred ) 241 242 def __neg__(self): 243 return Neg( self ) 244 245 def __pos__(self): 246 return Pos( self ) 247 248 def __invert__(self): 249 return Invert( self ) 250 251 def __abs__(self): 252 return Abs( self ) 253 254 255# Temporary container for Expression before evaluating to minizinc_internal object 256# Calling Predicate.init automatically check: 257# if the arguments belong to the same model 258class Predicate(Expression): 259 def __init__(self, vars, args_and_return_type_tuple = None, name = None, model = None): 260 self.vars = vars 261 self.name = str(name) 262 263 def eval_model(args): 264 model = None 265 for val in args: 266 if isinstance(val, Expression): 267 if hasattr(val, 'model'): 268 if val.model is not None: 269 if model is None: 270 model = val.model 271 elif model != val.model: 272 raise TypeError("Arguments in the Predicate don't belong to the same model") 273 return model 274 275 self.vars_type = self.eval_type(vars) 276 self.model = eval_model(vars) 277 if args_and_return_type_tuple is not None: 278 for t in args_and_return_type_tuple: 279 def check_type(expected, actual): 280 if expected is None: 281 return True 282 if len(expected) != len(actual): 283 return False 284 for i, val in enumerate(expected): 285 if (val is None): 286 pass 287 elif type(val) is list and type(actual[i]) is list: 288 if check_type(val, actual[i]) == False: 289 return False 290 elif type(val) != type(actual[i]): 291 return False 292 return True 293 if check_type(t[0], self.vars_type): 294 #self.vars_type == t[0]: 295 self.type = t[1] 296 break 297 else: 298 s = "MiniZinc: function '" + self.name + "': Argument type does not match, expected: " 299 for i, t in enumerate(args_and_return_type_tuple): 300 if i != 0: 301 s += ' or ' 302 s += type_presentation(t[0]) 303 s += ', received: ' + type_presentation(self.vars_type) 304 raise TypeError(s) 305 else: 306 self.type = None 307 Expression.__init__(self) 308 309 310class BinOp(Predicate): 311 def __init__(self, vars, code, args_and_return_type_tuple, name): 312 Predicate.__init__(self,vars,args_and_return_type_tuple, name) 313 self.BinOpCode = code 314 315class UnOp(Predicate): 316 def __init__(self, vars, code, args_and_return_type_tuple, name): 317 Predicate.__init__(self,vars, args_and_return_type_tuple, name) 318 self.UnOpCode = code 319 320class Call(Predicate): 321 def __init__(self, vars, code, args_and_return_type_tuple = None, name = None, model_list = None): 322 Predicate.__init__(self,vars, args_and_return_type_tuple, name) 323 self.CallCode = code 324 self.model_list = model_list 325 326 327 328args_ret_dict = {} 329 330to_assign = [ ((int_t, int_t), int_t ), 331 ((float, float), float ), 332 ((int_t, float), float ), 333 ((float, int_t), float)] 334args_ret_dict['add'] = to_assign 335args_ret_dict['sub'] = to_assign 336args_ret_dict['mul'] = to_assign 337args_ret_dict['div'] = to_assign 338args_ret_dict['floordiv'] = to_assign 339args_ret_dict['pow'] = to_assign 340 341 342args_ret_dict['mod'] =[ ((int_t, int_t), int_t )] 343 344to_assign = [ ((int_t, int_t), bool ), 345 ((float, float), bool ), 346 ((bool, bool), bool ), 347 ((str, str), bool ), 348 ((minizinc_internal.VarSet, minizinc_internal.VarSet), bool)] 349args_ret_dict['lt'] = to_assign 350args_ret_dict['le'] = to_assign 351args_ret_dict['gt'] = to_assign 352args_ret_dict['ge'] = to_assign 353args_ret_dict['eq'] = to_assign 354args_ret_dict['ne'] = to_assign 355 356 357 358args_ret_dict['in'] = [ ((int_t, minizinc_internal.VarSet), bool) ] 359 360 361to_assign = [ ((bool, bool), bool) ] 362args_ret_dict['or'] = to_assign 363args_ret_dict['and'] = to_assign 364args_ret_dict['xor'] = to_assign 365 366args_ret_dict['abort'] = [ ((), bool)] 367to_assign = [ ((int_t,), int_t), 368 ((float,), float) ] 369args_ret_dict['abs'] = to_assign 370args_ret_dict['neg'] = to_assign 371args_ret_dict['pos'] = to_assign 372args_ret_dict['invert'] = to_assign 373 374args_ret_dict['array1d'] = [ (None, [int_t]) ] #the function has checked its argument already 375args_ret_dict['array2d'] = [ (None, [int_t, int_t]) ] 376args_ret_dict['array3d'] = [ (None, [int_t, int_t, int_t]) ] 377args_ret_dict['array4d'] = [ (None, [int_t, int_t, int_t, int_t])] 378args_ret_dict['array5d'] = [ (None, [int_t, int_t, int_t, int_t, int_t])] 379args_ret_dict['array6d'] = [ (None, [int_t, int_t, int_t, int_t, int_t, int_t])] 380 381to_assign = [ ((float,), float) ] 382args_ret_dict['sin'] = to_assign 383args_ret_dict['cos'] = to_assign 384args_ret_dict['tan'] = to_assign 385args_ret_dict['asin'] = to_assign 386args_ret_dict['acos'] = to_assign 387args_ret_dict['atan'] = to_assign 388args_ret_dict['sinh'] = to_assign 389args_ret_dict['cosh'] = to_assign 390args_ret_dict['tanh'] = to_assign 391args_ret_dict['asinh'] = to_assign 392args_ret_dict['acosh'] = to_assign 393args_ret_dict['atanh'] = to_assign 394args_ret_dict['sqrt'] = to_assign 395args_ret_dict['ln'] = to_assign 396args_ret_dict['log2'] = to_assign 397args_ret_dict['log10'] = to_assign 398args_ret_dict['exp'] = to_assign 399 400args_ret_dict['log'] = [ ((float, float), float) ] 401 402 403args_ret_dict['assert'] = [ ((bool, str), bool) ] 404args_ret_dict['bool2int'] = [ ((bool,), int_t) ] 405# XXX what to do with cardinality? 406args_ret_dict['card'] = None 407 408to_assign = [ ((float,), int_t) ] 409args_ret_dict['ceil'] = to_assign 410args_ret_dict['floor'] = to_assign 411args_ret_dict['round'] = to_assign 412 413to_assign = [ ((int_t,), float) ] 414args_ret_dict['int2float'] = to_assign 415# XXX consider remove 416args_ret_dict['concat'] = None 417 418 419to_assign = [ ((int_t, int_t), int_t), 420 ((float, float), float), 421 (([int_t],), int_t), 422 (([float],), float) ] 423args_ret_dict['min'] = to_assign 424args_ret_dict['max'] = to_assign 425 426to_assign = [ (([int_t], ), int_t), 427 (([float], ), float) ] 428args_ret_dict['product'] = to_assign 429args_ret_dict['sum'] = to_assign 430 431 432del to_assign 433 434 435 436class Add(BinOp): 437 def __init__(self, *vars): 438 BinOp.__init__(self, vars, 0, args_ret_dict['add'], '+') 439 440 def get_symbol(self): 441 return '+' 442 443 def get_value(self): 444 lhs = self.evaluate(self.vars[0]) 445 rhs = self.evaluate(self.vars[1]) 446 if isinstance(rhs, str): 447 lhs = str(lhs) 448 elif isinstance(lhs, str): 449 rhs = str(rhs) 450 return lhs + rhs 451 452class Sub(BinOp): 453 def __init__(self, *vars): 454 BinOp.__init__(self, vars, 1, args_ret_dict['sub'], '-') 455 456 def get_symbol(self): 457 return '-' 458 459 def get_value(self): 460 return self.evaluate(self.vars[0]) - self.evaluate(self.vars[1]) 461 462class Mul(BinOp): 463 464 def __init__(self, *vars): 465 BinOp.__init__(self, vars, 2, args_ret_dict['mul'], '*') 466 467 def get_symbol(self): 468 return '*' 469 470 def get_value(self): 471 return self.evaluate(self.vars[0]) * self.evaluate(self.vars[1]) 472 473class Div(BinOp): 474 475 def __init__(self, *vars) : 476 BinOp.__init__(self, vars, 3, args_ret_dict['div'], '/') 477 478 def get_symbol(self): 479 return '/' 480 481 def get_value(self): 482 return self.evaluate(self.vars[0]) / self.evaluate(self.vars[1]) 483 484class FloorDiv(BinOp): 485 486 def __init__(self, *vars) : 487 BinOp.__init__(self, vars, 4, args_ret_dict['floordiv'], '//') 488 489 def get_symbol(self): 490 return '//' 491 492 def get_value(self): 493 return self.evaluate(self.vars[0]) // self.evaluate(self.vars[1]) 494 495 496class Mod(BinOp): 497 498 def __init__(self, *vars): 499 BinOp.__init__(self, vars, 5, args_ret_dict['mod'], '%') 500 501 def get_symbol(self): 502 return '%' 503 504 def get_value(self): 505 return self.evaluate(self.vars[0]) % self.evaluate(self.vars[1]) 506 507 508class Lt(BinOp): 509 510 def __init__(self, *vars): 511 BinOp.__init__(self, vars, 6, args_ret_dict['lt'], '<') 512 513 def get_symbol(self): 514 return '<' 515 516 def get_value(self): 517 return self.evaluate(self.vars[0]) < self.evaluate(self.vars[1]) 518 519class Le(BinOp): 520 521 def __init__(self, *vars): 522 BinOp.__init__(self, vars, 7, args_ret_dict['le'], '<=') 523 524 def get_symbol(self): 525 return '<=' 526 527 def get_value(self): 528 return self.evaluate(self.vars[0]) <= self.evaluate(self.vars[1]) 529 530class Gt(BinOp): 531 532 def __init__(self, *vars): 533 BinOp.__init__(self, vars, 8, args_ret_dict['gt'], '>') 534 535 def get_symbol(self): 536 return '>' 537 538 def get_value(self): 539 return self.evaluate(self.vars[0]) > self.evaluate(self.vars[1]) 540 541class Ge(BinOp): 542 543 def __init__(self, *vars): 544 BinOp.__init__(self, vars, 9, args_ret_dict['ge'], '>=') 545 546 def get_symbol(self): 547 return '>=' 548 549 def get_value(self): 550 return self.evaluate(self.vars[0]) >= self.evaluate(self.vars[1]) 551 552 553class Eq(BinOp): 554 555 def __init__(self, *vars): 556 BinOp.__init__(self, vars, 10, args_ret_dict['eq'], '==') 557 558 def get_symbol(self): 559 return '==' 560 561 def get_value(self): 562 return self.evaluate(self.vars[0]) == self.evaluate(self.vars[1]) 563 564class Ne(BinOp): 565 566 def __init__(self, *vars): 567 BinOp.__init__(self, vars, 11, args_ret_dict['ne'], '!=') 568 569 def get_symbol(self): 570 return '!=' 571 572 def get_value(self): 573 return self.evaluate(self.vars[0]) != self.evaluate(self.vars[1]) 574 575class In(BinOp): 576 577 def __init__(self, *vars): 578 BinOp.__init__(self, vars, 12, args_ret_dict['in'], 'in') 579 580 def get_symbol(self): 581 return 'in' 582 583 def get_value(self): 584 return self.evaluate(self.vars[0]) in self.evaluate(self.vars[1]) 585 586 def __nonzero__(self): 587 return self 588 589 590class Or(BinOp): 591 592 def __init__(self, *vars): 593 BinOp.__init__(self, vars, 23, args_ret_dict['or'], 'or') 594 595 def get_symbol(self): 596 return 'or' 597 598 def get_value(self): 599 return self.evaluate(self.vars[0]) or self.evaluate(self.vars[1]) 600 601class And(BinOp): 602 603 def __init__(self, *vars): 604 BinOp.__init__(self, vars, 24, args_ret_dict['and'], 'and') 605 606 def get_symbol(self): 607 return '&' 608 609 def get_value(self): 610 return self.evaluate(self.vars[0]) & self.evaluate(self.vars[1]) 611 612class Xor(BinOp): 613 614 def __init__(self, *vars): 615 BinOp.__init__(self, vars, 25, args_ret_dict['xor'], 'xor') 616 617 def get_symbol(self): 618 return 'xor' 619 620 def get_value(self): 621 return bool(self.evaluate(self.vars[0])) != bool(self.evaluate(self.vars[1])) 622 623class Pow(Call): 624 def __init__(self, *vars): 625 Call.__init__(self, vars, "pow", args_ret_dict['pow'], '^') 626 627 def get_symbol(self): 628 return '**' 629 630 def get_value(self): 631 return self.evaluate(self.vars[0]) ** self.evaluate(self.vars[1]) 632 633class Abort(Call): 634 def __init__(self, *vars): 635 Call.__init__(self, vars, "abort", args_ret_dict['abort'], 'abort') 636 637class Abs(Call): 638 def __init__(self, *vars): 639 Call.__init__(self, vars, "abs", args_ret_dict['abs'], 'abs') 640 641 642class Array1d(Call): 643 def __init__(self, dim1, array): 644 Call.__init__(self,(dim1, array),"array1d", args_ret_dict['array1d'], 'array1d') 645 646class Array2d(Call): 647 def __init__(self, dim1, dim2, array): 648 Call.__init__(self, (dim1, dim2, array), "array2d", args_ret_dict['array2d'], 'array2d') 649 650class Array3d(Call): 651 def __init__(self, dim1, dim2, dim3, array): 652 Call.__init__(self,[dim1, dim2, dim3, array],"array3d", args_ret_dict['array3d'], 'array3d') 653 654class Array4d(Call): 655 def __init__(self, dim1, dim2, dim3, dim4, array): 656 Call.__init__(self,[dim1, dim2, dim3, dim4, array],"array4d", args_ret_dict['array4d'], 'array4d') 657 658class Array5d(Call): 659 def __init__(self, dim1, dim2, dim3, dim4, dim5, array): 660 Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, array],"array5d", args_ret_dict['array5d'], 'array5d') 661 662class Array6d(Call): 663 def __init__(self, dim1, dim2, dim3, dim4, dim5, dim6, array): 664 Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, dim6, array],"array6d", args_ret_dict['array6d'], 'array6d') 665 666class Acos(Call): 667 def __init__(self, var): 668 Call.__init__(self, (var,), "acos", args_ret_dict['acos'], 'acos') 669 670class Acosh(Call): 671 def __init__(self, var): 672 Call.__init__(self, (var,), "acosh", args_ret_dict['acosh'], 'acosh') 673 674class Asin(Call): 675 def __init__(self, var): 676 Call.__init__(self, (var,), "asin", args_ret_dict['asin'], 'asin') 677 678class Asinh(Call): 679 def __init__(self, var): 680 Call.__init__(self, (var,), "asinh", args_ret_dict['asinh'], 'asinh') 681 682class Atan(Call): 683 def __init__(self, var): 684 Call.__init__(self, (var,), "atan", args_ret_dict['atan'], 'atan') 685 686class Atanh(Call): 687 def __init__(self, var): 688 Call.__init__(self, (var,), "atanh", args_ret_dict['atanh'], 'atanh') 689 690class Assert(Call): 691 def __init__(self, constraint, message): 692 Call.__init__(self, (constraint, message), "assert", args_ret_dict['assert'], 'assert') 693 694class Bool2Int(Call): 695 def __init__(self, var): 696 Call.__init__(self, (var,), "bool2int", args_ret_dict['bool2int'], 'bool2int') 697 698class Card(Call): 699 def __init__(self, var): 700 Call.__init__(self, (var,), "card", args_ret_dict['card'], 'card') 701 702class Ceil(Call): 703 def __init__(self, var): 704 Call.__init__(self, (var,), "ceil", args_ret_dict['ceil'], 'ceil') 705 706class Concat(Call): 707 def __init__(self, var): 708 Call.__init__(self, (var,), "concat",) 709 710class Cos(Call): 711 def __init__(self, var): 712 Call.__init__(self, (var,), "cos", args_ret_dict['cos'], 'cos') 713 714class Cosh(Call): 715 def __init__(self, var): 716 Call.__init__(self, (var,), "cosh", args_ret_dict['cosh'], 'cosh') 717 718class Dom(Call): 719 def __init__(self, var): 720 Call.__init__(self,[var],"dom") 721 722class Dom_Array(Call): 723 def __init__(self, var): 724 Call.__init__(self,[var],"dom_array") 725 726class Dom_Size(Call): 727 def __init__(self, var): 728 Call.__init__(self,[var],"dom_size") 729 730class Fix(Call): 731 def __init__(self, var): 732 Call.__init__(self,[var],"fix") 733 734class Floor(Call): 735 def __init__(self, var): 736 Call.__init__(self, (var,),"floor", args_ret_dict['floor'], 'floor') 737 738class Exp(Call): 739 def __init__(self, var): 740 Call.__init__(self, (var,),"exp", args_ret_dict['exp'], 'exp') 741 742class Int2Float(Call): 743 def __init__(self, var): 744 Call.__init__(self, (var,),"int2float", args_ret_dict['int2float'], 'int2float') 745 746class Is_Fixed(Call): 747 def __init__(self, var): 748 Call.__init__(self,[var],"is_fixed") 749 750class Join(Call): 751 def __init__(self, var): 752 Call.__init__(self,[var],"join") 753 754class Lb(Call): 755 def __init__(self, var): 756 Call.__init__(self,[var],"lb") 757 758class Lb_array(Call): 759 def __init__(self, var): 760 Call.__init__(self,[var],"lb_array") 761 762class Length(Call): 763 def __init__(self, var): 764 Call.__init__(self,[var],"length") 765 766class Ln(Call): 767 def __init__(self, var): 768 Call.__init__(self, (var,),"ln", args_ret_dict['ln'], 'ln') 769 770class Log(Call): 771 def __init__(self, var1, var2): 772 Call.__init__(self, (var1, var2),"log", args_ret_dict['log'], 'log') 773 774class Log2(Call): 775 def __init__(self, var): 776 Call.__init__(self, (var,), "log2", args_ret_dict['log2'], 'log2') 777 778class Log10(Call): 779 def __init__(self, var): 780 Call.__init__(self, (var,), "log10", args_ret_dict['log10'], 'log10') 781 782class Min(Call): 783 def __init__(self, var): 784 Call.__init__(self, (var,), "min", args_ret_dict(['min']), 'min') 785 786class Max(Call): 787 def __init__(self, var): 788 Call.__init__(self, (var,), "max", args_ret_dict(['max']), 'max') 789 790class Product(Call): 791 def __init__(self, var): 792 Call.__init__(self, (var, ),"product", args_ret_dict['product'], 'product') 793 794class Round(Call): 795 def __init__(self, var): 796 Call.__init__(self, (var, ),"round", args_ret_dict['round'], 'round') 797 798class Set2array(Call): 799 def __init__(self, var): 800 Call.__init__(self,[var],"set2array") 801 802class Sin(Call): 803 def __init__(self, var): 804 Call.__init__(self, (var, ),"sin", args_ret_dict['sin'], 'sin') 805 806class Sinh(Call): 807 def __init__(self, var): 808 Call.__init__(self, (var, ),"sinh", args_ret_dict['sinh'], 'sinh') 809 810class Sqrt(Call): 811 def __init__(self, var): 812 Call.__init__(self, (var, ),"sqrt", args_ret_dict['sqrt'], 'sqrt') 813 814class Sum(Call): 815 def __init__(self, var): 816 Call.__init__(self, (var, ),"sum", args_ret_dict['sum'], 'sum') 817 818class Tan(Call): 819 def __init__(self, var): 820 Call.__init__(self, (var, ),"tan", args_ret_dict['tan'], 'tan') 821 822class Tanh(Call): 823 def __init__(self, var): 824 Call.__init__(self, (var, ),"tanh", args_ret_dict['tanh'], 'tanh') 825 826class Trace(Call): 827 def __init__(self, var): 828 Call.__init__(self,[var],"trace") 829 830class Ub(Call): 831 def __init__(self, var): 832 Call.__init__(self,[var],"ub") 833 834class Ub_Array(Call): 835 def __init__(self, var): 836 Call.__init__(self,[var],"ub_array") 837 838 839class Neg(UnOp): 840 def __init__(self, var): 841 UnOp.__init__(self, (var, ), 2, args_ret_dict['neg'], 'neg') 842 843class Pos(UnOp): 844 def __init__(self, var): 845 UnOp.__init__(self, (var, ), 1, args_ret_dict['pos'], 'pos') 846 847class Invert(Predicate): 848 def __init__(self, vars): 849 UnOp.__init__(self, (var, ), 0, args_ret_dict['invert'], 'invert') 850 851class Id(Expression): 852 def __init__(self, name, model_list = None): 853 self.name = name 854 self.model_list = model_list 855 self.type = minizinc_internal.Annotation 856 857 858# Temporary container for Variable Declaration 859class Declaration(Expression): 860 def __init__(self, model): 861 if not isinstance(model, Model): 862 raise TypeError('Warning: First argument must be a Model Object') 863 Expression.__init__(self, model) 864 self.name = '"' + str(id(self)) + '"' 865 self.solve_counter = -1 866 self.next_counter = -1 867 self.value = None 868 869 def get_value(self): 870 if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter: 871 return self.value 872 self.solve_counter = self.model.solve_counter 873 self.next_counter = self.model.next_counter 874 self.value = self.model.mznsolver.get_value(self.name) 875 return self.value 876 877 def __str__(self): 878 return str(self.get_value()) 879 880 def __repr__(self): 881 return 'A Minizinc ' + self.__class__.__name__ + ' with the value of ' + self.__str__() 882 883 884class Construct(Declaration): 885 def __init__(self, model, arg1): 886 Declaration.__init__(self, model) 887 self.type = self.eval_type(arg1) 888 self.has_minizinc_objects = False 889 890 def unwrap(arg): 891 if isinstance(arg, Expression): 892 self.has_minizinc_objects = True 893 return arg.obj 894 elif type(arg) in (list, tuple): 895 ret = [] 896 for val in arg: 897 ret.append(unwrap(val)) 898 return ret 899 elif type(arg) in python_types: 900 return arg 901 else: 902 raise TypeError('Unexpected type: argument should be a single/list/tuple of MiniZinc objects or Python basic types') 903 904 try: 905 self.obj = model.mznmodel.Declaration(self.name, unwrap(arg1)) 906 except: 907 print sys.exc_info()[0] 908 raise 909 910 if self.has_minizinc_objects: 911 self.value = arg1 912 else: 913 self.wrapped_value = arg1 914 self.class_name = 'Construct' 915 916 ''' 917 imagine we have: 918 a = Variable(1,100) undefined 919 b = Variable(1,100) undefined 920 921 c = Construct([a,b]) [ undefined, undefined ] 922 when the model is solved, c's value should be changed 923 ''' 924 925 def get_value(self): 926 if self.has_minizinc_objects: 927 def get_value_helper(arg): 928 if isinstance(arg, Expression): 929 return arg.get_value() 930 elif type(arg) is list: 931 ret = [] 932 for val in arg: 933 ret.append(get_value_helper(val)) 934 return ret 935 elif type(arg) in python_types: 936 return arg 937 else: 938 raise TypeError('Internal error: Unexpected type') 939 if not (self.solve_counter == self.model.solve_counter and self.next_counter == self.obj.next_counter): 940 self.value = get_value_helper(self.wrapped_value) 941 return self.value 942 943 944 945 946class Variable(Declaration): 947 def __init__(self, model, arg1=None, arg2=None): 948 Declaration.__init__(self, model) 949 lb, ub = False, True 950 if arg1 is not None: 951 typearg1 = type(arg1) 952 if arg2 is None: 953 if typearg1 is Set: 954 lb = arg1 955 ub = None 956 elif typearg1 in (list,tuple): 957 if len(arg1) != 2: 958 raise ValueError('Requires a list or tuple of exactly 2 numbers') 959 lb,ub = sorted(arg1)[0,1] 960 else: 961 ub = arg1 - 1 962 lb = typearg1(lb) 963 else: 964 lb,ub = arg1, arg2 965 966 typelb, typeub = type(lb), type(ub) 967 if not typelb is Set: 968 if typelb not in (bool, float, ) and typelb not in integer_types and not isinstance(typelb, Declaration): 969 raise TypeError('Lower bound must be a boolean, an int, a float or a set') 970 if typeub not in (bool, float, ) and typeub not in integer_types and not isinstance(typelb, Declaration): 971 raise TypeError('Upper bound must be a boolean, an int or a float') 972 if typelb != typeub: 973 raise TypeError('Upper bound an dlower bound is of different type') 974 if lb > ub: 975 raise ValueError('Lower bound cannot be greater than upper bound') 976 977 self.dim_list = [] 978 979 if typelb is bool: 980 self.obj = model.mznmodel.Declaration(self.name, 10, []) 981 self.type = bool 982 elif typelb in integer_types: 983 self.obj = model.mznmodel.Declaration(self.name, 9, [], lb, ub) 984 self.type = int_t 985 elif typelb is float: 986 self.obj = model.mznmodel.Declaration(self.name, 11, [], lb, ub) 987 self.type = float 988 elif typelb is Set: 989 self.obj = model.mznmodel.Declaration(self.name, 9, [], lb.obj) 990 self.type = int_t 991 else: 992 raise TypeError('Internal: Unexpected type') 993 994 self.class_name = 'Variable' 995 996class VariableConstruct(Variable, Construct): 997 def __init__(self, model, arg1, arg2 = None): 998 Construct.__init__(self, model, arg1, arg2) 999 1000class Array(Variable): 1001 def __init__(self, model, argopt1, argopt2, *args): 1002 Declaration.__init__(self, model) 1003 dim_list = [] 1004 lb = None 1005 ub = None 1006 1007 def add_to_dim_list(i): 1008 if type(i) is int: 1009 if i > 0: 1010 dim_list.append([0,i-1]) 1011 else: 1012 raise TypeError('Single value must be a positive integer') 1013 elif type(i) is list: 1014 if type(i[0]) in integer_types and type(i[-1]) in integer_types: 1015 dim_list.append([ i[0],i[-1]] ) 1016 else: 1017 raise TypeError('Range boundaries must be integers') 1018 elif isinstance(i, Set): 1019 if i.continuous(): 1020 dim_list.append([i.min(), i.max()]) 1021 else: 1022 raise TypeError('Array ranges must be continuous') 1023 else: 1024 raise TypeError('Unknown type') 1025 1026 if type(argopt1) is Set: 1027 lb = argopt1 1028 ub = None 1029 add_to_dim_list(argopt2) 1030 elif type(argopt1) is bool and type(argopt2) is bool: 1031 lb = argopt1 1032 ub = argopt2 1033 elif type(argopt2) not in integer_types: 1034 if type(argopt1) not in integer_types: 1035 raise TypeError('Range values must be integers') 1036 lb = 0 1037 ub = argopt1 - 1 1038 add_to_dim_list(argopt2) 1039 else: 1040 if type(argopt1) not in integer_types or type(argopt2) not in integer_types: 1041 raise TypeError('Lower bound and upper bound must be integers') 1042 lb = argopt1 1043 ub = argopt2 1044 1045 for i in args: 1046 add_to_dim_list(i) 1047 1048 self.lb = lb 1049 self.ub = ub 1050 if dim_list == []: 1051 raise AttributeError('Initialising an Array without dimension list') 1052 self.dim_list = dim_list 1053 tlb = type(argopt1) 1054 if tlb is bool: 1055 self.type = [bool] * len(dim_list) 1056 self.obj = model.mznmodel.Declaration(self.name,10,dim_list,lb,ub) 1057 elif tlb in integer_types: 1058 self.type = [int_t] * len(dim_list) 1059 self.obj = model.mznmodel.Declaration(self.name,9,dim_list, lb, ub) 1060 elif tlb is float: #isinstance(lb, float): 1061 self.type = [float] * len(dim_list) 1062 self.obj = model.mznmodel.Declaration(self.name,11,dim_list,lb,ub) 1063 elif tlb is Set: 1064 self.type = [int_t] * len(dim_list) 1065 self.obj = model.mznmodel.Declaration(self.name,9,dim_list,lb.obj) 1066 else: 1067 raise TypeError('Unexpected type') 1068 1069 self.class_name = 'Array' 1070 1071 def __getitem__(self, *args): 1072 return ArrayAccess(self.model, self, args[0]) 1073 1074 1075# XXX: to be rewritten later 1076class ArrayAccess(Array): 1077 def __init__(self, model, array, idx): 1078 Declaration.__init__(self, model) 1079 if type(idx) is not tuple: 1080 idx = [idx] 1081 else: 1082 idx = flatten(idx) 1083 if len(idx) != len(array.dim_list): 1084 raise IndexError('Requires exactly ' + str(len(self.dim_list)) + ' index values') 1085 for i, value in enumerate(idx): 1086 if not isinstance(value, Expression): 1087 if value < array.dim_list[i][0] or value > array.dim_list[i][1]: 1088 raise IndexError('Index at pos ' + str(i) + ' is out of range') 1089 1090 self.array = array 1091 self.idx = idx 1092 self.type = array.type[0] 1093 self.value = None 1094 self.class_name = 'Array Item' 1095 1096 def get_value(self): 1097 if hasattr(self, 'solve_counter'): 1098 if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter: 1099 return self.value 1100 self.solve_counter = self.model.solve_counter 1101 self.next_counter = self.model.next_counter 1102 arrayvalue = self.array.get_value() 1103 if arrayvalue is None: 1104 return None 1105 else: 1106 for i,value in enumerate(self.idx): 1107 arrayvalue = arrayvalue[value - self.array.dim_list[i][0]] 1108 self.value = arrayvalue 1109 return self.value 1110 return self.value 1111 1112class ArrayConstruct(Array, Construct): 1113 def __init__(self, model, arg1, arg2 = None): 1114 Construct.__init__(self, model, arg1, arg2) 1115 1116 1117class Set(Declaration): 1118 # Set is model independent and can be reused in multiple models 1119 # Thus, Set.model = None 1120 def __init__(self, *args): 1121 self.model = None 1122 self.name = '"' + str(id(self)) + '"' 1123 ''' 1124 lb, ub = None, None 1125 set_list = None 1126 1127 if argopt2 is not None: 1128 lb,ub = argopt1, argopt2 1129 else: 1130 if type(argopt1) is list: 1131 set_list = argopt1 1132 else: 1133 ub = argopt1 - 1 1134 lb = 0 1135 1136 if set_list is None: 1137 if not (type(lb) in integer_types and type(ub) in integer_types): 1138 raise TypeError('Lower bound and upper bound must be integers') 1139 set_list = [[lb, ub]] 1140 ''' 1141 self.obj = minizinc_internal.Set(*args) 1142 self.type = minizinc_internal.Set 1143 self.class_name = 'Set' 1144 1145 def push(self, *args): 1146 self.obj.push(*args) 1147 1148 def clear(self): 1149 self.obj.clear() 1150 1151 def continuous(self): 1152 return self.obj.continuous() 1153 1154 def min(self): 1155 return self.obj.min() 1156 1157 def max(self): 1158 return self.obj.max() 1159 1160 def get_value(self): 1161 return self.obj 1162 1163 def __iter__(self): 1164 return self.obj.__iter__() 1165 1166 def __contains__(self, val): 1167 return self.obj.contains(val) 1168 1169class VarSet(Variable): 1170 def __init__(self, model, argopt1, argopt2 = None): 1171 self.model = model 1172 self.name = '"' + str(id(self)) + '"' 1173 1174 lb, ub = None, None 1175 set_list = None 1176 if argopt2 is not None: 1177 lb,ub = argopt1, argopt2 1178 else: 1179 if type(argopt1) is list: 1180 set_list = argopt1 1181 elif type(argopt1) is Set: 1182 lb = argopt1.min() 1183 ub = argopt1.max() 1184 else: 1185 ub = argopt1 - 1 1186 lb = 0 1187 1188 if set_list is None: 1189 if not (type(lb) in integer_types and type(ub) in integer_types): 1190 raise TypeError('Lower bound and upper bound must be integers') 1191 1192 model.mznmodel.Declaration(self.name, 12, [], lb, ub) 1193 self.type = minizinc_internal.VarSet 1194 self.class_name = 'Var Set' 1195 1196 ''' Python automatically evaluate a __contains__ return object to boolean 1197 thus, we have to use separate In class instead of 1198 for i in VarSet 1199 def __contains__(self, argopt): 1200 return In([argopt, self]) 1201 ''' 1202 1203variable_model_dict = {} 1204function_model_dict = {} 1205name_model_dict = {} 1206def handlerFunctionClosure(name, args_list, model_list): 1207 def handlerFunction(*args): 1208 return Call(args, name, args_list, name, model_list) 1209 return handlerFunction 1210 1211def handlerFunction(name, model_list): 1212 return Id(name, model_list) 1213 1214def init(args = None, model = None): 1215 def assign_usable_names_to_model(name, model): 1216 if name in name_model_dict: 1217 if model is None: 1218 name_model_dict[name] = None 1219 else: 1220 name_model_dict[name].append(model) 1221 return False 1222 else: 1223 if model is None: 1224 name_model_dict[name] = None 1225 else: 1226 name_model_dict[name] = [model] 1227 return True 1228 1229 names = minizinc_internal.retrieveNames(args) 1230 for name, args_and_return_type_tuple in names["boolfuncs"].items(): 1231 if assign_usable_names_to_model(name, model): 1232 setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name])) 1233 1234 for name, args_and_return_type_tuple in names["annfuncs"].items(): 1235 if assign_usable_names_to_model(name, model): 1236 setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name])) 1237 1238 for name in names["annvars"]: 1239 if assign_usable_names_to_model(name, model): 1240 setattr(annotation, name, handlerFunction(name, name_model_dict[name])) 1241 1242init() 1243 1244class Model(object): 1245 def __init__(self, args = None): 1246 self.loaded = False 1247 self.mznsolver = None 1248 self.mznmodel = minizinc_internal.Model(args) 1249 if args: 1250 init(args, self) 1251 self.solve_counter = -1 1252 self.next_counter = -1 1253 1254 1255 ''' 1256 # not used anymore 1257 def get_name(self,var): 1258 itemList = [var_name for var_name, var_val in self.frame if var_val is var] 1259 if len(itemList) == 1: 1260 return itemList[0] 1261 elif len(itemList) == 0: 1262 raise LookupError('Internal Error: variable name not found') 1263 else: 1264 raise LookupError('The object pointed to was assigned to different names') 1265 ''' 1266 1267 def add_recursive(self, expr): 1268 if isinstance(expr, (tuple, list)): 1269 for i in expr: 1270 self.add_recursive(i) 1271 else: 1272 if issubclass(type(expr), Expression): 1273 if expr.is_pre(): 1274 self.mznmodel.Constraint(self.evaluate(expr)) 1275 else: 1276 raise(TypeError, "Unexpected Expression") 1277 else: 1278 self.mznmodel.Constraint(expr) 1279 1280 def Constraint(self, *expr): 1281 minizinc_internal.lock() 1282 if len(expr)>0: 1283 self.loaded = True 1284 #self.frame = inspect.currentframe().f_back.f_locals.items() 1285 self.add_recursive(expr) 1286 #del self.frame 1287 minizinc_internal.unlock() 1288 1289 def evaluate(self, expr): 1290 if not isinstance(expr, Expression): 1291 if isinstance(expr, (list, tuple)): 1292 for i,item in enumerate(expr): 1293 expr[i] = self.evaluate(item) 1294 return expr 1295 if isinstance(expr, Id): 1296 if expr.model_list is not None: 1297 if not self in expr.model_list: 1298 raise TypeError("The annotation '" + expr.name + "' does not belong to the model") 1299 return minizinc_internal.Id(expr.name) 1300 if isinstance(expr, Call): 1301 variables = [] 1302 model = None 1303 for i in expr.vars: 1304 variables.append(self.evaluate(i)) 1305 if expr.model_list is not None: 1306 if not self in expr.model_list: 1307 raise TypeError("The function '" + expr.name + "' does not belong to the model") 1308 return minizinc_internal.Call(expr.CallCode, variables, expr.type) 1309 elif isinstance(expr, ArrayAccess): 1310 for i,value in enumerate(expr.idx): 1311 if isinstance(value, Expression): 1312 expr.idx[i] = self.evaluate(value) 1313 return minizinc_internal.at(expr.array.obj, expr.idx) 1314 elif isinstance(expr, Declaration): 1315 if expr.model != self: 1316 raise TypeError("The Declaration not belongs to this model") 1317 return expr.obj 1318 elif isinstance(expr, BinOp): 1319 if expr.model is not None and expr.model != self: 1320 raise TypeError("The Declaration not belongs to this model") 1321 lhs = self.evaluate(expr.vars[0]) 1322 rhs = self.evaluate(expr.vars[1]) 1323 return minizinc_internal.BinOp(lhs, expr.BinOpCode, rhs) 1324 elif isinstance(expr, UnOp): 1325 if expr.model is not None and expr.model != self: 1326 raise TypeError("The Declaration not belongs to this model") 1327 rhs = self.evaluate(expr.vars[0]) 1328 return minizinc_internal.UnOp(expr.UnOpCode, rhs) 1329 else: 1330 raise TypeError('Variable Type unspecified') 1331 1332 1333 1334 def Variable(self, argopt1=None, argopt2=None): 1335 return Variable(self, argopt1, argopt2) 1336 1337 def Array(self, argopt1, argopt2, *args): 1338 list_ = [] 1339 for i in args: 1340 list_.append(i) 1341 return Array(self, argopt1, argopt2, *list_) 1342 1343 def Set(self, *args): 1344 return Set(*args) 1345 1346 def Range(self, arg1, arg2 = None): 1347 if (arg2 is None): 1348 return Set((0, arg1 - 1)) 1349 else: 1350 return Set((arg1, arg2)) 1351 1352 def VarSet(self, argopt1, argopt2 = None): 1353 return VarSet(self, argopt1, argopt2) 1354 1355 def Construct(self, argopt1): 1356 if isinstance(argopt1, list): 1357 return ArrayConstruct(self, argopt1) 1358 else: 1359 return VariableConstruct(self, argopt1) 1360 1361 def __solve(self, code, expr, ann, data, solver, time): 1362 minizinc_internal.lock() 1363 1364 if ann is not None: 1365 if not hasattr(ann, 'type') or ann.type != minizinc_internal.Annotation: 1366 raise TypeError('Unexpected type of annotation') 1367 eval_ann = self.evaluate(ann) 1368 eval_expr = self.evaluate(expr) 1369 1370 savedModel = self.mznmodel.copy() 1371 1372 # The model with declared variable cannot be deleted. 1373 self.mznmodel, savedModel = savedModel, self.mznmodel 1374 self.mznmodel.SolveItem(code, eval_ann, eval_expr) 1375 if data is not None: 1376 self.add_recursive(data) 1377 minizinc_internal.unlock() 1378 1379 self.mznsolver = self.mznmodel.solve(solver = solver, time = time) 1380 self.mznmodel = savedModel 1381 self.solve_counter = self.solve_counter + 1 1382 self.next_counter = -1 1383 1384 1385 def satisfy(self, ann = None, data = None, solver = 'gecode', time = 0): 1386 self.__solve(0, None, ann, data, solver, time) 1387 def maximize(self, expr, ann = None, data = None, solver = 'gecode', time = 0): 1388 self.__solve(2, expr, ann, data, solver, time) 1389 def minimize(self, expr, ann = None, data = None, solver = 'gecode', time = 0): 1390 self.__solve(1, expr, ann, data, solver, time) 1391 def reset(self): 1392 self.__init__() 1393 1394 def next(self): 1395 if self.mznsolver is None: 1396 raise ValueError('Model is not solved yet') 1397 self.status = self.mznsolver.next() 1398 self.next_counter = self.next_counter + 1 1399 return (self.status is None) 1400 1401 def is_loaded(self): 1402 return self.loaded 1403 1404 def is_solved(self): 1405 return self.mznsolver != None 1406 1407 def set_time_limit(self, time): 1408 self.mznmodel.set_time_limit(time) 1409 1410 def set_solver(self, solver): 1411 self.mznmodel.set_solver(solver) 1412 1413 def _debugprint(self): 1414 self.mznmodel.debugprint() 1415