Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added source/_static/cumulative-decomposition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_static/cumulative_binary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions source/_static/discrepancy-search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions source/_static/vrp-search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion source/learning_minicp/exercises.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Of course you should get a strong inspiration from the
`TableCT.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/TableCT.java>`_
implementation you did in a previous exercise.

Check that your implementation passes the tests `NegTableTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/NegTableTest.java>`_
Verify that your implementation passes the tests of `NegTableTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/NegTableTest.java>`_



Expand Down
31 changes: 22 additions & 9 deletions source/learning_minicp/part_1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,36 @@ Less-or-equal Reified Constraint

Implement `IsLessOrEqual.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/IsLessOrEqual.java>`_.

This is a propagator for the constraint `b iff x <= c`, which is called the `reified constraint` (or: `reification`) of the constraint `x <= c`: it holds if Boolean variable `b` is true if and only if variable `x` is less than or equal to value `c`.
This is a propagator for the constraint :math:`b \Leftrightarrow x \leq c`, which is called the `reified constraint` (or: `reification`) of the constraint :math:`x \leq c`: it holds if the value of Boolean variable :math:`b` is true if and only if the value of variable :math:`x` is less than or equal to the value :math:`c`.

For example, the constraint holds for

.. code-block:: java
.. math::

b = true , x = 4, c = 5
b = false, x = 4, c = 2
\text{Dom}(b) = \{\mathit{true}\} , \text{Dom}(x) = \{4\}, c = 5

and

.. math::

\text{Dom}(b) = \{\mathit{false}\}, \text{Dom}(x) = \{4\}, c = 2


but is violated for

.. code-block:: java
.. math::

\text{Dom}(b) = \{\mathit{true}\} , \text{Dom}(x) = \{5\}, c = 4

and violated for

.. math::

\text{Dom}(b) = \{\mathit{false}\}, \text{Dom}(x) = \{2\}, c = 4


b = true , x = 5, c = 4
b = false, x = 2, c = 4
where the function :math:`\text{Dom}` returns the domain of the given variable.

For an example of reification, you can look at `IsEqual.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/IsEqual.java>`_.
**Hint**: use `IsEqual.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/IsEqual.java>`_ as a reference when implementing the constraint.

Check that your implementation passes the tests `IsLessOrEqualTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/IsEqualTest.java>`_.
Verify that your implementation passes the tests of `IsLessOrEqualTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/IsEqualTest.java>`_.
40 changes: 38 additions & 2 deletions source/learning_minicp/part_10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,22 @@ Your task is to make the Disjunctive constraint more efficient than by using the
This will be useful for implementing the decomposition of the Disjunctive constraint.
* Test your implementation in `IsLessOrEqualVarTest.java. <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/IsLessOrEqualVarTest.java>`_.
* Implement the decomposition with reified constraints for `Disjunctive.java. <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Disjunctive.java>`_.
* Make sure your implementation passes all the tests *except* `testOverloadChecker`, `testDetectablePrecedence`, and `testNotLast` (those are for the programming exercise below) in `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.
For each pair of tasks, :math:`i` and :math:`j`, with :math:`i \neq j`, the binary decomposition constrains the two tasks not to overlap in time:

* either task :math:`i` ends before task :math:`j` starts (:math:`b_{ij} \Leftrightarrow e_i \leq s_j`) or
* task :math:`j` ends before task :math:`i` starts (:math:`b_{ji} \Leftrightarrow e_j \leq s_i`),

with :math:`b_{ij} \neq b_{ji}`.

.. image:: ../_static/cumulative-decomposition.png
:width: 574
:alt: cumulative decomposition
:align: center


Task `j` cannot end before task `i` starts since :math:`e_j \leq s_i` does not hold (:math:`b_{ji} = \mathit{false}`).

* Verify that your implementation passes the tests `testAllDiffDisjunctive`, `testNotRemovingSolutions`, and `testBinaryDecomposition` of `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.
* Test whether, as expected, this decomposition prunes more than the formulation with timetable filtering for the Cumulative constraint.
For example, observe on `JobShop.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/examples/JobShop.java>`_ that the number of backtracks is reduced with the decomposition compared to the formulation with Cumulative.
Test for instance on the small instance `data/jobshop/sascha/jobshop-4-4-2` with 4 jobs, 4 machines, and 16 activities.
Expand All @@ -46,9 +61,30 @@ The Global Disjunctive Constraint: Overload Checker, Detectable Precedence, and
To make sure you understand it, add a unit test with 4 activities and compare the results with a manual computation.
* Overload checking, detectable precedences, not-first-not-last, and edge finding only filter one side of the activities.
To get the symmetrical filtering, implement the mirroring activities trick similarly to `Cumulative.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Cumulative.java>`_.
* **Hint**: overload checking, detectable precedences, and not-last all require an array `permEst` that is a permutation of the indices of the activities sorted increasingly by their earliest starting times
(`permEst[0]` holds the activity with the smallest earliest starting time, while `permEst[n - 1]` holds the activity with the greatest earliest starting time), as well as an
array `rankEst` that is the inverse of `permEst` (if `permEst[j] = i`, then `rankEst[i] = j`).

Create a helper method that sorts the array `permEst` and updates the array `rankEst` (both arrays are preferably instance variables of the `Disjunctive` class).
* Implement the overload checker in `Disjunctive.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Disjunctive.java>`_.

