/***
!Test
check_against: []
expected:
- !Result
  solution: !Solution
    last_step: 5
extra_files: []
markers: []
options:
  all_solutions: false
solvers:
- gecode
- cbc
- chuffed
type: solve

***/

% A factory has three kinds of machines:
% - input machines, where products enter the factory;
% - output machines, where finished products leave the factory;
% - work machines, which can hold at most one product and which may
% add some attributes to that product.
%
% A product can move from one machine to another iff there is a direct
% connection between the two machines (we model staying at a particular
% machine by making the connection graph reflexive).
%
% In this model all actions are assumed to take the same amount of time.
%
% The goal is to complete a set of products in the shortest time.
% XXX Optimisation is currently very expensive; we settle instead
% for merely finding a consistent solution.

% These values must be supplied by a data file.
%
int: n_machines;                        % The number of machines.
int: n_products;                        % The number of products.
int: n_attributes;                      % The number of attributes.
int: n_steps;                           % The number of plan steps.

set of int: machines = 1..n_machines;
set of int: products = 1..n_products;
set of int: attributes = 1..n_attributes;
set of int: steps = 1..n_steps;

% These values must be supplied by a data file:
% - input_machines  - the set of machines where products can start;
% - output_machines - the set of machines where products may finish;
% - connected       - the (reflexive) graph of inter-machine connections;
% - can_add_attr    - table of which machines can add which attributes;
% - prod_attr_goal  - table of goal attributes for each product;
% - prod_start_mach - table of starting machines for each product.
%
set of machines:                                        input_machines;
set of machines:                                        output_machines;
array [machines, machines]   of 0..1:                   connected;
array [machines, attributes] of 0..1:                   can_add_attr;
array [products, attributes] of 0..1:                   prod_attr_goal;
array [products]             of input_machines:         prod_start_mach;

% Decision variables:
% - step_prod_attr[s, p, a]     - at step s, product p has attribute a;
% - step_prod_mach[s, p] = m    - at step s, product p is at machine m;
% - last_step                   - the step at which the goal is achieved.
%
array [steps, products, attributes] of var 0..1:        step_prod_attr;
array [steps, products]             of var machines:    step_prod_mach;
var steps:                                              last_step;

% Work machines are those that are neither input machines or output machines.
%
set of machines: work_machines =
    machines diff (input_machines union output_machines);

% Products start at particular machines.
%
constraint
    forall (p in products) (
        step_prod_mach[1, p] = prod_start_mach[p]
    );

% Products start with no attributes.
%
constraint
    forall (p in products, a in attributes) (
        step_prod_attr[1, p, a] = 0
    );

% The attributes of a product at step s are a subset of its
% attributes at step s+1.
%
constraint
    forall (s in steps, p in products, a in attributes where s < n_steps) (
        step_prod_attr[s, p, a] <= step_prod_attr[s + 1, p, a]
    );

% No two products can occupy the same work machine at the same step.
%
constraint
    forall (s in steps, p, q in products, m in work_machines where p < q) (
        step_prod_mach[s, p] = m  ->  step_prod_mach[s, q] != m
    );

% At t = last_step all goals should be achieved:
% - each product should have (precisely) its goal set of attributes;
% - each product should be in an output machine.
%
constraint
    forall (p in products, a in attributes) (
        step_prod_attr[last_step, p, a] = prod_attr_goal[p, a]
    );

constraint
    forall (p in products) (
        step_prod_mach[last_step, p] in output_machines
    );

% Products can only move between directly connected machines.
%
constraint
    forall (s in steps, p in products where s < n_steps) (
        connected[step_prod_mach[s, p], step_prod_mach[s + 1, p]] = 1
    );

% A product can have an attribute added while at a particular machine.
%
constraint
    forall (s in steps, p in products, a in attributes where s < n_steps) (
        step_prod_attr[s + 1, p, a] <=
                step_prod_attr[s, p, a]
              + can_add_attr[step_prod_mach[s, p], a]
    );

% XXX Trying to optimize for last_step currently takes forever, even for
% relatively simple problems.
%
% solve minimize last_step;
%
last_step = n_steps;
% solve satisfy;

