If I have a 2D decision variable array and a 1D array of set, how do I add constraint to check that values in decision variables should match from 1D array?
For example,
array[1..3, 1..2] of var 1..10: x;
array[1..3] of set of 1..10: y;
And y is = [{2, 6, 7, 8},
{1, 2, 3},
{1, 2, 7, 8, 1, 5, 1},
];
I want to add constraint that ensures that x keeps only values contained in y.
For example, the rule is that values of x0, x1 should be one among first set of values of y {2, 6, 7, 8}. Values of x2, x3 should be amonst the second set of values of y {1, 2, 3} and so on.
Thanks
If y is defined as parameter (i.e. not defined with var) as in your example, then you can extract the values of y as a set (vals) like this:
array[1..3] of set of 1..10: y = [{2, 6, 7, 8},
{1, 2, 3},
{1, 2, 7, 8, 1, 5, 1},
];
% convert the values
set of int: vals = { val | row in y, val in row };
array[1..3, 1..2] of var vals : x;
output [
"vals:\(vals)\nx:\(x)\n"
];
Update: Here's a suggestion for the updated version of the question. Here we constrain the x variables in the i'th to values be only the i'th row in y, i.e. the i'th set in y. As before I assume that the values in y are plain integers (not decision variables):
array[1..3] of set of 1..10: y = [{2, 6, 7, 8},
{1, 2, 3},
{1, 2, 7, 8, 1, 5, 1},
];
array[1..3, 1..2] of var int : x;
% Ensure that the i'th row in x only contains values
% that are in the i'th row/set of y
constraint
forall(i in 1..3) (
forall(j in 1..2) (
x[i,j] in y[i]
)
)
;
output [
"x:\(x)\n",
];
Related
I am trying to find an efficient way of extracting groups of n consecutive columns in a matrix. Example:
A = [0, 1, 2, 3, 4; 0, 1, 2, 3, 4; 0, 1, 2, 3, 4];
n = 3;
should produce an output similar to this:
answer = cat(3, ...
[0, 1, 2; 0, 1, 2; 0, 1, 2], ...
[1, 2, 3; 1, 2, 3; 1, 2, 3], ...
[2, 3, 4; 2, 3, 4; 2, 3, 4]);
I know this is possible using a for loop, such as the following code snippet:
answer = zeros([3, 3, 3]);
for i=1:3
answer(:, :, i) = A(:, i:i+2);
endfor
However, I am trying to avoid using a for loop in this case - is there any possibility to vectorize this operation as well (using indexed expressions)?
Using just indexing
ind = reshape(1:size(A,1)*n, [], n) + reshape((0:size(A,2)-n)*size(A,1), 1, 1, []);
result = A(ind);
The index ind is built using linear indexing and implicit expansion.
Using the Image Package / Image Processing Toolbox
result = reshape(im2col(A, [size(A,1) n], 'sliding'), size(A,1), n, []);
Most of the work here is done by the im2col function with the 'sliding' option.
The Issue
With solve minimize I only get one solution, even though there are multiple optimal solutions. I have enabled printout of multiple solutions in the solver configurations. The other optimal solutions are found with solve satisfy, along with non-optimal solutions.
Possible causes
Could it be that the cardinality function card() ranks by enum value where size of two sets are equal? In other words that card(A, B) > card(B, C)? If so, do I have to switch the representation of my vertices?
The Program
I am creating a MiniZinc program for finding the minimum vertex cover of a given graph. The graph in this example is this:
With Minimal Vertex Cover solutions being:
[{A, B, C, E}, {A, B, E, F}, {A, C, D, E}, {B, C, D, E}, {B, C, D, F}, {B, D, E, F}]. My code only outputs {A, B, C, E}.
Data file:
VERTEX = {A, B, C, D, E, F};
edges = [|1, 0, 1, 0, 0, 0, 0, 0, 0
|1, 1, 0, 1, 1, 0, 0, 0, 0
|0, 1, 0, 0, 0, 1, 1, 0, 0
|0, 0, 1, 1, 0, 0, 0, 1, 0
|0, 0, 0, 0, 1, 1, 0, 1, 1
|0, 0, 0, 0, 0, 0, 1, 0, 1|];
Solver program:
% Vertices in graph
enum VERTEX;
% Edges between vertices
array[VERTEX, int] of int: edges;
int: num_edges = (length(edges) div card(VERTEX));
% Set of vertices to find
var set of VERTEX: span;
% Number of vertices connected to edge resulting from span
array[1..num_edges] of var 0..num_edges: conn;
% All edges must be connected with at least one vertex from span
constraint forall(i in 1..num_edges)
(conn[i] >= 1);
% The number of connections to each edge is the number of vertices
% in span with a connection to that edge
constraint forall(i in 1..num_edges)
(conn[i] = sum([edges[vert,i]| vert in span]));
% Minimize the number of vertices in span
solve minimize card(span);
solve minimize only show one optimal solution (in some cases, intermediate values might also be shown).
If you want all optimal solutions you must use solve satisfy and add the constraint with the optimal value:
constraint card(span) = 4;
Then the model outputs all the 6 optimal solutions:
card(cpan): 4
span: {A, B, C, E}
conn: [2, 2, 1, 1, 2, 2, 1, 1, 1]
----------
card(cpan): 4
span: {B, C, D, F}
conn: [1, 2, 1, 2, 1, 1, 2, 1, 1]
----------
card(cpan): 4
span: {A, C, D, E}
conn: [1, 1, 2, 1, 1, 2, 1, 2, 1]
----------
card(cpan): 4
span: {B, C, D, E}
conn: [1, 2, 1, 2, 2, 2, 1, 2, 1]
----------
card(cpan): 4
span: {A, B, E, F}
conn: [2, 1, 1, 1, 2, 1, 1, 1, 2]
----------
card(cpan): 4
span: {B, D, E, F}
conn: [1, 1, 1, 2, 2, 1, 1, 2, 2]
----------
==========
Note: I added the output section to show all the values:
output [
"card(cpan): \(card(span))\n",
"span: \(span)\n",
"conn: \(conn)"
];
An alternative solution is to use OptiMathSAT (v. 1.6.3).
When asking for all solutions in optimization mode, the solver returns all solutions (with respect to the output variables) with the same optimal value.
Example:
~$ mzn2fzn test.mzn test.dzn # your instance
~$ optimathsat -input=fzn -opt.fzn.all_solutions=True < test.fzn
% allsat model
span = {2, 4, 5, 6};
conn = array1d(1..9, [1, 1, 1, 2, 2, 1, 1, 2, 2]);
----------
% allsat model
span = {1, 3, 4, 5};
conn = array1d(1..9, [1, 1, 2, 1, 1, 2, 1, 2, 1]);
----------
% allsat model
span = {1, 2, 3, 5};
conn = array1d(1..9, [2, 2, 1, 1, 2, 2, 1, 1, 1]);
----------
% allsat model
span = {1, 2, 5, 6};
conn = array1d(1..9, [2, 1, 1, 1, 2, 1, 1, 1, 2]);
----------
% allsat model
span = {2, 3, 4, 5};
conn = array1d(1..9, [1, 2, 1, 2, 2, 2, 1, 2, 1]);
----------
% allsat model
span = {2, 3, 4, 6};
conn = array1d(1..9, [1, 2, 1, 2, 1, 1, 2, 1, 1]);
----------
=========
The main advantage wrt. the approach presented in the accepted answer is that OptiMathSAT is incremental, meaning that the tool searches for other solutions without being restarted, so that it can re-use any useful information that has been previously generated to speed-up the search (e.g. theory lemmas). [CAVEAT: this may not be relevant for small instances; also, other MiniZinc solvers may still be faster depending on the input problem]
Note: please notice that OptiMathSAT does not print the labels of each VERTEX, because the mzn2fzn compiler removes these labels when compiling the file. However, the mapping among numbers and labels should be obvious.
Disclosure: I am one of the developers of this tool.
Let's say I have a vector that looks as so (the numbers will always be > 0)...
[1, 2, 1, 4, 1, 2, 4, 3]
I need a vectorized implementation that sums the numbers together and uses the original number as the index to store the number. So if I run it I would get...
% step 1
[1+1+1, 2+2, 3, 4+4]
% step 2
[3, 4, 3, 8]
I have already implemented this using for loops, but I feel like there is a vectorized way to achieve this. I am still quite new at vectorizing functions so any help is appreciated.
This sounds like a job for accumarray:
v = [1, 2, 1, 4, 1, 2, 4, 3];
result = accumarray(v(:), v(:)).'
result =
3 4 3 8
Other approaches:
Using histcounts:
x = [1, 2, 1, 4, 1, 2, 4, 3];
u = unique(x);
result = u.*histcounts(x, [u inf]);
Using bsxfun (may be more memory-intensive):
x = [1, 2, 1, 4, 1, 2, 4, 3];
u = unique(x);
result = u .* sum(bsxfun(#eq, x(:), u(:).' ), 1);
I have oldMat which is a ranking of equity tickers. The column number represents the respective rank, e.g. first column equals highest rank, second column represents second highest rank and so on. The integers within oldMatrepresent the number of the individual equity ticker. The number 3 in oldMat(3,2,1)means, that the third equity ticker is ranked second in the third period (rows represent different periods).
Now, I need to transform oldMat in the following way: The column numbers now represent the individual equity tickers. The integers now represent the rank that individual equity tickers hold at specific periods. For example, the number 2 in newMat(3,3,1) means, that the third equity ticker is ranked second in the third period.
I used a for-loop in order to solve that problem, but I am pretty sure there exists a more efficient way to achieve this result. Here's my code:
% Define oldMat
oldMat(:,:,1) = ...
[NaN, NaN, NaN, NaN, NaN, NaN; ...
1, 3, 4, 6, 2, 5; ...
6, 3, 4, 1, 2, 5; ...
2, 3, 6, 1, 4, 5; ...
5, 4, 6, 2, 3, 1; ...
5, 1, 2, 3, 6, 4; ...
4, 5, 1, 3, 6, 2; ...
4, 1, 6, 5, 2, 3];
oldMat(:,:,2) = ...
[NaN, NaN, NaN, NaN, NaN, NaN; ...
NaN, NaN, NaN, NaN, NaN, NaN; ...
1, 6, 3, 4, 2, 5; ...
6, 3, 2, 1, 4, 5; ...
2, 6, 3, 4, 1, 5; ...
5, 2, 1, 6, 3, 4; ...
5, 1, 3, 6, 2, 4; ...
4, 1, 5, 6, 3, 2];
% Pre-allocate newMat
newMat = nan(size(oldMat));
% Transform oldMat to newMat
for runNum = 1 : size(newMat,3)
for colNum = 1 : size(newMat,2)
for rowNum = 1 : size(newMat,1)
if ~isnan(oldMat(rowNum, colNum, runNum))
newMat(rowNum,oldMat(rowNum, colNum, runNum), runNum) = colNum;
end
end
end
end
Looks like a classic case of sub2ind. You want to create a set of linear indices to access the second dimension of the new matrix and set those equal to the column number. First create a grid of 3D coordinates with meshgrid, then use the oldMat matrix as an index into the second column of the output and set this equal to the column number. Make sure that you don't copy over any NaN values or sub2ind will complain. You can use isnan to help filter these values out for you:
% Initialize new matrix
newMat = nan(size(oldMat));
% Generate a grid of coordinates
[X,Y,Z] = meshgrid(1:size(newMat,2), 1:size(newMat,1), 1:size(newMat,3));
% Find elements that are NaN and remove
mask = isnan(oldMat);
X(mask) = []; Y(mask) = []; Z(mask) = [];
% Set the values now
newMat(sub2ind(size(oldMat), Y, oldMat(~isnan(oldMat)).', Z)) = X;
Not sure even if this question stands valid. But better ask.
Suppose we have two matrices in MATLAB of size (n,1) and (m,1) and we want to copy certain rows from matrix A to matrix B on a condition.
e.g. if value A(i,1) is less or equal to X
And later delete those rows from source matrix i.e. matrix A
Example:
A = [1, 2, 3, 4, 5, 6]
B = [8, 9]
copy all values which are less than or equal to 4 from A to B, and delete from A
Matrices becomes
A = [5, 6]
B = [8, 9, 1, 2, 3, 4]
You can use a logical matrix to identify the items:
mask = (A <= 4);
B = [B A(mask)];
A(mask) = [];