Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 40 additions & 25 deletions cpmpy/solvers/minizinc.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,14 @@

import numpy as np

from cpmpy.expressions import NoOverlap

from .solver_interface import SolverInterface, SolverStatus, ExitStatus, Callback
from ..exceptions import MinizincNameException, MinizincBoundsException
from ..expressions.core import Expression, Comparison, Operator, BoolVal
from ..expressions.python_builtins import any as cpm_any
from ..expressions.variables import _NumVarImpl, _IntVarImpl, _BoolVarImpl, NegBoolView, cpm_array
from ..expressions.globalconstraints import Cumulative, DirectConstraint, GlobalCardinalityCount
from ..expressions.variables import _NumVarImpl, NegBoolView
from ..expressions.globalconstraints import DirectConstraint, GlobalCardinalityCount, Regular
from ..expressions.globalfunctions import Multiplication
from ..expressions.utils import is_int, is_any_list, argvals, argval, get_nonneg_args
from ..expressions.utils import is_int, is_any_list, get_bounds, get_nonneg_args
from ..transformations.decompose_global import decompose_in_tree, decompose_objective
from ..exceptions import MinizincPathException, NotSupportedError
from ..transformations.get_variables import get_variables
Expand Down Expand Up @@ -103,8 +101,8 @@ class CPM_minizinc(SolverInterface):
"strictly_increasing", "strictly_decreasing", "lex_lesseq", "lex_less",
"lex_chain_less","lex_chain_lesseq",
"precedence", "no_overlap",
"min", "max", "abs", "mul", "div", "mod", "pow", "element", "count", "nvalue", "among"})
supported_reified_global_constraints = supported_global_constraints - {"circuit", "precedence"}
"min", "max", "abs", "mul", "div", "mod", "pow", "element", "count", "nvalue", "among", "regular"})
supported_reified_global_constraints = supported_global_constraints - {"circuit", "precedence", "regular"}

required_version = (2, 8, 2)

Expand Down Expand Up @@ -658,6 +656,9 @@ def _convert_expression(self, expr) -> str:
if isinstance(expr, (bool, np.bool_)):
expr = BoolVal(expr)

if isinstance(expr, str):
return f'\"{expr}\"'

if not isinstance(expr, Expression):
return self.solver_var(expr) # constants

Expand Down Expand Up @@ -890,25 +891,39 @@ def zero_based(array):
domain_str = self._convert_expression(domain)
return "({} in {})".format(arg0_str, domain_str)

elif expr.name == "regular":
# regular(array, transitions, start, accepting)
# MiniZinc regular constraint expects: regular(array, transitions_table, start, accepting)
# where transitions_table is a 2D array
array, transitions, start, accepting = expr.args
elif isinstance(expr, Regular):
# MiniZinc: `regular(array[int] of var int: x, array[int,int] of opt int: d, int: q0, set of int: F)`
# We map CPMpy's named states to 1-indexed integers.
# Example:
# CPMpy: `Regular([IV0,IV1,IV2], [('a',1,'b'),('b',1,'c'),('b',0,'b'),('c',1,'c'),('c',0,'b')], 'a', ['c'])`
# MiniZinc: `constraint regular([IV0,IV1,IV2], array2d(1..3, 0..1, [<>,2,2,3,2,3]), 1, {3})`
# note: `d` is a 2D array `[|<>,2|2,3|2,3|]` with rows=states, cols=values

array, _, start, accepting = expr.args

# Map states to 1..Q (MiniZinc states are 1-indexed)
node_map = {n: i + 1 for n, i in expr.node_map.items()}
Q = len(expr.nodes)

# Alphabet must cover the full variable domain (undefined transitions are <>)
lbs, ubs = get_bounds(array)
val_min, val_max = min(lbs), max(ubs)

# Transform transition dict to use 1-indexed state identifiers
trans = {(node_map[s], v): node_map[e] for (s, v), e in expr.trans_dict.items()}

# Build 2D transition table d[1..Q, val_min..val_max] with <> for undefined
d_entries = []
for q in range(1, Q + 1):
for v in range(val_min, val_max + 1):
d_entries.append(str(trans.get((q, v), "<>")))
d_str = "array2d(1..{}, {}..{}, [{}])".format(Q, val_min, val_max, ",".join(d_entries))

array_str = self._convert_expression(array)
# Convert transitions to a 2D array format for MiniZinc
# transitions is a list of (src, value, dst) tuples
transitions_list = []
for src, val, dst in transitions:
transitions_list.append("[{}, {}, {}]".format(
self._convert_expression(src),
self._convert_expression(val),
self._convert_expression(dst)
))
transitions_str = "[{}]".format(",".join(transitions_list))
start_str = self._convert_expression(start)
accepting_str = self._convert_expression(accepting)
return "regular({}, {}, {}, {})".format(array_str, transitions_str, start_str, accepting_str)
q0 = node_map[start]
F = "{{{}}}".format(",".join(str(node_map[a]) for a in accepting))

return "regular({}, {}, {}, {})".format(array_str, d_str, q0, F)

# a direct constraint, treat differently for MiniZinc, a text-based language
# use the name as, unpack the arguments from the argument tuple
Expand Down
Loading