% Instance data

n_machines = 5;
n_products = 2;
n_attributes = 3;
n_steps = 5;

input_machines = {1};

output_machines = {5};

% Connections:
%
%  M1 --- M2 ----.
%  |      |      |
%  |----- M3 --- M5
%  |      |      |
%  '----- M4 ----'

connected =
[|  1, 1, 1, 1, 0,
 |  1, 1, 1, 0, 1,
 |  1, 1, 1, 1, 1,
 |  1, 0, 1, 1, 1,
 |  0, 1, 1, 1, 1
|];

can_add_attr =
[|  0, 0, 0
 |  1, 0, 0
 |  0, 1, 0
 |  0, 0, 1
 |  0, 0, 0
|];

prod_start_mach = [
    1,
    1
];

prod_attr_goal =
[|  1, 1, 0
 |  1, 0, 1
|];

solve satisfy;

output [
    "factory planning instance\n",
    "step | product | location | a1 a2 a3\n",
    "-----+---------+----------+---------\n",
    " 1   | 1       | ", show(step_prod_mach[1, 1]), "        |  ",
        show(step_prod_attr[1, 1, 1]), "  ",
        show(step_prod_attr[1, 1, 2]), "  ",
        show(step_prod_attr[1, 1, 3]), "\n",
    "     | 2       | ", show(step_prod_mach[1, 2]), "        |  ",
        show(step_prod_attr[1, 2, 1]), "  ",
        show(step_prod_attr[1, 2, 2]), "  ",
        show(step_prod_attr[1, 2, 3]), "\n",
    "-----+---------+----------+---------\n",
    " 2   | 1       | ", show(step_prod_mach[2, 1]), "        |  ",
        show(step_prod_attr[2, 1, 1]), "  ",
        show(step_prod_attr[2, 1, 2]), "  ",
        show(step_prod_attr[2, 1, 3]), "\n",
    "     | 2       | ", show(step_prod_mach[2, 2]), "        |  ",
        show(step_prod_attr[2, 2, 1]), "  ",
        show(step_prod_attr[2, 2, 2]), "  ",
        show(step_prod_attr[2, 2, 3]), "\n",
    "-----+---------+----------+---------\n",
    " 3   | 1       | ", show(step_prod_mach[3, 1]), "        |  ",
        show(step_prod_attr[3, 1, 1]), "  ",
        show(step_prod_attr[3, 1, 2]), "  ",
        show(step_prod_attr[3, 1, 3]), "\n",
    "     | 2       | ", show(step_prod_mach[3, 2]), "        |  ",
        show(step_prod_attr[3, 2, 1]), "  ",
        show(step_prod_attr[3, 2, 2]), "  ",
        show(step_prod_attr[3, 2, 3]), "\n",
    "-----+---------+----------+---------\n",
    " 4   | 1       | ", show(step_prod_mach[4, 1]), "        |  ",
        show(step_prod_attr[4, 1, 1]), "  ",
        show(step_prod_attr[4, 1, 2]), "  ",
        show(step_prod_attr[4, 1, 3]), "\n",
    "     | 2       | ", show(step_prod_mach[4, 2]), "        |  ",
        show(step_prod_attr[4, 2, 1]), "  ",
        show(step_prod_attr[4, 2, 2]), "  ",
        show(step_prod_attr[4, 2, 3]), "\n",
    "-----+---------+----------+---------\n",
    " 5   | 1       | ", show(step_prod_mach[5, 1]), "        |  ",
        show(step_prod_attr[5, 1, 1]), "  ",
        show(step_prod_attr[5, 1, 2]), "  ",
        show(step_prod_attr[5, 1, 3]), "\n",
    "     | 2       | ", show(step_prod_mach[5, 2]), "        |  ",
        show(step_prod_attr[5, 2, 1]), "  ",
        show(step_prod_attr[5, 2, 2]), "  ",
        show(step_prod_attr[5, 2, 3]), "\n",
    "-----+---------+----------+---------\n"
];
