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