1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

1416 lines
37 KiB
Python

##@mainpage MiniZinc Python Interface
# @author Tai Tran
# @supervisor Guido Tack
#@Things to add:
# Improve the interface so that there is no need to assign MZN_STDLIB_DIR when starting python
# args_ret_dict: some functions type checking are not written
# Hide some internal functions such as flatten or type_presentation
#@Suggestion:
# Remove the user-defined name when declaring variable
# Consider replacing it with i, where i is the number of arguments created
# for example: calling [a,b,c] = m.Variable(1,100,3)
'''
@Usage:
definition:
lb: lower bound,
ub: upper bound,
set:minizinc.Model.Set)
* Declaration *
minizinc.Model:
accepts 1 optional argument:
a string or list of strings indicating libraries to be included.
returns:
model to be used, for example:
m = minizinc.Model
i = m.Variable
s = m.Set
m.Constraint
m.satisfy
etc..
minizinc.Model.Set:
accepts multiple arguments:
item1, item2, ... itemn
item?: a number - single element
or a list/tuple of 2 elements - elements ranging from lb to ub
returns:
an object to be used
functions:
push - push more elements onto the set
clear - clear all elements in the set
minizinc.Model.Variable:
accepts up to 2 arguments:
<None> : a boolean variable
ub : a variable ranging from 0 to ub - 1
lb, ub : a variable ranging from lb to ub
set : a variable based on the set
minizinc.Model.Array:
minizinc.Model.Construct:
* Functions *
minizinc.Model.satisfy
minizinc.Model.next
'''
import sys
import minizinc_internal
import predicate
import annotation
#import inspect
if sys.version < '3':
integer_types = (int, long, )
python_types = (int, long, float, bool, str)
int_t = long
def longify(x):
return long(x)
else:
integer_types = (int, )
python_types = (int, float, bool, str)
int_t = int
def longify(x):
return x
# turns a complex structure of items into a list of items
def flatten(x):
result = []
for el in x:
if isinstance(el, (list, tuple)):
result.extend(flatten(el))
else:
result.append(el)
return result
# nicely presents the type of a variable
def type_presentation(x):
if x is None:
return "Any_Type"
if type(x) is type:
name = x.__name__
if name[:9] == 'minizinc.':
name = name[9:]
return name
else:
if type(x) is tuple:
ret = '('
for i, t in enumerate(x):
if i != 0:
ret += ', '
ret += type_presentation(t)
ret += ')'
elif type(x) is list:
ret = 'array' + str(len(x)) + 'd of ' + type_presentation(x[0])
else:
raise TypeError('Type Presentation: Unexpected type: ' + type(x))
return ret
# All MiniZinc objects derived from here
class Expression(object):
def __init__(self, model = None):
self.model = model
def __str__(self):
return str(self.get_value())
def is_decl(self):
return isinstance(self, Declaration)
def is_pre(self):
return isinstance(self, Predicate)
def evaluate(self, var):
if isinstance(var, Expression):
ret = var.get_value()
if ret == None:
raise ValueError("'" + var.name + "'Variable value is not set")
return ret
else:
return var
def eval_type(self, args):
if isinstance(args, Expression):
return args.type
elif type(args) in integer_types:
return int_t
elif type(args) in (float, bool, str):
return type(args)
elif type(args) is list:
t = None
for i in args:
if t is None:
t = self.eval_type(i)
else:
type_i = self.eval_type(i)
if t != type_i:
raise TypeError("Type of arguments in an array must be the same: expected: " +
type_presentation((t,)) + ", received: " + type_presentation((type_i,)) )
return [t]
elif type(args) is tuple:
t = []
for i in args:
t.append(self.eval_type(i))
return tuple(t)
else:
raise TypeError("Unexpected Type: " + type_presentation(type(args)) )
def __and__(self, pred):
return And( self, pred )
def __rand__(self, pred):
return And( self, pred )
def __or__(self, pred):
return Or( self, pred )
def __xor__(self, pred):
return Xor( self, pred )
def __ror__(self, pred):
return Or( self, pred )
def __add__(self, pred):
return Add( self, pred )
def __radd__(self, pred):
return Add( pred, self )
def __sub__(self, pred):
return Sub( self, pred )
def __rsub__(self, pred):
return Sub( pred, self )
def __div__(self, pred):
return Div( self, pred )
def __rdiv__(self, pred):
return Div( pred, self )
def __floordiv__(self, pred):
return FloorDiv( self, pred )
def __rfloordiv__(self, pred):
return FloorDiv( pred, self )
def __mul__(self, pred):
return Mul( self, pred )
def __rmul__(self, pred):
return Mul( self, pred )
def __mod__(self, pred):
return Mod( self, pred )
def __rmod__(self, pred):
return Mod( pred, self )
def __pow__(self, pred):
return Pow( self, pred )
def __rpow__(self, pred):
return Pow( pred, self )
def __eq__(self, pred):
return Eq( self, pred )
def __ne__(self, pred):
return Ne( self, pred )
def __lt__(self, pred):
return Lt( self, pred )
def __gt__(self, pred):
return Gt( self, pred )
def __le__(self, pred):
return Le( self, pred )
def __ge__(self, pred):
return Ge( self, pred )
def __neg__(self):
return Neg( self )
def __pos__(self):
return Pos( self )
def __invert__(self):
return Invert( self )
def __abs__(self):
return Abs( self )
# Temporary container for Expression before evaluating to minizinc_internal object
# Calling Predicate.init automatically check:
# if the arguments belong to the same model
class Predicate(Expression):
def __init__(self, vars, args_and_return_type_tuple = None, name = None, model = None):
self.vars = vars
self.name = str(name)
def eval_model(args):
model = None
for val in args:
if isinstance(val, Expression):
if hasattr(val, 'model'):
if val.model is not None:
if model is None:
model = val.model
elif model != val.model:
raise TypeError("Arguments in the Predicate don't belong to the same model")
return model
self.vars_type = self.eval_type(vars)
self.model = eval_model(vars)
if args_and_return_type_tuple is not None:
for t in args_and_return_type_tuple:
def check_type(expected, actual):
if expected is None:
return True
if len(expected) != len(actual):
return False
for i, val in enumerate(expected):
if (val is None):
pass
elif type(val) is list and type(actual[i]) is list:
if check_type(val, actual[i]) == False:
return False
elif type(val) != type(actual[i]):
return False
return True
if check_type(t[0], self.vars_type):
#self.vars_type == t[0]:
self.type = t[1]
break
else:
s = "MiniZinc: function '" + self.name + "': Argument type does not match, expected: "
for i, t in enumerate(args_and_return_type_tuple):
if i != 0:
s += ' or '
s += type_presentation(t[0])
s += ', received: ' + type_presentation(self.vars_type)
raise TypeError(s)
else:
self.type = None
Expression.__init__(self)
class BinOp(Predicate):
def __init__(self, vars, code, args_and_return_type_tuple, name):
Predicate.__init__(self,vars,args_and_return_type_tuple, name)
self.BinOpCode = code
class UnOp(Predicate):
def __init__(self, vars, code, args_and_return_type_tuple, name):
Predicate.__init__(self,vars, args_and_return_type_tuple, name)
self.UnOpCode = code
class Call(Predicate):
def __init__(self, vars, code, args_and_return_type_tuple = None, name = None, model_list = None):
Predicate.__init__(self,vars, args_and_return_type_tuple, name)
self.CallCode = code
self.model_list = model_list
args_ret_dict = {}
to_assign = [ ((int_t, int_t), int_t ),
((float, float), float ),
((int_t, float), float ),
((float, int_t), float)]
args_ret_dict['add'] = to_assign
args_ret_dict['sub'] = to_assign
args_ret_dict['mul'] = to_assign
args_ret_dict['div'] = to_assign
args_ret_dict['floordiv'] = to_assign
args_ret_dict['pow'] = to_assign
args_ret_dict['mod'] =[ ((int_t, int_t), int_t )]
to_assign = [ ((int_t, int_t), bool ),
((float, float), bool ),
((bool, bool), bool ),
((str, str), bool ),
((minizinc_internal.VarSet, minizinc_internal.VarSet), bool)]
args_ret_dict['lt'] = to_assign
args_ret_dict['le'] = to_assign
args_ret_dict['gt'] = to_assign
args_ret_dict['ge'] = to_assign
args_ret_dict['eq'] = to_assign
args_ret_dict['ne'] = to_assign
args_ret_dict['in'] = [ ((int_t, minizinc_internal.VarSet), bool) ]
to_assign = [ ((bool, bool), bool) ]
args_ret_dict['or'] = to_assign
args_ret_dict['and'] = to_assign
args_ret_dict['xor'] = to_assign
args_ret_dict['abort'] = [ ((), bool)]
to_assign = [ ((int_t,), int_t),
((float,), float) ]
args_ret_dict['abs'] = to_assign
args_ret_dict['neg'] = to_assign
args_ret_dict['pos'] = to_assign
args_ret_dict['invert'] = to_assign
args_ret_dict['array1d'] = [ (None, [int_t]) ] #the function has checked its argument already
args_ret_dict['array2d'] = [ (None, [int_t, int_t]) ]
args_ret_dict['array3d'] = [ (None, [int_t, int_t, int_t]) ]
args_ret_dict['array4d'] = [ (None, [int_t, int_t, int_t, int_t])]
args_ret_dict['array5d'] = [ (None, [int_t, int_t, int_t, int_t, int_t])]
args_ret_dict['array6d'] = [ (None, [int_t, int_t, int_t, int_t, int_t, int_t])]
to_assign = [ ((float,), float) ]
args_ret_dict['sin'] = to_assign
args_ret_dict['cos'] = to_assign
args_ret_dict['tan'] = to_assign
args_ret_dict['asin'] = to_assign
args_ret_dict['acos'] = to_assign
args_ret_dict['atan'] = to_assign
args_ret_dict['sinh'] = to_assign
args_ret_dict['cosh'] = to_assign
args_ret_dict['tanh'] = to_assign
args_ret_dict['asinh'] = to_assign
args_ret_dict['acosh'] = to_assign
args_ret_dict['atanh'] = to_assign
args_ret_dict['sqrt'] = to_assign
args_ret_dict['ln'] = to_assign
args_ret_dict['log2'] = to_assign
args_ret_dict['log10'] = to_assign
args_ret_dict['exp'] = to_assign
args_ret_dict['log'] = [ ((float, float), float) ]
args_ret_dict['assert'] = [ ((bool, str), bool) ]
args_ret_dict['bool2int'] = [ ((bool,), int_t) ]
# XXX what to do with cardinality?
args_ret_dict['card'] = None
to_assign = [ ((float,), int_t) ]
args_ret_dict['ceil'] = to_assign
args_ret_dict['floor'] = to_assign
args_ret_dict['round'] = to_assign
to_assign = [ ((int_t,), float) ]
args_ret_dict['int2float'] = to_assign
# XXX consider remove
args_ret_dict['concat'] = None
to_assign = [ ((int_t, int_t), int_t),
((float, float), float),
(([int_t],), int_t),
(([float],), float) ]
args_ret_dict['min'] = to_assign
args_ret_dict['max'] = to_assign
to_assign = [ (([int_t], ), int_t),
(([float], ), float) ]
args_ret_dict['product'] = to_assign
args_ret_dict['sum'] = to_assign
del to_assign
class Add(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 0, args_ret_dict['add'], '+')
def get_symbol(self):
return '+'
def get_value(self):
lhs = self.evaluate(self.vars[0])
rhs = self.evaluate(self.vars[1])
if isinstance(rhs, str):
lhs = str(lhs)
elif isinstance(lhs, str):
rhs = str(rhs)
return lhs + rhs
class Sub(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 1, args_ret_dict['sub'], '-')
def get_symbol(self):
return '-'
def get_value(self):
return self.evaluate(self.vars[0]) - self.evaluate(self.vars[1])
class Mul(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 2, args_ret_dict['mul'], '*')
def get_symbol(self):
return '*'
def get_value(self):
return self.evaluate(self.vars[0]) * self.evaluate(self.vars[1])
class Div(BinOp):
def __init__(self, *vars) :
BinOp.__init__(self, vars, 3, args_ret_dict['div'], '/')
def get_symbol(self):
return '/'
def get_value(self):
return self.evaluate(self.vars[0]) / self.evaluate(self.vars[1])
class FloorDiv(BinOp):
def __init__(self, *vars) :
BinOp.__init__(self, vars, 4, args_ret_dict['floordiv'], '//')
def get_symbol(self):
return '//'
def get_value(self):
return self.evaluate(self.vars[0]) // self.evaluate(self.vars[1])
class Mod(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 5, args_ret_dict['mod'], '%')
def get_symbol(self):
return '%'
def get_value(self):
return self.evaluate(self.vars[0]) % self.evaluate(self.vars[1])
class Lt(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 6, args_ret_dict['lt'], '<')
def get_symbol(self):
return '<'
def get_value(self):
return self.evaluate(self.vars[0]) < self.evaluate(self.vars[1])
class Le(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 7, args_ret_dict['le'], '<=')
def get_symbol(self):
return '<='
def get_value(self):
return self.evaluate(self.vars[0]) <= self.evaluate(self.vars[1])
class Gt(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 8, args_ret_dict['gt'], '>')
def get_symbol(self):
return '>'
def get_value(self):
return self.evaluate(self.vars[0]) > self.evaluate(self.vars[1])
class Ge(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 9, args_ret_dict['ge'], '>=')
def get_symbol(self):
return '>='
def get_value(self):
return self.evaluate(self.vars[0]) >= self.evaluate(self.vars[1])
class Eq(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 10, args_ret_dict['eq'], '==')
def get_symbol(self):
return '=='
def get_value(self):
return self.evaluate(self.vars[0]) == self.evaluate(self.vars[1])
class Ne(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 11, args_ret_dict['ne'], '!=')
def get_symbol(self):
return '!='
def get_value(self):
return self.evaluate(self.vars[0]) != self.evaluate(self.vars[1])
class In(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 12, args_ret_dict['in'], 'in')
def get_symbol(self):
return 'in'
def get_value(self):
return self.evaluate(self.vars[0]) in self.evaluate(self.vars[1])
def __nonzero__(self):
return self
class Or(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 23, args_ret_dict['or'], 'or')
def get_symbol(self):
return 'or'
def get_value(self):
return self.evaluate(self.vars[0]) or self.evaluate(self.vars[1])
class And(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 24, args_ret_dict['and'], 'and')
def get_symbol(self):
return '&'
def get_value(self):
return self.evaluate(self.vars[0]) & self.evaluate(self.vars[1])
class Xor(BinOp):
def __init__(self, *vars):
BinOp.__init__(self, vars, 25, args_ret_dict['xor'], 'xor')
def get_symbol(self):
return 'xor'
def get_value(self):
return bool(self.evaluate(self.vars[0])) != bool(self.evaluate(self.vars[1]))
class Pow(Call):
def __init__(self, *vars):
Call.__init__(self, vars, "pow", args_ret_dict['pow'], '^')
def get_symbol(self):
return '**'
def get_value(self):
return self.evaluate(self.vars[0]) ** self.evaluate(self.vars[1])
class Abort(Call):
def __init__(self, *vars):
Call.__init__(self, vars, "abort", args_ret_dict['abort'], 'abort')
class Abs(Call):
def __init__(self, *vars):
Call.__init__(self, vars, "abs", args_ret_dict['abs'], 'abs')
class Array1d(Call):
def __init__(self, dim1, array):
Call.__init__(self,(dim1, array),"array1d", args_ret_dict['array1d'], 'array1d')
class Array2d(Call):
def __init__(self, dim1, dim2, array):
Call.__init__(self, (dim1, dim2, array), "array2d", args_ret_dict['array2d'], 'array2d')
class Array3d(Call):
def __init__(self, dim1, dim2, dim3, array):
Call.__init__(self,[dim1, dim2, dim3, array],"array3d", args_ret_dict['array3d'], 'array3d')
class Array4d(Call):
def __init__(self, dim1, dim2, dim3, dim4, array):
Call.__init__(self,[dim1, dim2, dim3, dim4, array],"array4d", args_ret_dict['array4d'], 'array4d')
class Array5d(Call):
def __init__(self, dim1, dim2, dim3, dim4, dim5, array):
Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, array],"array5d", args_ret_dict['array5d'], 'array5d')
class Array6d(Call):
def __init__(self, dim1, dim2, dim3, dim4, dim5, dim6, array):
Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, dim6, array],"array6d", args_ret_dict['array6d'], 'array6d')
class Acos(Call):
def __init__(self, var):
Call.__init__(self, (var,), "acos", args_ret_dict['acos'], 'acos')
class Acosh(Call):
def __init__(self, var):
Call.__init__(self, (var,), "acosh", args_ret_dict['acosh'], 'acosh')
class Asin(Call):
def __init__(self, var):
Call.__init__(self, (var,), "asin", args_ret_dict['asin'], 'asin')
class Asinh(Call):
def __init__(self, var):
Call.__init__(self, (var,), "asinh", args_ret_dict['asinh'], 'asinh')
class Atan(Call):
def __init__(self, var):
Call.__init__(self, (var,), "atan", args_ret_dict['atan'], 'atan')
class Atanh(Call):
def __init__(self, var):
Call.__init__(self, (var,), "atanh", args_ret_dict['atanh'], 'atanh')
class Assert(Call):
def __init__(self, constraint, message):
Call.__init__(self, (constraint, message), "assert", args_ret_dict['assert'], 'assert')
class Bool2Int(Call):
def __init__(self, var):
Call.__init__(self, (var,), "bool2int", args_ret_dict['bool2int'], 'bool2int')
class Card(Call):
def __init__(self, var):
Call.__init__(self, (var,), "card", args_ret_dict['card'], 'card')
class Ceil(Call):
def __init__(self, var):
Call.__init__(self, (var,), "ceil", args_ret_dict['ceil'], 'ceil')
class Concat(Call):
def __init__(self, var):
Call.__init__(self, (var,), "concat",)
class Cos(Call):
def __init__(self, var):
Call.__init__(self, (var,), "cos", args_ret_dict['cos'], 'cos')
class Cosh(Call):
def __init__(self, var):
Call.__init__(self, (var,), "cosh", args_ret_dict['cosh'], 'cosh')
class Dom(Call):
def __init__(self, var):
Call.__init__(self,[var],"dom")
class Dom_Array(Call):
def __init__(self, var):
Call.__init__(self,[var],"dom_array")
class Dom_Size(Call):
def __init__(self, var):
Call.__init__(self,[var],"dom_size")
class Fix(Call):
def __init__(self, var):
Call.__init__(self,[var],"fix")
class Floor(Call):
def __init__(self, var):
Call.__init__(self, (var,),"floor", args_ret_dict['floor'], 'floor')
class Exp(Call):
def __init__(self, var):
Call.__init__(self, (var,),"exp", args_ret_dict['exp'], 'exp')
class Int2Float(Call):
def __init__(self, var):
Call.__init__(self, (var,),"int2float", args_ret_dict['int2float'], 'int2float')
class Is_Fixed(Call):
def __init__(self, var):
Call.__init__(self,[var],"is_fixed")
class Join(Call):
def __init__(self, var):
Call.__init__(self,[var],"join")
class Lb(Call):
def __init__(self, var):
Call.__init__(self,[var],"lb")
class Lb_array(Call):
def __init__(self, var):
Call.__init__(self,[var],"lb_array")
class Length(Call):
def __init__(self, var):
Call.__init__(self,[var],"length")
class Ln(Call):
def __init__(self, var):
Call.__init__(self, (var,),"ln", args_ret_dict['ln'], 'ln')
class Log(Call):
def __init__(self, var1, var2):
Call.__init__(self, (var1, var2),"log", args_ret_dict['log'], 'log')
class Log2(Call):
def __init__(self, var):
Call.__init__(self, (var,), "log2", args_ret_dict['log2'], 'log2')
class Log10(Call):
def __init__(self, var):
Call.__init__(self, (var,), "log10", args_ret_dict['log10'], 'log10')
class Min(Call):
def __init__(self, var):
Call.__init__(self, (var,), "min", args_ret_dict(['min']), 'min')
class Max(Call):
def __init__(self, var):
Call.__init__(self, (var,), "max", args_ret_dict(['max']), 'max')
class Product(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"product", args_ret_dict['product'], 'product')
class Round(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"round", args_ret_dict['round'], 'round')
class Set2array(Call):
def __init__(self, var):
Call.__init__(self,[var],"set2array")
class Sin(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"sin", args_ret_dict['sin'], 'sin')
class Sinh(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"sinh", args_ret_dict['sinh'], 'sinh')
class Sqrt(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"sqrt", args_ret_dict['sqrt'], 'sqrt')
class Sum(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"sum", args_ret_dict['sum'], 'sum')
class Tan(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"tan", args_ret_dict['tan'], 'tan')
class Tanh(Call):
def __init__(self, var):
Call.__init__(self, (var, ),"tanh", args_ret_dict['tanh'], 'tanh')
class Trace(Call):
def __init__(self, var):
Call.__init__(self,[var],"trace")
class Ub(Call):
def __init__(self, var):
Call.__init__(self,[var],"ub")
class Ub_Array(Call):
def __init__(self, var):
Call.__init__(self,[var],"ub_array")
class Neg(UnOp):
def __init__(self, var):
UnOp.__init__(self, (var, ), 2, args_ret_dict['neg'], 'neg')
class Pos(UnOp):
def __init__(self, var):
UnOp.__init__(self, (var, ), 1, args_ret_dict['pos'], 'pos')
class Invert(Predicate):
def __init__(self, vars):
UnOp.__init__(self, (var, ), 0, args_ret_dict['invert'], 'invert')
class Id(Expression):
def __init__(self, name, model_list = None):
self.name = name
self.model_list = model_list
self.type = minizinc_internal.Annotation
# Temporary container for Variable Declaration
class Declaration(Expression):
def __init__(self, model):
if not isinstance(model, Model):
raise TypeError('Warning: First argument must be a Model Object')
Expression.__init__(self, model)
self.name = '"' + str(id(self)) + '"'
self.solve_counter = -1
self.next_counter = -1
self.value = None
def get_value(self):
if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter:
return self.value
self.solve_counter = self.model.solve_counter
self.next_counter = self.model.next_counter
self.value = self.model.mznsolver.get_value(self.name)
return self.value
def __str__(self):
return str(self.get_value())
def __repr__(self):
return 'A Minizinc ' + self.__class__.__name__ + ' with the value of ' + self.__str__()
class Construct(Declaration):
def __init__(self, model, arg1):
Declaration.__init__(self, model)
self.type = self.eval_type(arg1)
self.has_minizinc_objects = False
def unwrap(arg):
if isinstance(arg, Expression):
self.has_minizinc_objects = True
return arg.obj
elif type(arg) in (list, tuple):
ret = []
for val in arg:
ret.append(unwrap(val))
return ret
elif type(arg) in python_types:
return arg
else:
raise TypeError('Unexpected type: argument should be a single/list/tuple of MiniZinc objects or Python basic types')
try:
self.obj = model.mznmodel.Declaration(self.name, unwrap(arg1))
except:
print sys.exc_info()[0]
raise
if self.has_minizinc_objects:
self.value = arg1
else:
self.wrapped_value = arg1
self.class_name = 'Construct'
'''
imagine we have:
a = Variable(1,100) undefined
b = Variable(1,100) undefined
c = Construct([a,b]) [ undefined, undefined ]
when the model is solved, c's value should be changed
'''
def get_value(self):
if self.has_minizinc_objects:
def get_value_helper(arg):
if isinstance(arg, Expression):
return arg.get_value()
elif type(arg) is list:
ret = []
for val in arg:
ret.append(get_value_helper(val))
return ret
elif type(arg) in python_types:
return arg
else:
raise TypeError('Internal error: Unexpected type')
if not (self.solve_counter == self.model.solve_counter and self.next_counter == self.obj.next_counter):
self.value = get_value_helper(self.wrapped_value)
return self.value
class Variable(Declaration):
def __init__(self, model, arg1=None, arg2=None):
Declaration.__init__(self, model)
lb, ub = False, True
if arg1 is not None:
typearg1 = type(arg1)
if arg2 is None:
if typearg1 is Set:
lb = arg1
ub = None
elif typearg1 in (list,tuple):
if len(arg1) != 2:
raise ValueError('Requires a list or tuple of exactly 2 numbers')
lb,ub = sorted(arg1)[0,1]
else:
ub = arg1 - 1
lb = typearg1(lb)
else:
lb,ub = arg1, arg2
typelb, typeub = type(lb), type(ub)
if not typelb is Set:
if typelb not in (bool, float, ) and typelb not in integer_types and not isinstance(typelb, Declaration):
raise TypeError('Lower bound must be a boolean, an int, a float or a set')
if typeub not in (bool, float, ) and typeub not in integer_types and not isinstance(typelb, Declaration):
raise TypeError('Upper bound must be a boolean, an int or a float')
if typelb != typeub:
raise TypeError('Upper bound an dlower bound is of different type')
if lb > ub:
raise ValueError('Lower bound cannot be greater than upper bound')
self.dim_list = []
if typelb is bool:
self.obj = model.mznmodel.Declaration(self.name, 10, [])
self.type = bool
elif typelb in integer_types:
self.obj = model.mznmodel.Declaration(self.name, 9, [], lb, ub)
self.type = int_t
elif typelb is float:
self.obj = model.mznmodel.Declaration(self.name, 11, [], lb, ub)
self.type = float
elif typelb is Set:
self.obj = model.mznmodel.Declaration(self.name, 9, [], lb.obj)
self.type = int_t
else:
raise TypeError('Internal: Unexpected type')
self.class_name = 'Variable'
class VariableConstruct(Variable, Construct):
def __init__(self, model, arg1, arg2 = None):
Construct.__init__(self, model, arg1, arg2)
class Array(Variable):
def __init__(self, model, argopt1, argopt2, *args):
Declaration.__init__(self, model)
dim_list = []
lb = None
ub = None
def add_to_dim_list(i):
if type(i) is int:
if i > 0:
dim_list.append([0,i-1])
else:
raise TypeError('Single value must be a positive integer')
elif type(i) is list:
if type(i[0]) in integer_types and type(i[-1]) in integer_types:
dim_list.append([ i[0],i[-1]] )
else:
raise TypeError('Range boundaries must be integers')
elif isinstance(i, Set):
if i.continuous():
dim_list.append([i.min(), i.max()])
else:
raise TypeError('Array ranges must be continuous')
else:
raise TypeError('Unknown type')
if type(argopt1) is Set:
lb = argopt1
ub = None
add_to_dim_list(argopt2)
elif type(argopt1) is bool and type(argopt2) is bool:
lb = argopt1
ub = argopt2
elif type(argopt2) not in integer_types:
if type(argopt1) not in integer_types:
raise TypeError('Range values must be integers')
lb = 0
ub = argopt1 - 1
add_to_dim_list(argopt2)
else:
if type(argopt1) not in integer_types or type(argopt2) not in integer_types:
raise TypeError('Lower bound and upper bound must be integers')
lb = argopt1
ub = argopt2
for i in args:
add_to_dim_list(i)
self.lb = lb
self.ub = ub
if dim_list == []:
raise AttributeError('Initialising an Array without dimension list')
self.dim_list = dim_list
tlb = type(argopt1)
if tlb is bool:
self.type = [bool] * len(dim_list)
self.obj = model.mznmodel.Declaration(self.name,10,dim_list,lb,ub)
elif tlb in integer_types:
self.type = [int_t] * len(dim_list)
self.obj = model.mznmodel.Declaration(self.name,9,dim_list, lb, ub)
elif tlb is float: #isinstance(lb, float):
self.type = [float] * len(dim_list)
self.obj = model.mznmodel.Declaration(self.name,11,dim_list,lb,ub)
elif tlb is Set:
self.type = [int_t] * len(dim_list)
self.obj = model.mznmodel.Declaration(self.name,9,dim_list,lb.obj)
else:
raise TypeError('Unexpected type')
self.class_name = 'Array'
def __getitem__(self, *args):
return ArrayAccess(self.model, self, args[0])
# XXX: to be rewritten later
class ArrayAccess(Array):
def __init__(self, model, array, idx):
Declaration.__init__(self, model)
if type(idx) is not tuple:
idx = [idx]
else:
idx = flatten(idx)
if len(idx) != len(array.dim_list):
raise IndexError('Requires exactly ' + str(len(self.dim_list)) + ' index values')
for i, value in enumerate(idx):
if not isinstance(value, Expression):
if value < array.dim_list[i][0] or value > array.dim_list[i][1]:
raise IndexError('Index at pos ' + str(i) + ' is out of range')
self.array = array
self.idx = idx
self.type = array.type[0]
self.value = None
self.class_name = 'Array Item'
def get_value(self):
if hasattr(self, 'solve_counter'):
if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter:
return self.value
self.solve_counter = self.model.solve_counter
self.next_counter = self.model.next_counter
arrayvalue = self.array.get_value()
if arrayvalue is None:
return None
else:
for i,value in enumerate(self.idx):
arrayvalue = arrayvalue[value - self.array.dim_list[i][0]]
self.value = arrayvalue
return self.value
return self.value
class ArrayConstruct(Array, Construct):
def __init__(self, model, arg1, arg2 = None):
Construct.__init__(self, model, arg1, arg2)
class Set(Declaration):
# Set is model independent and can be reused in multiple models
# Thus, Set.model = None
def __init__(self, *args):
self.model = None
self.name = '"' + str(id(self)) + '"'
'''
lb, ub = None, None
set_list = None
if argopt2 is not None:
lb,ub = argopt1, argopt2
else:
if type(argopt1) is list:
set_list = argopt1
else:
ub = argopt1 - 1
lb = 0
if set_list is None:
if not (type(lb) in integer_types and type(ub) in integer_types):
raise TypeError('Lower bound and upper bound must be integers')
set_list = [[lb, ub]]
'''
self.obj = minizinc_internal.Set(*args)
self.type = minizinc_internal.Set
self.class_name = 'Set'
def push(self, *args):
self.obj.push(*args)
def clear(self):
self.obj.clear()
def continuous(self):
return self.obj.continuous()
def min(self):
return self.obj.min()
def max(self):
return self.obj.max()
def get_value(self):
return self.obj
def __iter__(self):
return self.obj.__iter__()
def __contains__(self, val):
return self.obj.contains(val)
class VarSet(Variable):
def __init__(self, model, argopt1, argopt2 = None):
self.model = model
self.name = '"' + str(id(self)) + '"'
lb, ub = None, None
set_list = None
if argopt2 is not None:
lb,ub = argopt1, argopt2
else:
if type(argopt1) is list:
set_list = argopt1
elif type(argopt1) is Set:
lb = argopt1.min()
ub = argopt1.max()
else:
ub = argopt1 - 1
lb = 0
if set_list is None:
if not (type(lb) in integer_types and type(ub) in integer_types):
raise TypeError('Lower bound and upper bound must be integers')
model.mznmodel.Declaration(self.name, 12, [], lb, ub)
self.type = minizinc_internal.VarSet
self.class_name = 'Var Set'
''' Python automatically evaluate a __contains__ return object to boolean
thus, we have to use separate In class instead of
for i in VarSet
def __contains__(self, argopt):
return In([argopt, self])
'''
variable_model_dict = {}
function_model_dict = {}
name_model_dict = {}
def handlerFunctionClosure(name, args_list, model_list):
def handlerFunction(*args):
return Call(args, name, args_list, name, model_list)
return handlerFunction
def handlerFunction(name, model_list):
return Id(name, model_list)
def init(args = None, model = None):
def assign_usable_names_to_model(name, model):
if name in name_model_dict:
if model is None:
name_model_dict[name] = None
else:
name_model_dict[name].append(model)
return False
else:
if model is None:
name_model_dict[name] = None
else:
name_model_dict[name] = [model]
return True
names = minizinc_internal.retrieveNames(args)
for name, args_and_return_type_tuple in names["boolfuncs"].items():
if assign_usable_names_to_model(name, model):
setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name]))
for name, args_and_return_type_tuple in names["annfuncs"].items():
if assign_usable_names_to_model(name, model):
setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name]))
for name in names["annvars"]:
if assign_usable_names_to_model(name, model):
setattr(annotation, name, handlerFunction(name, name_model_dict[name]))
init()
class Model(object):
def __init__(self, args = None):
self.loaded = False
self.mznsolver = None
self.mznmodel = minizinc_internal.Model(args)
if args:
init(args, self)
self.solve_counter = -1
self.next_counter = -1
'''
# not used anymore
def get_name(self,var):
itemList = [var_name for var_name, var_val in self.frame if var_val is var]
if len(itemList) == 1:
return itemList[0]
elif len(itemList) == 0:
raise LookupError('Internal Error: variable name not found')
else:
raise LookupError('The object pointed to was assigned to different names')
'''
def add_recursive(self, expr):
if isinstance(expr, (tuple, list)):
for i in expr:
self.add_recursive(i)
else:
if issubclass(type(expr), Expression):
if expr.is_pre():
self.mznmodel.Constraint(self.evaluate(expr))
else:
raise(TypeError, "Unexpected Expression")
else:
self.mznmodel.Constraint(expr)
def Constraint(self, *expr):
minizinc_internal.lock()
if len(expr)>0:
self.loaded = True
#self.frame = inspect.currentframe().f_back.f_locals.items()
self.add_recursive(expr)
#del self.frame
minizinc_internal.unlock()
def evaluate(self, expr):
if not isinstance(expr, Expression):
if isinstance(expr, (list, tuple)):
for i,item in enumerate(expr):
expr[i] = self.evaluate(item)
return expr
if isinstance(expr, Id):
if expr.model_list is not None:
if not self in expr.model_list:
raise TypeError("The annotation '" + expr.name + "' does not belong to the model")
return minizinc_internal.Id(expr.name)
if isinstance(expr, Call):
variables = []
model = None
for i in expr.vars:
variables.append(self.evaluate(i))
if expr.model_list is not None:
if not self in expr.model_list:
raise TypeError("The function '" + expr.name + "' does not belong to the model")
return minizinc_internal.Call(expr.CallCode, variables, expr.type)
elif isinstance(expr, ArrayAccess):
for i,value in enumerate(expr.idx):
if isinstance(value, Expression):
expr.idx[i] = self.evaluate(value)
return minizinc_internal.at(expr.array.obj, expr.idx)
elif isinstance(expr, Declaration):
if expr.model != self:
raise TypeError("The Declaration not belongs to this model")
return expr.obj
elif isinstance(expr, BinOp):
if expr.model is not None and expr.model != self:
raise TypeError("The Declaration not belongs to this model")
lhs = self.evaluate(expr.vars[0])
rhs = self.evaluate(expr.vars[1])
return minizinc_internal.BinOp(lhs, expr.BinOpCode, rhs)
elif isinstance(expr, UnOp):
if expr.model is not None and expr.model != self:
raise TypeError("The Declaration not belongs to this model")
rhs = self.evaluate(expr.vars[0])
return minizinc_internal.UnOp(expr.UnOpCode, rhs)
else:
raise TypeError('Variable Type unspecified')
def Variable(self, argopt1=None, argopt2=None):
return Variable(self, argopt1, argopt2)
def Array(self, argopt1, argopt2, *args):
list_ = []
for i in args:
list_.append(i)
return Array(self, argopt1, argopt2, *list_)
def Set(self, *args):
return Set(*args)
def Range(self, arg1, arg2 = None):
if (arg2 is None):
return Set((0, arg1 - 1))
else:
return Set((arg1, arg2))
def VarSet(self, argopt1, argopt2 = None):
return VarSet(self, argopt1, argopt2)
def Construct(self, argopt1):
if isinstance(argopt1, list):
return ArrayConstruct(self, argopt1)
else:
return VariableConstruct(self, argopt1)
def __solve(self, code, expr, ann, data, solver, time):
minizinc_internal.lock()
if ann is not None:
if not hasattr(ann, 'type') or ann.type != minizinc_internal.Annotation:
raise TypeError('Unexpected type of annotation')
eval_ann = self.evaluate(ann)
eval_expr = self.evaluate(expr)
savedModel = self.mznmodel.copy()
# The model with declared variable cannot be deleted.
self.mznmodel, savedModel = savedModel, self.mznmodel
self.mznmodel.SolveItem(code, eval_ann, eval_expr)
if data is not None:
self.add_recursive(data)
minizinc_internal.unlock()
self.mznsolver = self.mznmodel.solve(solver = solver, time = time)
self.mznmodel = savedModel
self.solve_counter = self.solve_counter + 1
self.next_counter = -1
def satisfy(self, ann = None, data = None, solver = 'gecode', time = 0):
self.__solve(0, None, ann, data, solver, time)
def maximize(self, expr, ann = None, data = None, solver = 'gecode', time = 0):
self.__solve(2, expr, ann, data, solver, time)
def minimize(self, expr, ann = None, data = None, solver = 'gecode', time = 0):
self.__solve(1, expr, ann, data, solver, time)
def reset(self):
self.__init__()
def next(self):
if self.mznsolver is None:
raise ValueError('Model is not solved yet')
self.status = self.mznsolver.next()
self.next_counter = self.next_counter + 1
return (self.status is None)
def is_loaded(self):
return self.loaded
def is_solved(self):
return self.mznsolver != None
def set_time_limit(self, time):
self.mznmodel.set_time_limit(time)
def set_solver(self, solver):
self.mznmodel.set_solver(solver)
def _debugprint(self):
self.mznmodel.debugprint()