如何在mzn2fzn转换期间传播一组int域?

时间:2017-11-08 19:06:54

标签: minizinc flatzinc

我有以下MiniZinc代码示例:

include "globals.mzn";

var int: i;
array[-3..3] of var set of 1..10: x;
var set of 1..100: y;

constraint element(i, x, y);

solve satisfy;

output [
    show(i), "\n",
    show(x), "\n",
    show(y), "\n",
];

使用标准库执行的mzn2fzn命令输出以下FlatZinc代码:

var set of 1..10: X_INTRODUCED_0_;
var set of 1..10: X_INTRODUCED_1_;
var set of 1..10: X_INTRODUCED_2_;
var set of 1..10: X_INTRODUCED_3_;
var set of 1..10: X_INTRODUCED_4_;
var set of 1..10: X_INTRODUCED_5_;
var set of 1..10: X_INTRODUCED_6_;
var -3..3: i:: output_var;
var set of 1..10: y:: output_var;
var 1..7: X_INTRODUCED_9_ ::var_is_introduced :: is_defined_var;
array [1..7] of var set of int: x:: output_array([-3..3]) = [X_INTRODUCED_0_,X_INTRODUCED_1_,X_INTRODUCED_2_,X_INTRODUCED_3_,X_INTRODUCED_4_,X_INTRODUCED_5_,X_INTRODUCED_6_];
constraint array_var_set_element(X_INTRODUCED_9_,[X_INTRODUCED_0_,X_INTRODUCED_1_,X_INTRODUCED_2_,X_INTRODUCED_3_,X_INTRODUCED_4_,X_INTRODUCED_5_,X_INTRODUCED_6_],y):: defines_var(y);
constraint int_lin_eq([1,-1],[i,X_INTRODUCED_9_],-4):: defines_var(X_INTRODUCED_9_):: domain;
solve  satisfy;

此处,请注意y模型中最初set of 1..100被声明为MiniZinc,但mzn2fzn正确传播了数组{{1}的元素上的边界转到x,以便y模型将FlatZinc声明为y

现在,我想自定义set of 1..10的内容,以便在使用element_set.mzn时调用我自己的谓词,因此我将其更改为如下所示:

element_set

但是,此代码将将数组predicate element_set(var int: i, array[int] of var set of int: x, var set of int: y) = min(index_set(x)) <= i /\ i <= max(index_set(x)) /\ optimathsat_element_set(i, x, y, index_set(x)); predicate optimathsat_element_set(var int: i, array[int] of var set of int: x, var set of int: y, set of int: xdom); 的元素上的边界传播到x,因此生成的y文件具有FlatZinc仍被宣布为y

set of 1..100

我想知道是否有人知道在predicate optimathsat_element_set(var int: i,array [int] of var set of int: x,var set of int: y,set of int: xdom); var set of 1..10: X_INTRODUCED_0_; var set of 1..10: X_INTRODUCED_1_; var set of 1..10: X_INTRODUCED_2_; var set of 1..10: X_INTRODUCED_3_; var set of 1..10: X_INTRODUCED_4_; var set of 1..10: X_INTRODUCED_5_; var set of 1..10: X_INTRODUCED_6_; var -3..3: i:: output_var; var set of 1..100: y:: output_var; %%% OFFENSIVE LINE %%% array [1..7] of var set of int: x:: output_array([-3..3]) = [X_INTRODUCED_0_,X_INTRODUCED_1_,X_INTRODUCED_2_,X_INTRODUCED_3_,X_INTRODUCED_4_,X_INTRODUCED_5_,X_INTRODUCED_6_]; constraint optimathsat_element_set(i,x,y,-3..3); solve satisfy; 上传播x域的正确编码是什么。我设法为其他y限制执行此操作,但我无法找到element_T的优雅解决方案,因为我无法找到正确的内置来执行此操作。

换句话说,我想得到

element_set

而不是

var set of 1..10: y:: output_var;

我该怎么做?

2 个答案:

答案 0 :(得分:2)

虽然有点难以找到,但索引的域实际上是在element函数的MiniZinc定义中传播的。该定义见builtins.mzn,如下所示:

function var set of int: element(var int: idx, array[int] of var set of int: x) =
  if mzn_in_root_context(idx) then let {
    constraint idx in index_set(x)
  } in element_t(idx,x)
  elseif (has_bounds(idx) /\ lb(idx) >= min(index_set(x)) /\ ub(idx) <= max(index_set(x))) then
    element_t(idx,x)
  else let {
    constraint idx in index_set(x)
  } in element_mt(idx,x)
  endif;

您可以看到您的元素约束被评估为位于第一个if-then分支中。更重要的问题是,为什么元素约束如此复杂。答案是它与未定义有关。

假设我们有array[1..3] of int: a,那么a[4]评估的是什么? 使这样的事情变得非法似乎很容易,但表达式a[i] > 5 \/ i > 10如何呢?这可能是一个经过深思熟虑的模型,应该可以满足i = 11。在MiniZinc中,这些非法值被称为undefined,并被冒泡到最近的布尔表达式为false

现在,当我们回顾元素约束时,我们可以理解,在根上下文中,就像在您的示例中一样,我们可以强制执行域并使用“安全”元素约束。类似地,如果索引的边界小于数组边界,我们可以使用“safe”元素约束,但是如果边界更大,我们使用“unsafe”元素约束并确保仅在true时返回该值已定义。

我建议您使用类似的方法或重新定义后期阶段谓词。 (可以重新定义array_var_set_element

有关MiniZinc中未定义的更多信息,请参阅以下文章:

  

Frisch,A。M.,&amp; Stuckey,P。J.(2009)。正确治疗   约束语言中的不确定性。 CP,5732,367-382。

MiniZinc使用关系语义。

答案 1 :(得分:0)

element / element_T谓词系列内部映射到element / element_T个函数系列。

MiniZinc版本2.0.2及更高版本中,这些函数映射到以下谓词:

% This file contains redefinitions of standard builtins for version 2.0.2
% that can be overridden by solvers.

...    

predicate array_var_bool_element_nonshifted(var int: idx, array[int] of var bool: x, var bool: c) =
  array_var_bool_element((idx-(min(index_set(x))-1))::domain,array1d(x),c);

predicate array_var_int_element_nonshifted(var int: idx, array[int] of var int: x, var int: c) =
  array_var_int_element((idx-(min(index_set(x))-1))::domain,array1d(x),c);

predicate array_var_float_element_nonshifted(var int: idx, array[int] of var float: x, var float: c) =
  array_var_float_element((idx-(min(index_set(x))-1))::domain,array1d(x),c);

predicate array_var_set_element_nonshifted(var int: idx, array[int] of var set of int: x, var set of int: c) =
  array_var_set_element((idx-(min(index_set(x))-1))::domain,array1d(x),c);

最佳解决方案是覆盖这些谓词,而不是弄乱element / element_T个谓词。