Verify that your implementation passes the test `testOverloadChecker` of `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.
* The overload checker should already make a big difference to prune the search tree. Make sure that larger job-shop instances are now solved faster; for instance, `data/jobshop/sascha/jobshop-6-6-0` should now become easy to solve.
* Implement detectable precedences in `Disjunctive.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Disjunctive.java>`_.

**Note:** given activity `i`, when updating the temporary earliest start time integer value:

.. math::

est^{\prime}_{i} \leftarrow \max (est_{i}, ect_{\theta \setminus \{i\}})

if the activity `i` has already been added to the theta tree, then it must first be removed before updating :math:`est^{\prime}_{i}` and reinserted again after updating :math:`est^{\prime}_{i}` (as indicated by :math:`\theta \setminus \{i\}`).

Verify that your implementation passes the test `testDetectablePrecedence` of `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.

* Implement not-first-not-last in `Disjunctive.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Disjunctive.java>`_.
* Make sure your implementation passes the tests `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.

Make sure your implementation passes the test `testNotLast` of `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.

* Make sure your implementation passes all tests in `DisjunctiveTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/DisjunctiveTest.java>`_.
* (optional) Implement edge finding in `Disjunctive.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/constraints/Disjunctive.java>`_ (you will also need to implement the ThetaLambdaTree data structure).
38 changes: 19 additions & 19 deletions source/learning_minicp/part_2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ Implement the missing constructor in `IntVarImpl.java <https://github.com/minicp
throw new NotImplementedException();
}

This exercise is straightforward: just create a dense domain and then remove the values not present in the set.
Create a dense domain and then remove all values not present in the set `values`.

Check that your implementation passes the tests `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_.
Verify that your implementation passes the tests of `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_.

Implement a Domain Iterator
======================================

Many filtering algorithms require iteration over the values of a domain.

A naive (but correct) way of iterating over a domain is:
A naive (and correct) way of iterating over the values of a domain is:


.. code-block:: java
Expand All @@ -50,25 +50,23 @@ A naive (but correct) way of iterating over a domain is:
}
}

This method is rather inefficient because it will also consider the values that are not present in the domain.
This method is rather inefficient as it will (possibly) iterate over values that are not present in the domain.
Instead, the `fillArray` method from `StateSparseSet.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/state/StateSparseSet.java>`_
allows filling an array with all the values present in the sparse set.
In case of an offset value of 0, you could even use the very efficient `System.arraycopy`.

The main advantage over the iterator mechanism is that no object is created (and thus garbage collected).
Indeed `dest` is typically a container array stored as an instance variable and reused many times.
It is important for efficiency to avoid creating objects on the heap at each execution of a propagator.
Never forget that a `propagate()` method of `Constraint` may be called thousands of times per second.
This implementation using `fillArray` avoids the `ConcurrentModificationException` discussion
when implementing an Iterator: should we allow modifying a domain while iterating on it?
The answer here is very clear: you get a snapshot of the domain at the time of the call to `fillArray` and you can thus
safely iterate over this `dest` array and modify the domain at the same time.
allows filling an array only with the values present in the sparse set.
Additionally, if the offset is 0, then the very efficient method `System.arraycopy` can be used.

If the array that stores the returned array of the method `fillArray` is an instance variable, then it will not be garbage collected by the java garbage collector, resulting in a very efficient implementation.
It is important for efficiency to avoid creating objects on the heap (such as arrays) at each execution of propagators that need to be garbage collected,
as the `propagate()` method of a constraint can be called thousands of times every second.

Since the implementation using `fillArray` iterates on a copy of the array containing the domain, any modification on the actual domain will not carry over to the copy, and vice versa, removing any
`ConcurrentModificationException` exceptions that might otherwise have been thrown.

To do:

* Improve the efficiency of `fillArray` from `StateSparseSet.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/state/StateSparseSet.java>`_ in order to use `System.arraycopy` when possible.
* Improve the efficiency of `fillArray` from `StateSparseSet.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/state/StateSparseSet.java>`_, using `System.arraycopy` where possible.
* Implement `public int fillArray(int [] dest)` in `IntVarImpl.java <https://github.com/minicp/minicp/blob/master/src/main/java/minicp/engine/core/IntVarImpl.java>`_.
* Check that your implementation passes the tests `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_ and `StateSparseSetTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/state/StateSparseSetTest.java>`_. Additionally, add more tests to `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_.
* Verify that your implementation passes the tests of `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_ and `StateSparseSetTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/state/StateSparseSetTest.java>`_. Additionally, add more tests to `IntVarTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/core/IntVarTest.java>`_.

The Absolute Value Constraint
==============================
Expand All @@ -80,7 +78,7 @@ Several directions of implementation are possible:
1. The full domain-consistent version (use the `fillArray` method to iterate over domains).
2. A hybrid domain-bound consistent one.

Check that your implementation passes the tests `AbsoluteTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/AbsoluteTest.java>`_.
Verify that your implementation passes the tests of `AbsoluteTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/AbsoluteTest.java>`_.


The Maximum Constraint
Expand All @@ -90,4 +88,6 @@ Implement `Maximum.java <https://github.com/minicp/minicp/blob/master/src/main/j

Implement a bound-consistent filtering algorithm.

Check that your implementation passes the tests `MaximumTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/MaximumTest.java>`_.
**Hint**: Given :math:`y = \max (X)`, where :math:`X` is an array of variables, what are the lower and upper bounds of :math:`y`?

Verify that your implementation passes the tests of `MaximumTest.java <https://github.com/minicp/minicp/blob/master/src/test/java/minicp/engine/constraints/MaximumTest.java>`_.
Loading