Minizinc: Job Shop Problem, minimize taking too long - minizinc

I have a version of the Job Shop Problem where the tasks can have multiple dependency(hard constraint) and have a deadline(soft constraint).
Finding a solution where the dependency is met and there is no overlap isn't the problem, the problem is when i try to "minimize" the number of tasks that will break the deadline. When i use the "minimize" in the "solve", the running process takes more than 30 minutes and don't finish (if i use "satisfy" it ends in 20 seconds) (for 3000 tasks using Chuffed solver).
Any ideas on how to apply the soft constraint (deadline) in a reasonable time ?
the .mzn file:
include "disjunctive.mzn";
int: dispositiveQty;
int: taskQty;
set of int: DEVICE = 0..dispositiveQty;
set of int: TASK = 1..taskQty;
array[TASK] of int: duration;
array[TASK] of DEVICE: devTask; % device on which the task will run
array[TASK] of set of TASK: dependency; % which tasks has to be finished before the task can start
array[TASK] of int: maxDateTask; % deadline of the task
int: maxTime = sum(t in TASK)(duration[t]);
array[TASK] of var 0..maxTime: taskStart;
constraint forall(t in TASK where dependency[t] != {})
(taskStart[t] >= max(d in dependency[t])(taskStart[d] + duration[d]));
int: dev1 = 125;
int: dev2 = 5;
int: dev3 = 18;
int: dev5 = 2;
int: dev6 = 786;
int: dev7 = 291;
int: dev8 = 3;
int: dev9 = 226;
int: dev10 = 906;
int: dev11 = 720;
int: dev12 = 4;
int: dev13 = 36;
int: dev15 = 4;
int: dev16 = 2;
int: dev17 = 14;
int: dev18 = 42;
int: dev21 = 2;
constraint let{array[1..dev1] of var 0..maxTime: dev1Start = [taskStart[i] | i in TASK where devTask[i] == 1],
array[1..dev1] of var int: dev1Duration = [duration[i] | i in TASK where devTask[i] == 1]
} in disjunctive(dev1Start, dev1Duration);
constraint let{array[1..dev2] of var 0..maxTime: dev2Start = [taskStart[i] | i in TASK where devTask[i] == 2],
array[1..dev2] of var int: dev2Duration = [duration[i] | i in TASK where devTask[i] == 2]
} in disjunctive(dev2Start, dev2Duration);
constraint let{array[1..dev3] of var 0..maxTime: dev3Start = [taskStart[i] | i in TASK where devTask[i] == 3],
array[1..dev3] of var int: dev3Duration = [duration[i] | i in TASK where devTask[i] == 3]
} in disjunctive(dev3Start, dev3Duration);
constraint let{array[1..dev5] of var 0..maxTime: dev5Start = [taskStart[i] | i in TASK where devTask[i] == 5],
array[1..dev5] of var int: dev5Duration = [duration[i] | i in TASK where devTask[i] == 5]
} in disjunctive(dev5Start, dev5Duration);
constraint let{array[1..dev6] of var 0..maxTime: dev6Start = [taskStart[i] | i in TASK where devTask[i] == 6],
array[1..dev6] of var int: dev6Duration = [duration[i] | i in TASK where devTask[i] == 6]
} in disjunctive(dev6Start, dev6Duration);
constraint let{array[1..dev7] of var 0..maxTime: dev7Start = [taskStart[i] | i in TASK where devTask[i] == 7],
array[1..dev7] of var int: dev7Duration = [duration[i] | i in TASK where devTask[i] == 7]
} in disjunctive(dev7Start, dev7Duration);
constraint let{array[1..dev8] of var 0..maxTime: dev8Start = [taskStart[i] | i in TASK where devTask[i] == 8],
array[1..dev8] of var int: dev8Duration = [duration[i] | i in TASK where devTask[i] == 8]
} in disjunctive(dev8Start, dev8Duration);
constraint let{array[1..dev9] of var 0..maxTime: dev9Start = [taskStart[i] | i in TASK where devTask[i] == 9],
array[1..dev9] of var int: dev9Duration = [duration[i] | i in TASK where devTask[i] == 9]
} in disjunctive(dev9Start, dev9Duration);
constraint let{array[1..dev10] of var 0..maxTime: dev10Start = [taskStart[i] | i in TASK where devTask[i] == 10],
array[1..dev10] of var int: dev10Duration = [duration[i] | i in TASK where devTask[i] == 10]
} in disjunctive(dev10Start, dev10Duration);
constraint let{array[1..dev11] of var 0..maxTime: dev11Start = [taskStart[i] | i in TASK where devTask[i] == 11],
array[1..dev11] of var int: dev11Duration = [duration[i] | i in TASK where devTask[i] == 11]
} in disjunctive(dev11Start, dev11Duration);
constraint let{array[1..dev12] of var 0..maxTime: dev12Start = [taskStart[i] | i in TASK where devTask[i] == 12],
array[1..dev12] of var int: dev12Duration = [duration[i] | i in TASK where devTask[i] == 12]
} in disjunctive(dev12Start, dev12Duration);
constraint let{array[1..dev13] of var 0..maxTime: dev13Start = [taskStart[i] | i in TASK where devTask[i] == 13],
array[1..dev13] of var int: dev13Duration = [duration[i] | i in TASK where devTask[i] == 13]
} in disjunctive(dev13Start, dev13Duration);
constraint let{array[1..dev15] of var 0..maxTime: dev15Start = [taskStart[i] | i in TASK where devTask[i] == 15],
array[1..dev15] of var int: dev15Duration = [duration[i] | i in TASK where devTask[i] == 15]
} in disjunctive(dev15Start, dev15Duration);
constraint let{array[1..dev16] of var 0..maxTime: dev16Start = [taskStart[i] | i in TASK where devTask[i] == 16],
array[1..dev16] of var int: dev16Duration = [duration[i] | i in TASK where devTask[i] == 16]
} in disjunctive(dev16Start, dev16Duration);
constraint let{array[1..dev17] of var 0..maxTime: dev17Start = [taskStart[i] | i in TASK where devTask[i] == 17],
array[1..dev17] of var int: dev17Duration = [duration[i] | i in TASK where devTask[i] == 17]
} in disjunctive(dev17Start, dev17Duration);
constraint let{array[1..dev18] of var 0..maxTime: dev18Start = [taskStart[i] | i in TASK where devTask[i] == 18],
array[1..dev18] of var int: dev18Duration = [duration[i] | i in TASK where devTask[i] == 18]
} in disjunctive(dev18Start, dev18Duration);
constraint let{array[1..dev21] of var 0..maxTime: dev21Start = [taskStart[i] | i in TASK where devTask[i] == 21],
array[1..dev21] of var int: dev21Duration = [duration[i] | i in TASK where devTask[i] == 21]
} in disjunctive(dev21Start, dev21Duration);
var int: aboveDeadline = sum(i in TASK where maxDateTask[i] < (taskStart[i] + duration[i]))(1);
solve :: int_search([taskStart[t] | t in TASK], input_order, indomain_min, complete) satisfy;%minimize aboveDeadline;
the .dzn file: https://www.dropbox.com/s/94n7fqxzcai5tvf/testData.dzn?dl=0
(i can't put the .dzn file directly because it exceeds the character limit)

By introducing a new parameter tasks, which holds the number of tasks per device, all of the disjunctive constraints can be generated by a single forall loop. (Even better would be to move the tasks parameter to the data file, then the same model could be used across the different instances).
Edit: tasks can also be directly derived as array[DEVICE] of int: tasks = [sum(t in TASK)(d == devTask[t]) | d in DEVICE]; given DEVICE is re-defined as set of int: DEVICE = 1..dispositiveQty;
Using the model below, Chuffed finds a solution to the larger instance in ~10s.
include "disjunctive.mzn";
int: dispositiveQty;
int: taskQty;
set of int: DEVICE = 0..dispositiveQty;
set of int: TASK = 1..taskQty;
array[TASK] of int: duration;
array[TASK] of DEVICE: devTask; % device on which the task will run
array[TASK] of set of TASK: dependency; % which tasks has to be finished before the task can start
array[TASK] of int: maxDateTask; % deadline of the task
int: maxTime = sum(t in TASK)(duration[t]);
array[TASK] of var 0..maxTime: taskStart;
constraint forall(t in TASK, d in dependency[t])
(taskStart[t] >= taskStart[d] + duration[d]);
array[int] of int: tasks = [125, 5, 18, 0, 2, 786, 291, 3, 226, 906, 720, 4, 36, 0, 4, 2, 14, 42, 0, 0, 2];
constraint forall(d in index_set(tasks) where tasks[d] > 0)
(let {array[1..tasks[d]] of var int: taskStarts = [taskStart[i] | i in TASK where devTask[i] == d];
array[1..tasks[d]] of var int: durations = [duration[i] | i in TASK where devTask[i] == d]}
in disjunctive(taskStarts, durations));
var int: aboveDeadline = sum(i in TASK)(bool2int(taskStart[i] + duration[i] > maxDateTask[i]));
solve :: int_search(taskStart, input_order, indomain_min, complete)
minimize aboveDeadline;
output ["aboveDeadline=\(aboveDeadline)"];

Related

Want caculate the n-th largest value in an array Use Minizinc

Any boys can help me write a function to caculate the n-th largest value in an array ?
for example :
array[1..10] of 1..200 : t=[30,20,30,40,50,50,70,10,90,100];
var int : nth_largest;
In general to determine the nth largest numbers you first have to determine the n-1 larger numbers. If there is only 1 or 2, then you might be able to do something smarter, but in general I think the best supported solution is to use the arg_sort function to determine the order of all elements, and then choose the nth largest one.
This can be written as the following function:
function var int: nth_largest(array[$$E] of $$V: x, var int: n) =
let {
any: perm = arg_sort(x);
} in x[perm[length(x)-n]];
In your fragment this can be used as:
array[1..10] of 1..200: t= [30,20,30,40,50,50,70,10,90,100];
var int: nth_largest ::output = nth_largest(t, 2);
function var int: nth_largest(array[$$E] of $$V: x, var int: n) =
let {
any: perm = arg_sort(x);
} in x[perm[length(x)-n]];
Although you might want to find a more meaningful name for the variable, since shadowing names of variables and functions is generally frowned upon.
Reading #Dekker1's solution, here's what I had in mind. Instead of sort(x,y) the function y = sort(x) is used.
include "globals.mzn";
int: n = 10;
int: nth = 3;
array[1..n] of 1..200: t = [30,20,30,40,50,50,70,10,90,100];
var 1..200: nth_largest;
constraint
nth_largest = sort(t)[n-nth+1]
;
Same idea using a function:
include "globals.mzn";
int: n = 10;
int: nth = 3;
array[1..n] of 1..200: t = [30,20,30,40,50,50,70,10,90,100];
var 1..200: nth_largest;
function var int: nth_largest_val(array[int] of var int: a, var int: m) =
sort(a)[length(a)-m+1]
;
constraint
nth_largest = nth_largest_val(t,nth)
;
Both give:
nth_largest = 70;
Update: Ignoring duplicates
Here is a variant which ignores duplicate values. nthis here a decision variable so we can see all solutions (and it makes it possible to "reverse" the constraint, see below).
include "globals.mzn";
int: n = 10;
var 1..n: nth; % : nth = 4;
array[1..n] of 1..200: t = [30,20,30,40,50,50,70,10,90,100];
var 1..200: nth_largest;
% Skipping duplicates
function var int: nth_largest_skip_dups(array[int] of var int: a, var int: nth) =
let {
var lb_array(a)..ub_array(a): v;
constraint
% count the number of larger values in the unique set
sum([ k > v | k in {a[j] | j in index_set(a) } ]) = nth -1
% ensure that v is in a
/\ exists(i in index_set(a)) (
v = a[i]
);
} in
v
;
constraint
nth_largest = nth_largest_skip_dups(t,nth)
;
output ["nth:\(nth)\nlargest: \(nth_largest)\n" ];
Here's the output:
nth:8
largest: 10
----------
nth:7
largest: 20
----------
nth:1
largest: 100
----------
nth:2
largest: 90
----------
nth:3
largest: 70
----------
nth:4
largest: 50
----------
nth:5
largest: 40
----------
nth:6
largest: 30
----------
Note about reversibility: If the following constraint is added the the constraint is reversed, i.e. we get what rank of the number 50:
constraint nth_largest = 50;
The output:
nth:4
largest: 50
A drawback is that it might be costly for large arrays, and it's better to use the first suggestion if you can ensure that the array has no duplicates.

Minizinc strange behaviour

I have recently started studying minizinc, but I have got this strange behaviour in my program.
.dzn
n = 5;
c = 2;
.mzn
include "globals.mzn";
int: n;
int: c;
set of int: num_deliveries = 1..n-1;
int: headquarter = 1;
set of int: num_places = 1..n;
set of int: deliveries = 2..n;
set of int: couriers = 1..c;
set of int: num_max_deliveries = 1..n+2;
set of int: schedule_domain = 0..n;
int: first_place_idx = 1;
int: last_place_idx = n+2;
array[couriers,num_max_deliveries] of var schedule_domain: schedule;
array[1..2*n] of int: total = [schedule[i,j]|i,j in num_max_deliveries where i<=2 /\ j != first_place_idx /\ j!= last_place_idx];
output ["len_without_variable = \(length([ k | k in total where k != 0]))"];
var int: len_cleaned = length([ k | k in total where k != 0]);
output ["len_with_variable = \(len_cleaned)\n"];
In particular, from these lines of code I have different results, even if they are equal.
output ["len_without_variable = \(length([ k | k in total where k != 0]))"];
var int: len_cleaned = length([ k | k in total where k != 0]);
output ["len_with_variable = \(len_cleaned)\n"];
Why does it happen?
Here is one output of the model is this (with total added):
len_without_variable = 2
len_with_variable = 10
total: [3, 3, 0, 0, 0, 0, 0, 0, 0, 0]
To be honest, I'm not sure if the two calculations of the number of non zero elements in total should be the same or not. Perhaps this is a bug in how length operates on decision variables (with a where condition), and it should be 2 (in this example). Until this is settled, you should probably avoid using length like this.
The output of len_without_variable - the one defined in the output section - operates on the actual and known values of the solution. So this might not be the exact thing as using length in a constraint/variable definition.
To calculate the number of non zero values you can use sum instead:
var 0..n num_non_zeros = sum([ 1 | k in total where k != 0]);
However, the construct ... where k != 0 creates temporary variables which makes the model larger than necessary so it's better to use the following:
var 0..n num_non_zeros = sum([ total[i] != 0 | i in 1..n]);

Swift: how to more accurately detect if a symbol inside or outside of the wall?

Map is drawn with text content like below:
#######..
#.....#..
#...#####
#...##..#
#...##..#
#....#...
######...
#: wall
.: blank (use # as a special . on above)
Since there are two kinds of blank, inside and outside the wall. I need to figure them out separately.
The inside . will convert to 2, the outside . will convert to 0
By now, I'm using a method that search the four directions of each dot's (up, down, left, right), if all of them are #, the dot is inside wall.
First, convert each line to array lines
var wallCount = 0
for i in 0...lines.count{
let line = lines[i]
// ignore first and last character
for j in 1...line.count-2 {
let char = String(Array(line)[j])
if char == "." {
var wallCount = 0
// left
var l = j
while (l >= 0) {
let lChar = String(Array(line)[l])
if(lChar == "#"){
wallCount += 1
break;
}
l -= 1
}
// and then...
// right
// up
// down
}
}
}
It maybe not efficient, and it's ok in most cases, except in one case it's ineffective: from the map above, it figures that # is inside the wall. Since it has # in four directions, but actually it stands outside the wall.
How to improve the above code more efficient and accurate?
Thanks.
The idea
We can build a recursive function to look for a path from the hero to the border.
If such a path exists then the hero is outside of the walls.
Otherwise he's inside the walls.
The recursive function receives a cell index as paramenter along with the set of the visited indexes.
If this cell is on the border then the function returns true.
Otherwise it evaluates all the cells adjacents to current one that are free and have not been visited.
For each one of these cells the function is called recursively until a path to the border is found or all the cells have been visited.
Code!
We need a proper way of representing and interacting with the Map.
I am going to use the Matrix struct described in The Swift Programming Language and will add some customizations.
The Matrix 🕶
struct Matrix {
enum Cell: String {
case wall = "#"
case empty = "."
case hero = "#"
}
struct Index: Hashable {
let x, y: Int
var adjacents: [Index] {
return [Index(x: x - 1, y: y), Index(x: x + 1, y: y), Index(x: x, y: y - 1), Index(x: x, y: y + 1)]
}
}
private let rows: Int, columns: Int
private var grid: [Cell]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(repeating: .empty, count: rows * columns)
}
private func indexIsValid(index: Index) -> Bool {
return indexIsValid(row: index.x, column: index.y)
}
private func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Cell {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
var description: String {
var res = ""
for row in 0..<rows {
for column in 0..<columns {
res += self[row, column].rawValue
}
res += "\n"
}
return res
}
private func indexIsOnBorder(_ index: Index) -> Bool {
return index.x == 0 || index.y == 0 || index.x == columns - 1 || index.y == rows - 1
}
func findPathToBorder(from index: Index,
visitedIndexes: inout Set<Index>) -> Bool {
visitedIndexes.insert(index)
if matrix.indexIsOnBorder(index) {
return true
}
return index
.adjacents // up, right, down, left
.filter(indexIsValid) // which are inside the matrix
.filter { matrix[$0.x, $0.y] == .empty } // are empty
.filter { !visitedIndexes.contains($0) } // not visited yet
.contains { adjacent in
findPathToBorder(from: adjacent, visitedIndexes: &visitedIndexes)
} // and are part of a path to the border
}
}
Populating the Matrix
var matrix = Matrix(rows: 7, columns: 9)
matrix[0, 0] = .wall
matrix[0, 1] = .wall
matrix[0, 2] = .wall
matrix[0, 3] = .wall
matrix[0, 4] = .wall
matrix[0, 5] = .wall
matrix[0, 6] = .wall
matrix[1, 0] = .wall
matrix[1, 6] = .wall
matrix[2, 0] = .wall
matrix[2, 4] = .wall
matrix[2, 5] = .wall
matrix[2, 6] = .wall
matrix[2, 7] = .wall
matrix[2, 8] = .wall
matrix[3, 0] = .wall
matrix[3, 4] = .wall
matrix[3, 5] = .hero
matrix[3, 8] = .wall
matrix[4, 0] = .wall
matrix[4, 4] = .wall
matrix[4, 5] = .wall
matrix[4, 8] = .wall
matrix[5, 0] = .wall
matrix[5, 5] = .wall
matrix[6, 0] = .wall
matrix[6, 1] = .wall
matrix[6, 2] = .wall
matrix[6, 3] = .wall
matrix[6, 4] = .wall
matrix[6, 5] = .wall
print(matrix.description)
Now you'll see this in the console
#######..
#.....#..
#...#####
#...##..#
#...##..#
#....#...
######...
Running our function
var visitedIndexes: Set<Matrix.Index> = []
let isHeroOutsideTheWalls = matrix.findPathToBorder(from: Matrix.Index(x: 3, y: 2), visitedIndexes: &visitedIndexes)
print(isHeroOutsideTheWalls) // true
Hope this helps.

MiniZinc does not find a solution to the scheduling problem

I have a problem finding a solution using MiniZinc.
The task:
It is necessary to make a schedule of shifts for employees.
In one day there are three shifts: day (D), evening (E) and night (N).
It is necessary to draw up an optimal schedule, if possible avoiding undesirable situations:
Avoid single shifts (one shift between two breaks)
Avoid single breaks (shift, break, shift)
Avoid double breaks (shift, break, break, shift)
After a night shift should be a full day off (three breaks in a row)
To find a solution, I minimize the number of undesirable situations.
When I start the calculation, MiniZinc displays several intermediate variants, but does not find a final solution.
Is it possible to somehow optimize the calculations?
include "regular.mzn";
int: n = 21;
int: m = 6;
set of int: D = 1..n;
set of int: E = 1..m;
% Number of employees per shift
%|Sun |Mon |Tue |Wen |Thur |Fri |Sat |
array[D] of int: SHIFTS = [2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1];
/*2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1,
2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1,
2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 2];*/
% The range of the number of shifts per employee for the period ([|from, to)
array[E, 1..2] of int: DC_SHIFTS = [|0, 10 %emp1
|0, 10 %emp2
|0, 10 %emp3
|0, 10 %emp4
|0, 10 %emp5
|0, 10 %emp6
|];
%-------------------------------------------------
% Variables
%-------------------------------------------------
array[E, D] of var 1..4: X;
% Counters of avoidable situations
var int: OS_PENALTY; % break, shift, break (single shift)
var int: NS_PENALTY; % night shift, not break, not break, not break (full day off after a night shift)
var int: DS_PENALTY; % shift, break, break, shift (two breaks between shifts)
var int: OO_PENALTY; % shift, break, shift (one break between shifts)
%-------------------------------------------------
% Constraints
%-------------------------------------------------
constraint
forall(d in D)(
sum(e in E)(bool2int(X[e, d] != 4)) = SHIFTS[d]
);
constraint
forall(e in E)(
sum(d in D)(bool2int(X[e, d] != 4)) >= DC_SHIFTS[e, 1]
/\
sum(d in D)(bool2int(X[e, d] != 4)) < DC_SHIFTS[e, 2]
);
constraint
forall(d in D)(
if d mod 3 = 1 then forall(e in E)(X[e, d] = 1 \/ X[e, d] = 4) else
if d mod 3 = 2 then forall(e in E)(X[e, d] = 2 \/ X[e, d] = 4) else
forall(e in E)(X[e, d] = 3 \/ X[e, d] = 4) endif endif
);
NS_PENALTY = sum(e in E, d in D where d < max(D) - 2)(bool2int(
X[e, d] = 3 \/ (X[e,d+1] != 4 /\ X[e,d + 2] != 4 /\ X[e,d + 3] != 4)
));
DS_PENALTY = sum(e in E, d in D where d < max(D) - 2)(bool2int(X[e, d] != 4 \/ X[e, d + 1] = 4 \/ X[e, d + 2] = 4 \/ X[e, d + 3] != 4));
OS_PENALTY = sum(e in E, d in D where d < max(D) - 1)(bool2int(X[e, d] = 4 /\ X[e, d + 1] != 4 /\ X[e, d + 2] = 4));
OO_PENALTY = sum(e in E, d in D where d < max(D) - 1)(bool2int(X[e, d] != 4 \/ X[e, d + 1] = 4 \/ X[e, d + 2] != 4));
%-------------------------------------------------
% Solve
%-------------------------------------------------
solve minimize OS_PENALTY + NS_PENALTY + DS_PENALTY + OO_PENALTY;
%-------------------------------------------------
% Output
%-------------------------------------------------
array[1..4] of string: rest_view = ["D", "E", "N", "-"];
output
[
rest_view[fix(X[e, d])] ++
if d = n then "\n" else "" endif
| e in E, d in D
];
I suggest the following changes to your model:
Change the declaration of X to array[E, D] of var 0..1: X; where 0 means break and 1 shift. Whether it's a day, evening or night shift is handled in the output section, where the results are transformed to show the shift type like if fix(X[e, d]) == 0 then "-" else rest_view[1 + (d-1) mod 3] endif.
Rewrite the constraints using globals like:
import "globals.mzn";
constraint
forall(d in D)(
exactly(SHIFTS[d], col(X, d), 1)
%sum(e in E)(bool2int(X[e, d] != 0)) = SHIFTS[d]
);
constraint
forall(e in E)(
global_cardinality_low_up(row(X, e), [1], [DC_SHIFTS[e, 1]], [DC_SHIFTS[e, 2] - 1])
%sum(d in D)(bool2int(X[e, d] != 0)) >= DC_SHIFTS[e, 1]
%/\
%sum(d in D)(bool2int(X[e, d] != 0)) < DC_SHIFTS[e, 2]
);
%constraint
% forall(d in D)(
% if d mod 3 = 1 then forall(e in E)(X[e, d] = 1 \/ X[e, d] = 4) else
% if d mod 3 = 2 then forall(e in E)(X[e, d] = 2 \/ X[e, d] = 4) else
% forall(e in E)(X[e, d] = 3 \/ X[e, d] = 4) endif endif
% );
Rewrite the penalties like:
NS_PENALTY = sum(e in E, d in 1..n - 3 where d mod 3 = 0)(bool2int(
X[e, d] = 1 /\ (sum(i in 1..3)(X[e,d+i]) > 0)
));
DS_PENALTY = sum(e in E, d in 1..n - 3)(bool2int(X[e, d] != 0 /\ X[e, d + 1] = 0 /\ X[e, d + 2] = 0 /\ X[e, d + 3] != 0));
OS_PENALTY = sum(e in E, d in 1..n - 2)(bool2int(X[e, d] = 0 /\ X[e, d + 1] != 0 /\ X[e, d + 2] = 0));
OO_PENALTY = sum(e in E, d in 1..n - 2)(bool2int(X[e, d] != 0 /\ X[e, d + 1] = 0 /\ X[e, d + 2] != 0));

Improve performance of maximum filter in 2D array by shape

Let say I have a 2D array
let img = [[0, 1, 2, 1, 3, 0],
[1, 1, 1, 1, 1, 1],
[1, 2, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1],
[0, 1, 4, 1, 5, 0],
]
let shape = [[0,1,0],
[1,1,1],
[0,1,0]]
let diamon_shape = [[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[1, 1, 1, 1, 1],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0]]
I place center of the shape(diamond) onto each column then each row to get max number inside the shape (=1) then replace center of shape by the max number. This likes dilation and erosion in image morphology
Here is my implementation in Swift:
class func maximum_filter(image:[[Int]], shape:[[Int]]) -> [[Int]]{
let wShape = shape[0].count
let hShape = shape.count
let wImage = image[0].count
let hImage = image.count
var final = Array(repeating: Array(repeating: 0.0, count: image[0].count), count: image.count)
for i in 0..<hImage {
for ii in 0..<wImage {
var startOfWZ = 0
var startOfHZ = 0
var wStart = ii - wShape/2
if wStart < 0 {
wStart = 0
startOfWZ = 1
}
var wEnd = ii + wShape/2
if wEnd >= wImage {
wEnd = wImage - 1
}
var hStart = i - hShape/2
if hStart < 0 {
hStart = 0
startOfHZ = 1
}
var hEnd = i + hShape/2
if hEnd >= hImage {
hEnd = hImage - 1
}
var hz = startOfHZ
var maxNumber = 0.0
for x in hStart...hEnd {
var wz = startOfWZ
for xx in wStart...wEnd {
if shape[hz][wz] == 1 {
let currentNumber = image[x][xx]
if currentNumber > maxNumber {
maxNumber = currentNumber
}
}
wz += 1
}
hz += 1
}
final[i][ii] = maxNumber
}
}
return final
}
First 2 loops I iterate each element of matrix to place center of the shape onto. Then next 2 loops I get all elements of image map with elements (=1) of shape then compare them to get maximum number. Nothing complicated.
The result is :
1 2 2 3 3 3
1 2 2 1 3 1
2 2 2 1 1 1
1 2 4 1 5 1
1 4 4 5 5 5
But when I try with real image 4096x4096(The input in Double not in Int in sample) and the diamond shape is 41x41. The performance is super slow (10 seconds) compared with python(1 second). Here the code i use in python result = maximum_filter(img, footprint=shape). I couldn't see the source code of maximum_filter to follow, so I implement it by my self. I got same result but the performance is much slower than their's.
You can't expect to achieve the same performance as a specialized function from a framework using your the fist algorithm that comes to mind. The code behind that Python function is probably optimized using advanced memory management techniques and different logic.
Just as an example of the kind of difference this can mean, here's a naive algorithm for the same function that performs 20x faster than yours on a 4096 x 4096 image with a 41x41 crosshair shape :
func naiveFilter(image:[[Int]], shape:[[Int]]) -> [[Int]]
{
let imageRows = image.count
let imageCols = image.first!.count
let shapeRows = shape.count
let shapeCols = shape.first!.count
let shapeCenterRow = shapeRows / 2
let shapeCenterCol = shapeCols / 2
let outerRowIndex = imageRows - shapeRows + shapeCenterRow
let outerColIndex = imageCols - shapeCols + shapeCenterCol
let shapeOffsets = shape.enumerated().flatMap{ row,rowFlags in rowFlags.enumerated().filter{$1 == 1}
.map{(row - shapeCenterRow, $0.0 - shapeCenterCol) } }
var maxValues = image
var imageRow = 0
var imageCol = 0
var imageValue = 0
var maxValue = 0
for row in (0..<imageRows)
{
let innerRow = row >= shapeCenterRow && row < outerRowIndex
for col in (0..<imageCols)
{
maxValue = 0
if innerRow && col >= shapeCenterCol && col < outerColIndex
{
for (rowOffset,colOffset) in shapeOffsets
{
imageValue = image[row+rowOffset][col+colOffset]
if imageValue > maxValue { maxValue = imageValue }
}
}
else
{
for (rowOffset,colOffset) in shapeOffsets
{
imageRow = row + rowOffset
imageCol = col + colOffset
guard imageRow < imageRows else { break }
if imageRow >= 0
&& imageCol >= 0
&& imageCol < imageCols
{
imageValue = image[row+rowOffset][col+colOffset]
if imageValue > maxValue { maxValue = imageValue }
}
}
}
if maxValue > 0 { maxValues[row][col] = maxValue }
}
}
return maxValues
}
I didn't even go into flat memory models, range check and retain cycle optimization, register shifting, assembly code, or any of the 100s of techniques that could have been used in the code you didn't see.