Closed ozgurakgun closed 4 years ago
Hi Oz!
Sorry it took so longgg to have it! Here we go! :D
In this first proposal, we focus on partial function only. It should be extended to other types later on when this one is tested successfully. Here I assume that we already replace middle/delta as min/max (pull request).
What we need is a repair.essence
file generated automatically by conjure generator-parameter
. This model receive as input a generator configuration. Solving it will give us a repaired version of the generator configuration. There are two groups of repairing constraints:
min<=max
$ model.essence
given f: function (minSize 1) int(1..10) --> int(1..)
$ generator.essence
given f_cardMin: int(1..100)
given f_cardMax: int(1..100)
given f_defined_min: int(1..10)
given f_defined_max: int(1..10)
given f_range_min: int(1..100)
given f_range_max: int(1..100)
...
The repair.essence
should include:
generator.essence
as input (including the percentage one for boolean function in general cases, even though it doesn't need to be repaired), and a repaired_
decision variable for each of them
$ repair.essence
given f_cardMin: int(1..100)
given f_cardMax: int(1..100)
given f_defined_min: int(1..10)
given f_defined_max: int(1..10)
given f_range_min: int(1..100)
given f_range_max: int(1..100)
find repaired_f_cardMin: int(1..100) find repaired_f_cardMax: int(1..100) find repaired_f_defined_min: int(1..10) find repaired_f_defined_max: int(1..10) find repaired_f_range_min: int(1..100) find repaired_f_range_max: int(1..100)
$ Constraints to make sure that the the repaired values always follow the rules of min<=max
such that
repaired_f_cardMin <= repaired_f_cardMax,
repaired_f_defined_min <= repaired_f_defined_max,
repaired_f_range_min <= repaired_f_range_max
$ Constraints represent the dependency between cardinality and the defined domain of a partial function such that (repaired_f_defined_max-repaired_f_defined_min+1) >= repaired_f_cardMax
minimising |repaired_f_cardMin-f_cardMin| +|repaired_f_cardMax-f_cardMax| +|repaired_f_defined_min-f_defined_min| +|repaired_f_defined_max-f_defined_max| +|repaired_f_range_min-f_range_min| +|repaired_f_range_max-f_range_max|
Note: You may wonder if this repairing model would do something like this: given `x_min=10` and `x_max=4`, we may end up having the repaired version as `x_min=x_max=10` or `x_min=x_max=4`. But it won't happen, as in my repairing script, I already fixed those in advance by simply swapping values of min and max variables if `min>max` before giving them to `repair.essence` as input, i.e., `x_min=4` and `x_max=10`. The constraints shown in `repair.essence` are just to make sure that the `repaired_` values of those variables still follow this rule of `min<=max`.
### Example 2: a partial function with domain's upper bound depending on another variable
When the partial function's domain's upper bound is defined by another variable, we have an extra constraint on the dependency.
$model.essence given m: int(1..) given f: function (minSize 1) int(1..m) --> int(1..)
$ repair.essence ... such that repaired_f_defined_max <= repaired_m_max, $ this is the extra constraint (repaired_f_defined_max-repaired_f_defined_min+1) >= repaired_f_cardMax ...
### Example 3: a partial function with domain's lower bound and domain's upper bound depending on other variables
Similarly, when the partial function's domain's lower and upper bounds are defined by other variables, we have two extra constraints on the dependency.
$ model.essence given m1: int(1..) given m2: int(1..) given f: function (minSize 1) int(m1..m2) --> int(1..)
$ repair.essece ... such that repaired_f_defined_min >= repaired_m1_min, $ this is the extra constraint repaired_f_defined_max <= repaired_m2_max, $ this is the extra constraint (repaired_f_defined_max-repaired_f_defined_min+1) >= repaired_f_cardMax ...
### Example 4: the same as example 3, but with a boolean function
For boolean function, we have the `f_percentage` param for the generator spec. We don't need to do any repairing for such param, so the only thing we need is to have a constraint to make sure that the repaired one is equal to the original value.
$ model.essence given m1: int(1..) given m2: int(1..) given f: function (minSize 1) int(m1..m2) --> bool
$ repair.essence ... given f_percentage: int(0..100) find repaired_f_percentage: int(0..100) such that repaired_f_percentage = f_percentage $ this is the repairing constraint ...
[testcase-01.zip](https://github.com/conjure-cp/conjure/files/4535806/testcase-01.zip)
[testcase-02.zip](https://github.com/conjure-cp/conjure/files/4535808/testcase-02.zip)
[testcase-03.zip](https://github.com/conjure-cp/conjure/files/4535809/testcase-03.zip)
[testcase-04.zip](https://github.com/conjure-cp/conjure/files/4594082/testcase-04.zip)
Although I have reservations about whether we can always solve the repair in reasonable time (these kinds of problems can be very hard, and if we are assuming that repair is quick and easy when it is not, then it might be better just giving up on the current parameter vector and trying another one), I do support the change to min/max. It made sense to use mid/delta but it is becoming more and more difficult to justify it now.
@ott2: regarding the difficulty of the repairing problem, the constraints described above seem all trivial to solve to me. But it's just simply my guess, as I don't have experience with minion's solving procedure. If we do find that they are difficult, or if the repairing instances are too large to go through the pipeline (which I don't really expect to happen, as the repairing model is pretty flat), then we could consider using Athanor instead of minion to solve the repairing problem, as proving unsat is not a necessary requirement. (we still need to use minion for solving the generator spec though, as we need the ability to prove unsat in that case).
Some other reasons why I think we should use the repair:
Example 4: I don't think this one is necessary since we are minimising the difference.
@ozgurakgun: you're right! We don't need it indeed! :)
here it is! let me know if anything is broken please.
Thanks very much for the implementation, Oz!
I currently get three issues. Could you please help to have a look? :D
$ model-instanceRepair.essence
given m1_min: int(1..100)
given m1_max: int(1..100)
given f_defined_min: int(1..100)
given f_defined_max: int(1..100)
such that
repaired_f_defined_min >= m1, $ corrected version: repaired_m1_min instead of m1
repaired_f_defined_max <= m2, $ corrected version: repaired_m2_max instead of m2
...
model.essence.txt model-instanceRepair.essence.txt model-instanceRepair-corrected.essence.txt
such that
keyword.$ model.essence
given f1: function (total) int(1..10) --> bool
$ model-instanceRepair.essence
given f1_percentage: int(0..100)
find repaired_f1_percentage: int(0..100)
such that $ this line should be removed
f1_cardMin <= f1_cardMax, $ this line should be removed
f1_defined_min >= 1, $ this line should be removed
f1_defined_max <= 10, $ this line should be removed
f1_defined_min <= f1_defined_max $ this line should be removed
minimising sum([|repaired_f1_percentage - f1_percentage|; int(1)])
model.essence.txt model-instanceRepair.essence.txt
conjure
.$ model.essence
given r: record { f: function int(1..10) --> bool }
$ model-instanceRepair.essence
given r_f_cardMin: int(0..10)
given r_f_cardMax: int(0..10)
given r_f_defined_min: int(1..10)
given r_f_defined_max: int(1..10)
given r_f_percentage: int(0..100)
find repaired_r_f_cardMin: int(0..10)
find repaired_r_f_cardMax: int(0..10)
find repaired_r_f_defined_min: int(1..10)
find repaired_r_f_defined_max: int(1..10)
find repaired_r_f_percentage: int(0..100)
such that $ all repairing constraints are missing
minimising
sum([|repaired_r_f_cardMin - r_f_cardMin|, |repaired_r_f_cardMax - r_f_cardMax|,
|repaired_r_f_defined_min - r_f_defined_min|, |repaired_r_f_defined_max - r_f_defined_max|,
|repaired_r_f_percentage - r_f_percentage|;
int(1..5)])
Hi Oz,
Please find below some of the issues I still have:
$ model.essence
given m1, m2: int(1..)
given f: function (minSize 1) int(m1..m2) --> int(1..)
$ model-instanceRepair.essence
....
such that
repaired_f_defined_min >= m1_min, $ correcting: repaired_m1_min instead of m1_min
repaired_f_defined_max <= m2_max, $ correcting: repaired_m2_max instead of m2_max
...
The reason why I'd like to have repaired_m1_min
instead of m1_min
is because it provides more flexibility for the repairing. Consider this case, for example: f_defined_min
and f_cardMin
are already good, we'd only want to repair m1_min
to satisfy the constraints.
defined_min
and defined_max
still appear in repair essence for total functions$ model.essence
given f: function (total) int(1..10) --> bool
$ model-instanceRepair.essence
...
such that f_defined_min <= f_defined_max $ corrected: this line should be removed
...
$ model.essence
given m1,m2: int(1..)
given r: record {
f: function int(0+m1..1+m2) --> bool
}
$ model-instanceRepair.essence
...
such that
repaired_r_f_defined_min >= 0 + m1, $ corrected: 0 + repaired_m1_min
repaired_r_f_defined_max <= 1 + m2, $ corrected: 1 + repaired_m2_max
...
$ model.essence
given m1,m2,m3: int(1..)
letting d be domain int(m1..m1+m2,m1+m2+1..m1+m2+m3)
given f: function d --> bool
$ model-instanceRepair.essence
...
such that
$ correction 1: max -> min
$ correction 2 (testcase-07): m1 -> repaired_m1_min, m2 -> repaired_m2_min
repaired_f_defined_min >= max([m1, m1 + m2 + 1; int(1..2)]),
$ correction 1: min -> max
$ correction 2 (testcase-07): m1 -> repaired_m1_max, m2 -> repaired_m2_max, m3 -> repaired_m3_max
repaired_f_defined_max <= min([m1 + m2, m1 + m2 + m3; int(1..2)]),
...
Sorry I didn't realise this case in my original post. Classical planning models are so painful :P
$ model.essence
given m1, m2: int(1..)
letting d1 be domain int(1..m1)
letting d2 be domain int(1..m2)
given f: function (d1,d2) --> bool
$ model-instanceRepair.essence
...
find repaired_f_defined_tuple1_min: int(1..100)
find repaired_f_defined_tuple1_max: int(1..100)
find repaired_f_defined_tuple2_min: int(1..100)
find repaired_f_defined_tuple2_max: int(1..100)
such that
$ current output, this causes issues as there're no f_defined_min and f_defined_max
$f_defined_max - f_defined_min + 1 >= repaired_f_cardMax,
$ corrected output
(repaired_f_defined_tuple1_max-repaired_f_defined_tuple1_min+1)*(repaired_f_defined_tuple2_max-repaired_f_defined_tuple2_min+1) >= repaired_f_cardMax,
...
ball in your court again @ndangtt :)
I won't bother closing the issue this time, feel free to close if you are happy with everything.
It passes all the tests so far, so I'm gonna close the issue :)
Oooooh - I was developing an emotional connection with this issue :)
Maybe open a new issue so I can stop thinking about the number 465 if/when new issues arise?
Haha yes I will. Let's leave this one rest in peace :P
A subset of the where statements can be converted to irace forbidden statements. Waiting for input from @ndangtt on this.