Skip to content

Commit

Permalink
add smarttable, improve callbacker
Browse files Browse the repository at this point in the history
  • Loading branch information
Wout4 committed May 6, 2024
1 parent 099e8d6 commit 81ba31f
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 21 deletions.
133 changes: 119 additions & 14 deletions benchmarks/callbackscpmpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from pycsp3.tools.utilities import _Star

import cpmpy as cp
from cpmpy.expressions.utils import is_any_list, get_bounds


class CallbacksCPMPy(Callbacks):

Expand All @@ -21,15 +23,23 @@ def __init__(self):
self.cpm_variables = dict()

def var_integer_range(self, x: Variable, min_value: int, max_value: int):
newvar = cp.intvar(min_value, max_value, name=x.id)
if min_value == 0 and max_value == 1:
#boolvar
newvar = cp.boolvar(name=x.id)
else:
newvar = cp.intvar(min_value, max_value, name=x.id)
self.cpm_variables[x] = newvar

def var_integer(self, x: Variable, values: list[int]):
mini = min(values)
maxi = max(values)
newvar = cp.intvar(mini, maxi, name=x.id)
nbvals = maxi - mini + 1
if mini == 0 and maxi == 1:
#boolvar
newvar = cp.boolvar(name=x.id)
else:
newvar = cp.intvar(mini, maxi, name=x.id)
self.cpm_variables[x] = newvar
nbvals = maxi - mini + 1
if nbvals < len(values):
# only do this if there are holes in the domain
self.cpm_model += cp.InDomain(newvar, values)
Expand Down Expand Up @@ -181,9 +191,19 @@ def strwildcard(x):
if isinstance(x,_Star):
return '*'
return x
cpm_vars = self.vars_from_node(scope)
exttuples = [tuple([strwildcard(x) for x in tup]) for tup in tuples]
self.cpm_model += cp.SmartTable(cpm_vars, exttuples)
if 'starred' in flags:
cpm_vars = self.vars_from_node(scope)
exttuples = [tuple([strwildcard(x) for x in tup]) for tup in tuples]
if positive:
self.cpm_model += cp.SmartTable(cpm_vars, exttuples)
else:
self.cpm_model += ~cp.SmartTable(cpm_vars, exttuples)
else:
cpm_vars = self.vars_from_node(scope)
if positive:
self.cpm_model += cp.Table(cpm_vars, tuples)
else:
self.cpm_model += ~cp.Table(cpm_vars, tuples)


def ctr_regular(self, scope: list[Variable], transitions: list, start_state: str, final_states: list[str]):
Expand All @@ -200,6 +220,8 @@ def ctr_all_different(self, scope: list[Variable] | list[Node], excepting: None
if excepting is None:
cpm_exprs = self.exprs_from_node(scope)
return cp.AllDifferent(cpm_exprs)
elif excepting == [0]:
return cp.AllDifferentExcept0(self.exprs_from_node(scope))
else:
self._unimplemented(scope, excepting)

Expand All @@ -211,7 +233,7 @@ def ctr_all_different_matrix(self, matrix: list[list[Variable]], excepting: None
#self._unimplemented(matrix, excepting)

def ctr_all_equal(self, scope: list[Variable] | list[Node], excepting: None | list[int]):
self._unimplemented(scope, excepting)
self.cpm_model += cp.AllEqual(self.get_cpm_exprs(scope))

def ctr_ordered(self, lst: list[Variable], operator: TypeOrderedOperator, lengths: None | list[int] | list[Variable]):
#raise NotImplementedError('Increasing global not in cpmpy')
Expand Down Expand Up @@ -358,10 +380,24 @@ def ctr_nooverlap_multi(self, origins: list[list[Variable]], lengths: list[list[
self._unimplemented(origins, lengths, zero_ignored)

def ctr_nooverlap_mixed(self, xs: list[Variable], ys: list[Variable], lx: list[Variable], ly: list[int], zero_ignored: bool):
self._unimplemented(xs, ys, lx, ly, zero_ignored)
#self._unimplemented(xs, ys, lx, ly, zero_ignored)
pass

def ctr_cumulative(self, origins: list[Variable], lengths: list[int] | list[Variable], heights: list[int] | list[Variable], condition: Condition):
self._unimplemented(origins, lengths, heights, condition)
#self._unimplemented(origins, lengths, heights, condition)
cpm_start = self.get_cpm_exprs(origins)
cpm_durations = self.get_cpm_exprs(lengths)
cpm_demands = self.get_cpm_exprs(heights)
if condition.operator.name == 'LE':
lbs, ubs = get_bounds(cp.cpm_array(cpm_start) + cpm_durations)
lb = min(lbs)
ub = max(ubs)
cpm_ends = cp.intvar(lb, ub, len(cpm_start))
for i in range(len(cpm_start)):
self.cpm_model += cpm_start[i] + cpm_ends[i] == cpm_ends[i]
self.cpm_model += cp.Cumulative(cpm_start, cpm_durations, cpm_ends, cpm_demands, capacity=self.get_cpm_var(condition.operator.value))



def ctr_binpacking(self, lst: list[Variable], sizes: list[int], condition: Condition):
self._unimplemented(lst, sizes, condition)
Expand Down Expand Up @@ -400,16 +436,74 @@ def ctr_circuit(self, lst: list[Variable], size: None | int | Variable): # size
# # # # # # # # # #

def obj_minimize(self, term: Variable | Node):
self._unimplemented(term)
if isinstance(term, Node):
term = term.cnt
self.cpm_model.minimize(self.get_cpm_var(term))

def obj_maximize(self, term: Variable | Node):
self._unimplemented(term)
self.cpm_model.maximize(self.get_cpm_exprs(term)[0])

def obj_minimize_special(self, obj_type: TypeObj, terms: list[Variable] | list[Node], coefficients: None | list[int]):
self._unimplemented(obj_type, terms, coefficients)
if obj_type == TypeObj.SUM:
if coefficients is None:
self.cpm_model.minimize(cp.sum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.minimize(cp.sum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.PRODUCT:
if coefficients is None:
self.cpm_model.minimize(reduce((lambda x, y: x*y),self.get_cpm_exprs(terms)))
else:
self._unimplemented(obj_type, terms, coefficients)
elif obj_type == TypeObj.EXPRESSION:
self._unimplemented(obj_type, terms, coefficients)
elif obj_type == TypeObj.MAXIMUM:
if coefficients is None:
self.cpm_model.minimize(cp.Maximum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.minimize(cp.Maximum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.MINIMUM:
if coefficients is None:
self.cpm_model.minimize(cp.Minimum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.minimize(cp.Minimum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.NVALUES:
if coefficients is None:
self.cpm_model.minimize(cp.NValue(self.get_cpm_exprs(terms)))
else:
self.cpm_model.minimize(cp.NValue(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
else:
self._unimplemented(obj_type, terms, coefficients)

def obj_maximize_special(self, obj_type: TypeObj, terms: list[Variable] | list[Node], coefficients: None | list[int]):
self._unimplemented(obj_type, terms, coefficients)
if obj_type == TypeObj.SUM:
if coefficients is None:
self.cpm_model.maximize(cp.sum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.maximize(cp.sum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.PRODUCT:
if coefficients is None:
self.cpm_model.maximize(reduce((lambda x, y: x * y), self.get_cpm_exprs(terms)))
else:
self._unimplemented(obj_type, terms, coefficients)
elif obj_type == TypeObj.EXPRESSION:
self._unimplemented(obj_type, terms, coefficients)
elif obj_type == TypeObj.MAXIMUM:
if coefficients is None:
self.cpm_model.maximize(cp.Maximum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.maximize(cp.Maximum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.MINIMUM:
if coefficients is None:
self.cpm_model.maximize(cp.Minimum(self.get_cpm_exprs(terms)))
else:
self.cpm_model.maximize(cp.Minimum(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
elif obj_type == TypeObj.NVALUES:
if coefficients is None:
self.cpm_model.maximize(cp.NValue(self.get_cpm_exprs(terms)))
else:
self.cpm_model.maximize(cp.NValue(cp.cpm_array(self.get_cpm_exprs(terms)) * coefficients))
else:
self._unimplemented(obj_type, terms, coefficients)

def vars_from_node(self, scope):
cpm_vars = []
Expand Down Expand Up @@ -441,4 +535,15 @@ def get_cpm_exprs(self, list):
if isinstance(list[0], XVar):
return [self.get_cpm_var(x) for x in list]
else:
return self.exprs_from_node(list)
return self.exprs_from_node(list)

def end_instance(self):
pass

def load_annotation(self, annotation):
pass
def load_annotations(self, annotations):
print('load_annotations_')

def load_objectives(self, objectives):
print('load_objectives_')
5 changes: 2 additions & 3 deletions benchmarks/run_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@

import glob
from os.path import join
problems = glob.glob(join("CSP", '*.xml'))
problems += glob.glob(join("..", "MiniCOP", '*.xml'))
problems = glob.glob(join("CSP", 'bugs', '*.xml'))
print(problems)
count = 160
count = 10
ecount = 0
print(problems[count])
for xml in problems[count:]:
Expand Down
16 changes: 12 additions & 4 deletions cpmpy/expressions/globalconstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,22 @@ def __init__(self, array, table):
def decompose(self):
from .python_builtins import any, all
arr, tab = self.args
raise NotImplementedError('not yet implemented')
return [any(all(ai == ri for ai, ri in zip(arr, row)) for row in tab)], []
return [any(all(ai == ri for ai, ri in zip(arr, row) if ri != '*') for row in tab)], []

def value(self):
arr, tab = self.args
arrval = [argval(a) for a in arr]
raise NotImplementedError('not yet implemented')
return arrval in tab
for tup in tab:
thistup = True
for aval, tval in zip(arrval, tup):
if tval != '*':
if aval != tval:
thistup = False
if thistup:
#found tuple that matches
return True
#didn't find tuple that matches
return False

# syntax of the form 'if b then x == 9 else x == 0' is not supported (no override possible)
# same semantic as CPLEX IfThenElse constraint
Expand Down

0 comments on commit 81ba31f

Please sign in to comment.