Skip to content
Commits on Source (7)
......@@ -6,3 +6,4 @@ planimod-log.*
.classpath
.settings
.project
*.h2.db
......@@ -4,7 +4,7 @@
<parent>
<artifactId>com.patrikdufresne.planimod-parent</artifactId>
<groupId>com.patrikdufresne.planimod</groupId>
<version>0.2.26-SNAPSHOT</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>com.patrikdufresne.planimod-installation-package</artifactId>
<packaging>pom</packaging>
......
......@@ -48,6 +48,11 @@
<level value="info" />
</logger>
<!-- Uncomment to generate dump files. -->
<!-- <logger name="com.planimod.core.planif.SolveRunnable">
<level value="trace" />
</logger> -->
<logger name="com.patrikdufresne">
<level value="info" />
</logger>
......
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.patrikdufresne.planimod</groupId>
<artifactId>com.patrikdufresne.planimod-parent</artifactId>
<version>0.2.26-SNAPSHOT</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>com.patrikdufresne.planimod</artifactId>
<name>Planimod :: Application</name>
......@@ -23,6 +23,12 @@
<version>4.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.patrikdufresne.cbc4j</groupId>
<artifactId>cbc4j-linux-x86_64</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
<profile>
......@@ -40,6 +46,12 @@
<version>4.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.patrikdufresne.cbc4j</groupId>
<artifactId>cbc4j-win-x86_64</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
</profiles>
......@@ -78,12 +90,12 @@
<dependency>
<groupId>com.patrikdufresne.managers</groupId>
<artifactId>com.patrikdufresne.managers.jface</artifactId>
<version>1.8</version>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.patrikdufresne.managers</groupId>
<artifactId>com.patrikdufresne.managers.databinding</artifactId>
<version>1.8</version>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.patrikdufresne.printing</groupId>
......@@ -98,14 +110,18 @@
<dependency>
<groupId>com.patrikdufresne.ilp</groupId>
<artifactId>com.patrikdufresne.ilp</artifactId>
<version>0.12</version>
<version>0.14</version>
</dependency>
<dependency>
<groupId>com.patrikdufresne.ilp</groupId>
<artifactId>com.patrikdufresne.ilp.glpk</artifactId>
<version>0.12</version>
<artifactId>com.patrikdufresne.ilp.cbc</artifactId>
<version>0.14</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
<build>
<!-- Include the .properties file located in /src/main/java -->
......
......@@ -15,7 +15,7 @@ package com.planimod.core.planif;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.util.Bound;
import com.patrikdufresne.ilp.util.BoundSnapshot;
/**
* This runnable is used to check if their is a feasible solution with this employee not working.
......@@ -35,12 +35,12 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
/**
* Bounds of the xi variable before running this class.
*/
private Bound originalBound;
private BoundSnapshot originalBound;
/**
* Bounds of the xi variable when release was called.
*/
private Bound snapshotBound;
private BoundSnapshot snapshotBound;
/**
* Create a new runnable
......@@ -53,7 +53,6 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
public CheckEmployeeNotWorking(ProblemBuilder problem, int i) {
super(problem);
this.i = i;
setFeasibilityPumpHeuristic(true);
}
/**
......@@ -62,14 +61,14 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
@Override
public void engage() {
// Nothing to engage if xi is null.
if (pb.xi[i] == null) {
if (pb.xi().iEq(i).isEmpty()) {
return;
}
if (this.snapshotBound == null) {
throw new RuntimeException("snapshot is not available"); //$NON-NLS-1$
}
// Restore the bounds. Probably [0..0] or [1..1]
this.snapshotBound.restore(pb.xi[i]);
this.snapshotBound.restore();
this.snapshotBound = null;
}
......@@ -90,7 +89,7 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
@Override
public void release() {
// Nothing to release if xi is null.
if (pb.xi[i] == null) {
if (pb.xi().iEq(i).isEmpty()) {
return;
}
// Restore the variable bounds.
......@@ -98,27 +97,26 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
throw new RuntimeException("original bound not available"); //$NON-NLS-1$
}
// Capture the bound
this.snapshotBound = Bound.create(pb.xi[i]);
this.snapshotBound = pb.xi().iEq(i).createBoundSnapshot();
// Restore the original bound (should be [0..1]
this.originalBound.restore(pb.xi[i]);
this.originalBound.restore();
}
@Override
protected boolean setUp() {
// Skip this runnable if the variable is not set
if (pb.xi[i] == null) {
if (pb.xi().iEq(i).isEmpty()) {
feasible = true;
return false;
}
// Capture the original bounds
if (this.originalBound == null) {
this.originalBound = new Bound(pb.xi[i].getLowerBound(), pb.xi[i].getUpperBound());
this.originalBound = pb.xi().iEq(i).createBoundSnapshot();
}
// Fix the variable to ZERO.
pb.xi[i].setLowerBound(ZERO);
pb.xi[i].setUpperBound(ZERO);
pb.xi().iEq(i).setBounds(ZERO, ZERO);
// Check if the previous snapshot is not assigning the employee.
if (pb.snapshot != null && ZERO.equals(pb.snapshot.get(pb.xi[i]))) {
if (pb.lp.isFeasible() && pb.xi().iEq(i).valueEq(ZERO)) {
LOGGER.debug("Employee {} " //$NON-NLS-1$
+ " is already not working according to snapshot.", //$NON-NLS-1$
pb.employeeToString(i));
......@@ -139,7 +137,7 @@ public class CheckEmployeeNotWorking extends SolveRunnable {
throw new RuntimeException("original bound not available."); //$NON-NLS-1$
}
// Restore the variable bounds
this.originalBound.restore(pb.xi[i]);
this.originalBound.restore();
LOGGER.info("Employee {} is required.", pb.employeeToString(i)); //$NON-NLS-1$
}
}
\ No newline at end of file
......@@ -15,11 +15,12 @@
*/
package com.planimod.core.planif;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.util.BoundSnapshot;
import com.patrikdufresne.ilp.util.Variables;
import com.planimod.core.Team;
/**
* This runnable is used to check feasibility of assigning employee to preferred team.
......@@ -33,17 +34,21 @@ public class CheckPreferredTeam extends SolveRunnable {
/**
* Bound snapshot used to restore the bound is the problem is unfeasible.
*/
private BoundSnapshot boundSnapshot;
private BoundSnapshot boundSnapshotXijk;
/**
* Bound snapshot used to restore the bound is the problem is unfeasible.
*/
private BoundSnapshot boundSnapshotXij;
/**
* The employee.
* The employee index.
*/
private int i;
private final int i;
/**
* The team.
*/
private int j;
private final Team team;
/**
* Create a new runnable to assign employee <code>i</code> to preferred team <code>j</code>
......@@ -52,23 +57,20 @@ public class CheckPreferredTeam extends SolveRunnable {
* reference to ProblemBuilder
* @param i
* the employee
* @param j
* the team
* @param rtj
* True to assign the employee to tasks really in team.
* @param team
* the preferred team
*/
public CheckPreferredTeam(ProblemBuilder problem, int i, int j) {
public CheckPreferredTeam(ProblemBuilder problem, int i, Team team) {
super(problem);
this.i = i;
this.j = j;
setFeasibilityPumpHeuristic(true);
Validate.notNull(this.team = team);
}
@Override
protected void feasible() {
LOGGER.info("Employee {} assign to team {}.", //$NON-NLS-1$
pb.employeeToString(i),
pb.teamToString(j));
teamToString(team));
}
/**
......@@ -80,29 +82,21 @@ public class CheckPreferredTeam extends SolveRunnable {
@Override
protected boolean setUp() {
// Check if employee can be assigned to team j
if (pb.xij[i] == null || pb.xij[i][j] == null) {
if (pb.xij().iEq(i).jEq(team).isEmpty()) {
this.feasible = false;
return false;
}
// Take bounds snapshot before changing them
this.boundSnapshot = BoundSnapshot.create(pb.variablesForEmployee(i));
this.boundSnapshotXijk = pb.xijk().iEq(i).createBoundSnapshot();
this.boundSnapshotXij = pb.xij().iEq(i).createBoundSnapshot();
// Fix to zero (0) xik, xij and xi variables not corresponding to
// the team for employee i. Be careful not to change fixed
// variables.
for (int j2 = 0; j2 < pb.xij[this.i].length; j2++) {
if (j2 != this.j) {
for (int k = 0; k < pb.xijk[this.i][j2].length; k++) {
if (pb.xijk[this.i][j2][k] != null && !Variables.isFixed(pb.xijk[this.i][j2][k])) {
pb.xijk[this.i][j2][k].setLowerBound(ZERO);
pb.xijk[this.i][j2][k].setUpperBound(ZERO);
}
}
}
if (pb.xij[this.i][j2] != null && !Variables.isFixed(pb.xij[i][j2])) {
pb.xij[this.i][j2].setLowerBound(this.j == j2 ? ONE : ZERO);
pb.xij[this.i][j2].setUpperBound(this.j == j2 ? ONE : ZERO);
}
}
// TODO Check if it really make a different to "fixed" xijk variables.
pb.xijk().iEq(i).jNotEq(team).notFixed().setBounds(ZERO, ZERO);
pb.xij().iEq(i).jNotEq(team).notFixed().setBounds(ZERO, ZERO);
pb.xij().iEq(i).jEq(team).notFixed().setBounds(ONE, ONE);
// Don't create any constraint to force the employee to work a
// minimum number of time on the team. OItherwise it will break the
// non-availability handler. See ticket #191.
......@@ -110,10 +104,29 @@ public class CheckPreferredTeam extends SolveRunnable {
pb.lp.setObjectiveLinear(null);
LOGGER.debug("Check feasiblility with employee {} " //$NON-NLS-1$
+ "working on team {}.", pb.employeeToString(i), //$NON-NLS-1$
pb.teamToString(j));
teamToString(team));
return true;
}
/**
* Return a string representation of the team j
*
* @param j
* the team
* @return the string
*/
private String teamToString(Team team) {
StringBuilder buf = new StringBuilder();
buf.append(pb.getTeams().indexOf(team));
String name = team.getName();
if (name != null) {
buf.append(" ("); //$NON-NLS-1$
buf.append(team.getName());
buf.append(")"); //$NON-NLS-1$
}
return buf.toString();
}
@Override
protected void tearDown() throws GeneratePlanifException {
// Nothing to do.
......@@ -121,13 +134,15 @@ public class CheckPreferredTeam extends SolveRunnable {
@Override
protected void unfeasible() {
if (this.boundSnapshot != null) {
if (this.boundSnapshotXijk != null) {
// Restore the bound snapshot.
this.boundSnapshot.restore();
this.boundSnapshot = null;
this.boundSnapshotXijk.restore();
this.boundSnapshotXijk = null;
this.boundSnapshotXij.restore();
this.boundSnapshotXij = null;
LOGGER.info("Can't assign " //$NON-NLS-1$
+ "employee {} to team {}", pb.employeeToString(i), //$NON-NLS-1$
pb.teamToString(j));
teamToString(team));
}
}
}
\ No newline at end of file
/**
* Copyright(C) 2013 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.planimod.core.planif;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Linear;
import com.patrikdufresne.ilp.util.Constraints;
/**
* This runnable check if the problem is feasible without any swappable tasks.
*
* <pre>
* SUM from{i=0} to{n} SUM from{j=0} to{m} SUM from{k notin RTj} xijk ==0
* </pre>
*
* @author Patrik Dufresne
*/
public class CheckSwappableTask extends AbstractConstraintRunnable {
private static final transient Logger LOGGER = LoggerFactory.getLogger(CheckSwappableTask.class);
/**
* The constraint's name
*/
private static final String CONSTRAINT_NAME = "SUM from{i=0} to{n} SUM from{j=0} to{m} SUM from{k notin RTj} xijk <=0"; //$NON-NLS-1$
/**
* Public constructor.
*
* @param problem
* reference to ProblemBuilder
*/
public CheckSwappableTask(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
@Override
protected boolean setUp() {
// Create a linear function to compute a value using the snapshot.
Linear linear = pb.lp.createLinear();
int m = pb.table.tj().size();
int p = pb.tasks.size();
for (int k = 0; k < p; k++) {
for (int j = 0; j < m; j++) {
if (!pb.table.kInRTj(k, j)) {
for (int i = 0; i < pb.xijk.length; i++) {
if (pb.xijk[i][j][k] != null) {
linear.add(pb.lp.createTerm(ONE, pb.xijk[i][j][k]));
}
}
}
}
}
// Create the constraint
this.constraint = pb.lp.addConstraint(CONSTRAINT_NAME, linear, null, ZERO);
// If the linear function is empty, their is not need to optimize
// the problem.
// If the last snapshot was feasible without swappable tasks, we
// don't need to optimize either.
if (pb.snapshot != null && Constraints.isSatisfied(this.constraint, pb.snapshot)) {
this.feasible = true;
return false;
}
// Fix all swappable tasks variableto zero
pb.lp.setObjectiveLinear(null);
// Check feasibility without swappable tasks
LOGGER.info("Check feasibility without swappable tasks."); //$NON-NLS-1$
return true;
}
}
\ No newline at end of file
......@@ -32,8 +32,11 @@ public class CheckTaskAssigned extends AbstractConstraintRunnable {
/**
* The constraint's name.
* <pre>
* SUM from{i=0} to{n} SUM from{k=0} to{p} xijk >= %d
* </pre>
*/
private static final String CONSTRAINT_NAME = "CheckTaskAssigned: SUM from{i=0} to{n} SUM from{k=0} to{p} xijk >= %d"; //$NON-NLS-1$
private static final String CONSTRAINT_NAME = "checkTaskAssigned_%d"; //$NON-NLS-1$
/**
* Default constructor.
......@@ -43,7 +46,6 @@ public class CheckTaskAssigned extends AbstractConstraintRunnable {
*/
public CheckTaskAssigned(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
/**
......@@ -59,23 +61,14 @@ public class CheckTaskAssigned extends AbstractConstraintRunnable {
@Override
protected boolean setUp() {
// Create a linear for the sum of every tasks
Linear linear = pb.lp.createLinear();
for (int i = 0; i < pb.xijk.length; i++) {
for (int j = 0; j < pb.xijk[i].length; j++) {
for (int k = 0; k < pb.xijk[i][j].length; k++) {
if (pb.xijk[i][j][k] != null) {
linear.add(pb.lp.createTerm(ONE, pb.xijk[i][j][k]));
}
}
}
}
if (linear.size() == 0) {
Linear linear = pb.xijk().createLinear();
if (linear.isEmpty()) {
feasible = true;
return false;
}
// Create the following constraint
// SUM xijk >= nbTask
Number number = Integer.valueOf(pb.tasks.size());
Number number = Integer.valueOf(pb.getTasks().size());
String name = String.format(CONSTRAINT_NAME, number);
this.constraint = pb.lp.addConstraint(name, linear, number, null);
// Reset the objective function
......
......@@ -41,7 +41,7 @@ public class MaxAllTaskAssigned extends AbstractConstraintRunnable {
/**
* The constraint's name.
*/
private static final String CONSTRAINT_NAME = "MaxAllTaskAssigned: SUM from{i=0} to {n} SUM from{j=0} to {m} SUM from{k=0} to {p} xijk >=%d"; //$NON-NLS-1$
private static final String CONSTRAINT_NAME = "MaxAllTaskAssigned_gte_%d"; //$NON-NLS-1$
/**
* The linear being maximized.
......@@ -56,7 +56,6 @@ public class MaxAllTaskAssigned extends AbstractConstraintRunnable {
*/
public MaxAllTaskAssigned(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
/**
......@@ -79,16 +78,7 @@ public class MaxAllTaskAssigned extends AbstractConstraintRunnable {
protected boolean setUp() {
LOGGER.debug("Maximize number of task assigned."); //$NON-NLS-1$
// Create a linear
this.linear = pb.lp.createLinear();
for (int i = 0; i < pb.xijk.length; i++) {
for (int j = 0; j < pb.xijk[i].length; j++) {
for (int k = 0; k < pb.xijk[i][j].length; k++) {
if (pb.xijk[i][j][k] != null) {
this.linear.add(pb.lp.createTerm(1, pb.xijk[i][j][k]));
}
}
}
}
this.linear = pb.xijk().createLinear();
// Set the linear as an objective
pb.lp.setObjectiveLinear(this.linear);
pb.lp.setObjectiveDirection(LinearProblem.MAXIMIZE);
......
......@@ -43,8 +43,8 @@ public class MaxEmployeePreferences extends SolveRunnable {
* @throws GeneratePlanifException
*/
private void handleNonAvailabilities(int i) throws GeneratePlanifException {
// Check if the employee is available
if (!(pb.fixNonAvailabilitiesVariables(i))) {
// Check if the employee has no non-availabilities and, if there are any, sets them to 0
if (pb.xijk().iEq(i).notAvailable().setBounds(ZERO, ZERO).isEmpty()) {
return;
}
// Clear the employee from current to less senior. We do not need to
......@@ -62,9 +62,16 @@ public class MaxEmployeePreferences extends SolveRunnable {
@Override
protected boolean runSolver() throws GeneratePlanifException {
// Loop on employee - the list is sorted by seniority
for (int i = 0; i < pb.xi.length; i++) {
for (int i = 0; i < pb.getEmployees().size(); i++) {
// Update monitor
pb.worked(ProblemBuilder.UNIT);
// Check if the employee is schedule to work according to xi bounds
if (pb.xi().iEq(i).isFixedTo(ZERO)) {
LOGGER.info("Employee {} " //$NON-NLS-1$
+ " is not working according to current values.", //$NON-NLS-1$
pb.employeeToString(i));
continue;
}
// Maximize the preferred position
MaxPreferredPosition solver = new MaxPreferredPosition(pb, i);
solver.run();
......@@ -83,7 +90,7 @@ public class MaxEmployeePreferences extends SolveRunnable {
handleNonAvailabilities(i);
}
// Loop on employee
for (int i = 0; i < pb.xi.length; i++) {
for (int i = 0; i < pb.getEmployees().size(); i++) {
pb.worked(ProblemBuilder.UNIT);
// Maximize the employee preferred section.
MaxPreferredSection maxPreferredSection = new MaxPreferredSection(pb, i);
......
......@@ -38,7 +38,7 @@ public class MaxImportantTaskAssigned extends AbstractConstraintRunnable {
/**
* The constraint's name.
*/
private static final String CONSTRAINT_NAME = "MaxImportantTaskAssigned: SUM from{i=0} to {n} SUM from{j=0} to {m} SUM from{k in ??} xijk >=%d"; //$NON-NLS-1$
private static final String CONSTRAINT_NAME = "MaxImportantTaskAssigned_gte_%d"; //$NON-NLS-1$
/**
* The linear being maximized.
......@@ -53,7 +53,6 @@ public class MaxImportantTaskAssigned extends AbstractConstraintRunnable {
*/
public MaxImportantTaskAssigned(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
/**
......@@ -76,16 +75,7 @@ public class MaxImportantTaskAssigned extends AbstractConstraintRunnable {
protected boolean setUp() {
LOGGER.debug("Maximize number of important task assigned."); //$NON-NLS-1$
// Create a linear with important tasks
this.linear = pb.lp.createLinear();
for (int i = 0; i < pb.xijk.length; i++) {
for (int j = 0; j < pb.xijk[i].length; j++) {
for (int k = 0; k < pb.xijk[i][j].length; k++) {
if (pb.xijk[i][j][k] != null && pb.tasks.get(k).getPosition().getClassified()) {
this.linear.add(pb.lp.createTerm(1, pb.xijk[i][j][k]));
}
}
}
}
this.linear = pb.xijk().classified().createLinear();
// Set the linear as an objective
pb.lp.setObjectiveLinear(this.linear);
pb.lp.setObjectiveDirection(LinearProblem.MAXIMIZE);
......
......@@ -18,11 +18,8 @@ import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Constraint;
import com.patrikdufresne.ilp.Linear;
import com.patrikdufresne.ilp.LinearProblem;
import com.patrikdufresne.ilp.util.Linears;
import com.patrikdufresne.ilp.util.Variables;
import com.planimod.core.EmployeePreference;
import com.planimod.core.Position;
import com.planimod.core.Task;
import com.planimod.core.Team;
/**
......@@ -36,7 +33,7 @@ public class MaxPreferredPosition extends SolveRunnable {
private static final transient Logger LOGGER = LoggerFactory.getLogger(MaxPreferredPosition.class);
/** The constraints name. */
private static final String CONSTRAINT_NAME = "MaxPreferredPosition: SUM from{k in PPi} xik] >= %d, for i=%d"; //$NON-NLS-1$
private static final String CONSTRAINT_NAME = "MaxPreferredPosition_gte_%d_%d"; //$NON-NLS-1$
/** Constraint to check feasibility */
private Constraint constraint;
......@@ -45,7 +42,7 @@ public class MaxPreferredPosition extends SolveRunnable {
private int i;
/** Employee preferred position team */
private int j;
private Team team;
/** Employee preferred position */
private Position pos;
......@@ -69,41 +66,12 @@ public class MaxPreferredPosition extends SolveRunnable {
public MaxPreferredPosition(ProblemBuilder problem, int i) {
super(problem);
this.i = i;
setFeasibilityPumpHeuristic(true);
}
private void assignEmployeeToPreferredPosition() throws GeneratePlanifException {
// For each k, fix the xijk variables to ZERO or ONE if the employee
// i is assigned to his preferred position.
for (int k = 0; k < pb.xijk[i][j].length; k++) {
if (pb.xijk[i][j][k] != null && pb.table.kInRTj(k, j) && ONE.equals(pb.snapshot.get(pb.xijk[i][j][k])) && pos.equals(pb.tasks.get(k).getPosition())) {
// The employee i is assigned to preferred position k, fix
// the variables xijk, for each i, and each j.
for (int j2 = 0; j2 < pb.xijk[i].length; j2++) {
if (pb.xijk[i][j2][k] != null) {
pb.xijk[i][j2][k].setLowerBound(ONE);
pb.xijk[i][j2][k].setUpperBound(ONE);
}
}
}
}
// Fix to zero (0) xijk and xij variables not corresponding to
// the team j for employee i. Be careful not to change fixed
// variables.
for (int j2 = 0; j2 < pb.xij[this.i].length; j2++) {
if (j2 != this.j) {
for (int k = 0; k < pb.xijk[this.i][j2].length; k++) {
if (pb.xijk[this.i][j2][k] != null && !Variables.isFixed(pb.xijk[this.i][j2][k])) {
pb.xijk[this.i][j2][k].setLowerBound(ZERO);
pb.xijk[this.i][j2][k].setUpperBound(ZERO);
}
}
}
if (pb.xij[this.i][j2] != null && !Variables.isFixed(pb.xij[i][j2])) {
pb.xij[this.i][j2].setLowerBound(this.j == j2 ? ONE : ZERO);
pb.xij[this.i][j2].setUpperBound(this.j == j2 ? ONE : ZERO);
}
}
pb.xijk().iEq(i).kInRTj(team).valueEq(ONE).kInPos(this.pos).setBounds(ONE, ONE);
LOGGER.info("Employee {} assigned to preferred position.", //$NON-NLS-1$
pb.employeeToString(i));
// Override the feasible variable.
......@@ -136,33 +104,34 @@ public class MaxPreferredPosition extends SolveRunnable {
@Override
protected boolean setUp() {
// Check if the employee is schedule to work according to xi bounds
if (pb.xi().iEq(i).isFixedTo(ZERO)) {
LOGGER.debug("Employee {} " //$NON-NLS-1$
+ " is not working according to current values.", //$NON-NLS-1$
pb.employeeToString(i));
this.feasible = false;
return false;
}
// Check if the employee has a preferred position
EmployeePreference pref = pb.preferences.get(i);
Team team;
if (pref == null || (this.pos = pref.getPreferredPosition()) == null || (team = pref.getPreferredPositionTeam()) == null) {
this.feasible = false;
return false;
}
this.j = pb.table.indexOf(team);
if (j < 0) {
// Check if the team is part of the problem
if (!pb.getTeams().contains(team)) {
this.feasible = false;
return false;
}
// Build the a linear with the preferred position tasks.
this.linear = pb.lp.createLinear();
for (int k = 0; k < pb.tasks.size(); k++) {
Task task = pb.tasks.get(k);
if (pb.xijk[i][j][k] != null && this.pos.equals(task.getPosition()) && pb.table.kInRTj(k, j) && !ProblemBuilder.isFixedToZero(pb.xijk[i][j][k])) {
linear.add(pb.lp.createTerm(1, pb.xijk[i][j][k]));
}
}
if (linear.size() == 0) {
this.linear = pb.xijk().iEq(i).kInRTj(team).kInPos(this.pos).notFixedTo(ZERO).createLinear();
if (linear.isEmpty()) {
// Can't assign employee if no task are available.
this.feasible = false;
return false;
}
// Simple solution to fix ticket #263. Create a constraint to force the employee to work on every task matching
// the preferred position without consideration of the Gl.
// the preferred position without consideration of the Shiftl.
String name = String.format(CONSTRAINT_NAME, linear.size(), Integer.valueOf(i));
this.constraint = pb.lp.addConstraint(name, linear, linear.size(), null);
LOGGER.debug("Assign employee {} to preferred position.", pb.employeeToString(i));
......@@ -189,7 +158,7 @@ public class MaxPreferredPosition extends SolveRunnable {
@Override
protected void unfeasible() throws GeneratePlanifException {
if (pos != null && j >= 0) {
if (pos != null && pb.getTeams().contains(team)) {
LOGGER.info("Employee {} not assigned to preferred position.", //$NON-NLS-1$
pb.employeeToString(i));
}
......
/**
* Copyright(C) 2013 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.planimod.core.planif;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Constraint;
import com.patrikdufresne.ilp.Linear;
import com.patrikdufresne.ilp.LinearProblem;
import com.patrikdufresne.ilp.util.Linears;
import com.planimod.core.EmployeePreference;
import com.planimod.core.Section;
......@@ -40,31 +34,20 @@ public class MaxPreferredSection extends SolveRunnable {
private static final transient Logger LOGGER = LoggerFactory.getLogger(MaxPreferredSection.class);
/**
* The employee's preferred section or null.
* The employee's preferred section or null
*/
private int a;
/**
* Constraint used to check feasibility.
*/
private Constraint constraint;
private final Section section;
/**
* The employee.
*/
private int i;
private final int i;
/**
* The linear being maximized.
*/
private Linear linear;
/**
* When this value is set, we have determine a lower bound using the snapshot value. In case we can't improve this
* value, the problem will be unfeasible and as such, we must sets this value to the constraint.
*/
private Number snapshotLowerBound;
/**
* Default constructor to maximize the preferred section of the employee <code>i</code>.
*
......@@ -77,39 +60,16 @@ public class MaxPreferredSection extends SolveRunnable {
public MaxPreferredSection(ProblemBuilder problem, int i) {
super(problem);
this.i = i;
this.a = -1;
setFeasibilityPumpHeuristic(true);
// Get preferred section for the given employee.
EmployeePreference pref = pb.preferences.get(i);
this.section = pref == null ? null : pref.getPreferredSection();
}
private void assignEmployeeToSection(Integer lowerBound) {
// Fix all the xik variables where the employee is working on the
// section.
for (int j2 = 0; j2 < pb.xijk[i].length; j2++) {
for (int k = 0; k < pb.xijk[i][j2].length; k++) {
// Check if the xijk is assigned to the employee and is
// matching the preferred section.
if (pb.xijk[i][j2][k] != null && pb.siTable.kInSa(k, this.a) && ONE.equals(pb.snapshot.get(pb.xijk[i][j2][k]))) {
// Find the matching Gl
for (List<Integer> coefs : pb.table.glIntersectTask(k)) {
// For this Gl, remove any tasks not matching the
// section.
for (int k2 = 0; k2 < coefs.size(); k2++) {
if (pb.xijk[i][j2][k2] != null && !pb.siTable.kInSa(k2, a) && ONE.equals(coefs.get(k2))) {
pb.xijk[i][j2][k2].setLowerBound(ZERO);
pb.xijk[i][j2][k2].setLowerBound(ZERO);
}
}
}
}
}
}
// Add a constraint to make sure the employee is assigned to the
// preferred section.
String name = String.format("SUM from{k in Si} xijk >=%d, for i=%d", lowerBound, i); //$NON-NLS-1$
if (this.constraint != null) {
this.constraint.dispose();
}
this.constraint = pb.lp.addConstraint(name, this.linear, lowerBound, null);
String name = String.format("MaxPreferredSection_gte_%d_%d", lowerBound, i); //$NON-NLS-1$
pb.lp.addConstraint(name, this.linear, lowerBound, null);
}
@Override
......@@ -121,48 +81,26 @@ public class MaxPreferredSection extends SolveRunnable {
@Override
protected boolean setUp() {
// Check if the employee is schedule to work according to snapshot
if (pb.snapshot != null && ZERO.equals(pb.snapshot.get(pb.xi[i]))) {
// Check if the employee is schedule to work according to xi bounds
if (pb.xi().iEq(i).isFixedTo(ZERO)) {
LOGGER.debug("Employee {} " //$NON-NLS-1$
+ " is not working according to current values.", //$NON-NLS-1$
pb.employeeToString(i));
this.feasible = false;
return false;
}
// Check if the employee as preferences
EmployeePreference pref = pb.preferences.get(i);
Section section;
if (pref == null || (section = pref.getPreferredSection()) == null) {
this.feasible = false;
return false;
}
// Check if preferred section is part of the planif.
this.a = pb.siTable.indexOf(section);
if (this.a < 0) {
// Check if the employee as preferred section.
// And check if preferred section is part of the planif.
if (section == null || !pb.getSections().contains(section)) {
this.feasible = false;
return false;
}
// Build the linear expression to be maximize
this.linear = pb.lp.createLinear();
for (int j = 0; j < pb.xijk[i].length; j++) {
for (int k = 0; k < pb.xijk[i][j].length; k++) {
// Add the variable to the linear only if it's part of the
// Team as real (see bug #68) and part of the preferred
// section.
if (pb.xijk[i][j][k] != null && pb.table.kInRTj(k, j) && pb.siTable.kInSa(k, a)) {
this.linear.add(pb.lp.createTerm(ONE, pb.xijk[i][j][k]));
}
}
}
if (this.linear.size() == 0) {
this.linear = pb.xijk().iEq(i).kInRTj().kInSection(section).createLinear();
if (this.linear.isEmpty()) {
this.feasible = false;
return false;
}
// Also sets the linear with a minimum value using a constraints. To
// improve performance, compute the minimum value using the
// snapshot.
int lowerBound = Math.max(ZERO.intValue(), (pb.snapshot != null ? (snapshotLowerBound = Linears.compute(this.linear, pb.snapshot)) : ZERO).intValue());
String name = String.format("SUM from{k in Si} xik >=%d, for i=%d", //$NON-NLS-1$
Integer.valueOf(lowerBound + 1),
Integer.valueOf(i));
this.constraint = pb.lp.addConstraint(name, this.linear, Integer.valueOf(lowerBound + 1), null);
// Set the linear as an objective
pb.lp.setObjectiveLinear(this.linear);
pb.lp.setObjectiveDirection(LinearProblem.MAXIMIZE);
......@@ -179,9 +117,7 @@ public class MaxPreferredSection extends SolveRunnable {
@Override
protected void unfeasible() {
if (this.snapshotLowerBound != null) {
assignEmployeeToSection(Integer.valueOf(snapshotLowerBound.intValue()));
} else if (a >= 0) {
if (pb.getSections().contains(section)) {
LOGGER.warn("Employee {} can't be assigned to " //$NON-NLS-1$
+ "preferred section.", pb.employeeToString(i)); //$NON-NLS-1$
}
......
......@@ -15,9 +15,13 @@
*/
package com.planimod.core.planif;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.planimod.core.Employee;
/**
* Adds a constrain for the preferred seniority Maximize the number of assigned employees with preferred seniority
* employees.
......@@ -40,10 +44,11 @@ public class MaxPreferredSeniorities extends SolveRunnable {
@Override
protected boolean runSolver() throws GeneratePlanifException {
for (int i = 0; i < pb.employees.size(); i++) {
List<Employee> employees = pb.getEmployees();
for (int i = 0; i < employees.size(); i++) {
// Avoid creating constraint for employees without preferred
// seniority and required preferred team.
if (pb.employees.get(i) != null && pb.employees.get(i).getPreferencialSeniority()) {
if (employees.get(i) != null && employees.get(i).getPreferencialSeniority()) {
MaxPreferredSeniority solver = new MaxPreferredSeniority(pb, i);
solver.run();
}
......
......@@ -50,7 +50,7 @@ public class MaxPreferredSeniority extends SolveRunnable {
/**
* The preferred team.
*/
private int j;
private final Team team;
/**
* The bounds before applying the unavailability
......@@ -69,15 +69,13 @@ public class MaxPreferredSeniority extends SolveRunnable {
public MaxPreferredSeniority(ProblemBuilder problem, int i) {
super(problem);
this.i = i;
this.employee = pb.employees.get(i);
this.employee = pb.getEmployees().get(i);
this.team = getFirstPreferredTeam();;
}
/**
* Return the preferred team of the employee i. If the employee i has no preference, returns null.
*
* @param i
* the employee
*
* @return The preferred team of the employee i or null if the employee has no team.
*/
private Team getFirstPreferredTeam() {
......@@ -92,7 +90,7 @@ public class MaxPreferredSeniority extends SolveRunnable {
@Override
protected boolean runSolver() throws GeneratePlanifException {
// Check if the employee can be assigned to preferred team.
return (new CheckPreferredTeam(pb, i, j).run());
return (new CheckPreferredTeam(pb, i, team).run());
}
@Override
......@@ -100,18 +98,13 @@ public class MaxPreferredSeniority extends SolveRunnable {
/*
* Verify if the employee has a valid preferred team.
*/
this.originalBound = BoundSnapshot.create(pb.variablesForEmployee(i));
// Apply the non-availabilities
pb.fixNonAvailabilitiesVariables(i);
XijkVariableSet vars = pb.xijk().iEq(i).notAvailable();
this.originalBound = vars.createBoundSnapshot();
vars.setBounds(ZERO, ZERO);
// extract conditions to be reused unfeasible
// Check if has pref
Team team = getFirstPreferredTeam();
if (team == null) {
this.feasible = false;
return false;
}
// check if production
if ((j = pb.table.indexOf(team)) <= -1) {
// Check if has pref and check if the team is part of the problem
if (this.team == null || !pb.getTeams().contains(team)) {
this.feasible = false;
return false;
}
......@@ -138,16 +131,9 @@ public class MaxPreferredSeniority extends SolveRunnable {
this.originalBound.restore();
// Adds the constraint saying that an employee with preferred seniority has to work at least once in the
// planif
Linear linear = pb.lp.createLinear();
for (int j2 = 0; j2 < pb.xijk[i].length; j2++) {
for (int k = 0; k < pb.xijk[i][j2].length; k++) {
if (pb.xijk[i][j2][k] != null) {
linear.add(pb.lp.createTerm(ONE, pb.xijk[i][j2][k]));
}
}
}
String name = String.format("SUM from{j} to{m} SUM from{k} to {p} xijk >= 1, for i=%d", //$NON-NLS-1$
i);
Linear linear = pb.xijk().iEq(i).createLinear();
// SUM from{j} to{m} SUM from{k} to {p} xijk >= 1, for i=
String name = String.format("MaxPreferredSeniority_%d", this.i); //$NON-NLS-1$
pb.lp.addConstraint(name, linear, ONE, null);
this.feasible = true;
LOGGER.info("Employee {} can't be assigned to his prefered team but has to work.", pb.employeeToString(i)); //$NON-NLS-1$
......
......@@ -62,11 +62,11 @@ public class MaxPreferredTeams extends SolveRunnable {
// Loop on preferred team
for (int a = 0; a < preferredTeams.size(); a++) {
// Check if the employee can be assign to team j
int j = pb.table.indexOf(preferredTeams.get(a));
if (j != -1) {
Team team = preferredTeams.get(a);
if (pb.getTeams().contains(team)) {
// Check if there is a feasible solution where employee i is
// assigned to team j.
CheckPreferredTeam solve1 = new CheckPreferredTeam(pb, i, j);
CheckPreferredTeam solve1 = new CheckPreferredTeam(pb, i, team);
solve1.run();
if (solve1.isFeasible()) {
return true;
......@@ -74,27 +74,33 @@ public class MaxPreferredTeams extends SolveRunnable {
// FIXME if we determine it's not possible to assign the
// employee to the given team, we should fix all the
// variable to zero.
// pb.xij().iEq(i).jEq(team).setBounds(ZERO, ZERO);
}
}
// FIXME if the employee can't be assigned to any preferred team, it
// If the employee can't be assigned to any preferred team, it
// might be required to create a constraint for this employee to
// work at least once. Otherwise, if this employee is not available,
// most likely he will not be required to work and he will be
// replace by another employee.
pb.xi().iEq(i).setBounds(ONE, ONE);
return false;
}
@Override
protected boolean setUp() {
// Check if the employee is schedule to work according to snapshot
if (pb.snapshot != null && (pb.xi[i] == null || ZERO.equals(pb.snapshot.get(pb.xi[i])))) {
feasible = false;
// Check if the employee is schedule to work according to xi bounds
if (pb.xi().iEq(i).isFixedTo(ZERO)) {
LOGGER.debug("Employee {} " //$NON-NLS-1$
+ " is not working according to current values.", //$NON-NLS-1$
pb.employeeToString(i));
this.feasible = false;
return false;
}
// Check if employee as preference
EmployeePreference pref = pb.preferences.get(i);
if (pref == null || (this.preferredTeams = pref.getPreferredTeam()) == null || this.preferredTeams.size() == 0) {
// No preference for this employee, leave the function
pb.xi().iEq(i).setBounds(ONE, ONE);
feasible = false;
return false;
}
......
/**
* Copyright(C) 2013 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.planimod.core.planif;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Status;
/**
* Used to maximize the use of senior employee by checking if their is solutions without least senior employee.
* <p>
......@@ -49,8 +48,7 @@ public class MaxSeniorEmployee extends SolveRunnable {
*/
public MaxSeniorEmployee(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
this.runnables = new CheckEmployeeNotWorking[problem.xi.length];
this.runnables = new CheckEmployeeNotWorking[problem.getEmployees().size()];
}
/**
......@@ -61,23 +59,23 @@ public class MaxSeniorEmployee extends SolveRunnable {
* @return True if every higher employees are part of the solution.
*/
private boolean checkHigherEmployeesAssigned(int from, int to) {
if (pb.snapshot == null) {
if (!pb.lp.isFeasible()) {
return false;
}
// Check if every other employee are schedule to work.
int i2 = to;
while (i2 >= from && ONE.equals(pb.snapshot.get(pb.xi[i2]))) {
while (i2 >= from && pb.xi().iEq(i2).valueEq(ONE)) {
i2--;
}
return i2 < from;
}
/**
* Restore the previous snapshot.
* Restore the previous values.
*/
public void engage(int from) {
// Release all the constraints.
for (int i = from; i < pb.xi.length; i++) {
for (int i = from; i < pb.getEmployees().size(); i++) {
if (this.runnables[i] != null) {
this.runnables[i].engage();
}
......@@ -85,7 +83,7 @@ public class MaxSeniorEmployee extends SolveRunnable {
}
/**
* This implementation will call run. TODO improve performance by checking satisfiability using previous snapshot.
* This implementation will call run. TODO improve performance by checking satisfiability using previous values.
*
* @param from
* @throws GeneratePlanifException
......@@ -99,7 +97,7 @@ public class MaxSeniorEmployee extends SolveRunnable {
*/
public void release(int from) {
// Release all the constraints.
for (int i = from; i < pb.xi.length; i++) {
for (int i = from; i < pb.getEmployees().size(); i++) {
if (this.runnables[i] != null) {
this.runnables[i].release();
}
......@@ -126,7 +124,7 @@ public class MaxSeniorEmployee extends SolveRunnable {
@Override
protected boolean runSolver() throws GeneratePlanifException {
for (int i = pb.xi.length - 1; i >= this.from; i--) {
for (int i = pb.getEmployees().size() - 1; i >= this.from; i--) {
// Check if every higher employee are working, is so there
// is no
// need to compute any more
......
......@@ -121,7 +121,7 @@ public class MaxTaskAssigned extends SolveRunnable {
this.checkTaskAssigned = null;
// Ask user if the search should continue
// TODO Filter tasks list
if (!pb.monitor.callbackContinue(new UnassignedTaskCallbackReason(pb.tasks))) {
if (!pb.monitor.callbackContinue(new UnassignedTaskCallbackReason(pb.getTasks()))) {
throw new GeneratePlanifException("Cancel by user", //$NON-NLS-1$
GeneratePlanifException.CANCEL_BY_USER);
}
......
......@@ -20,7 +20,6 @@ import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Linear;
import com.patrikdufresne.ilp.LinearProblem;
import com.patrikdufresne.ilp.util.Linears;
/**
* This runnable is used to minimize the number of swappable tasks assigned to employees.
......@@ -38,7 +37,8 @@ public class MinAllSwappableTask extends AbstractConstraintRunnable {
/**
* The constraint's name
*/
private static final String CONSTRAINT_NAME = "SUM from{i=0} to{n} SUM from{j=0} to{m} SUM from{k notin RTj} xijk <= %d"; //$NON-NLS-1$
// SUM from{i=0} to{n} SUM from{j=0} to{m} SUM from{k notin RTj} xijk <= %d
private static final String CONSTRAINT_NAME = "MinAllSwappableTask_%f"; //$NON-NLS-1$
/**
* The linear objective.
......@@ -58,13 +58,12 @@ public class MinAllSwappableTask extends AbstractConstraintRunnable {
*/
public MinAllSwappableTask(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
@Override
protected void feasible() {
// The solution improved, fix the number of swappable tasks.
Integer value = Integer.valueOf(pb.lp.getObjectiveValue().intValue());
Double value = pb.lp.getObjectiveValue();
fixSwappableTask(value);
}
......@@ -76,7 +75,7 @@ public class MinAllSwappableTask extends AbstractConstraintRunnable {
* @param value
* the maximum number of swappable tasks assigned.
*/
private void fixSwappableTask(Integer value) {
private void fixSwappableTask(Double value) {
// To avoid assignment to more swappable, create or redefine the
// constraint.
String name = String.format(CONSTRAINT_NAME, value);
......@@ -92,27 +91,13 @@ public class MinAllSwappableTask extends AbstractConstraintRunnable {
@Override
protected boolean setUp() {
// Create the linear function to be optimized
this.linear = pb.lp.createLinear();
int m = pb.table.tj().size();
int p = pb.tasks.size();
for (int k = 0; k < p; k++) {
for (int j = 0; j < m; j++) {
if (!pb.table.kInRTj(k, j)) {
for (int i = 0; i < pb.xijk.length; i++) {
if (pb.xijk[i][j][k] != null) {
this.linear.add(pb.lp.createTerm(ONE, pb.xijk[i][j][k]));
}
}
}
}
}
// Use the snapshot as a starting solution to be improved.
if (pb.snapshot != null) {
this.snapshotUpperBound = Integer.valueOf(Linears.compute(this.linear, pb.snapshot).intValue());
// Restrict the feasibility to a lower value.
Integer upperBound = Integer.valueOf(this.snapshotUpperBound.intValue() - 1);
String name = String.format(CONSTRAINT_NAME, upperBound);
this.constraint = pb.lp.addConstraint(name, this.linear, null, upperBound);
this.linear = this.pb.xijk().kNotInRTj().createLinear();
// Check if the linear is empty.
if (this.linear.isEmpty()) {
LOGGER.info("No swappable task to be assigned."); //$NON-NLS-1$
this.feasible = false;
this.snapshotUpperBound = 0;
return false;
}
pb.lp.setObjectiveLinear(this.linear);
pb.lp.setObjectiveDirection(LinearProblem.MINIMIZE);
......@@ -130,7 +115,7 @@ public class MinAllSwappableTask extends AbstractConstraintRunnable {
protected void unfeasible() throws GeneratePlanifException {
if (this.snapshotUpperBound != null) {
// It was impossible to improve the best known solution.
fixSwappableTask(this.snapshotUpperBound);
fixSwappableTask(this.snapshotUpperBound.doubleValue());
} else {
// Should be feasible, throw exception.
throwProblemUnFeasible();
......
......@@ -15,14 +15,11 @@
*/
package com.planimod.core.planif;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.patrikdufresne.ilp.Linear;
import com.patrikdufresne.ilp.LinearProblem;
import com.patrikdufresne.ilp.util.Linears;
/**
* Minimize the number of employee assigned.
......@@ -36,19 +33,17 @@ public class MinEmployeeAssigned extends AbstractConstraintRunnable {
/**
* The constraint's name.
* <pre>
* SUM from{i} to{n} xi <= %f
* </pre>
*/
private static final String CONSTRAINT_NAME = "SUM from{i} to{n} xi <= %d"; //$NON-NLS-1$
private static final String CONSTRAINT_NAME = "MinEmployeeAssigned_%f"; //$NON-NLS-1$
/**
* The linear being minimized.
*/
private Linear linear;
/**
* If sets, define the best known solution according to the snapshot.
*/
private Integer snapshotUpperBound;
/**
* Public constructor.
*
......@@ -57,13 +52,12 @@ public class MinEmployeeAssigned extends AbstractConstraintRunnable {
*/
public MinEmployeeAssigned(ProblemBuilder problem) {
super(problem);
setFeasibilityPumpHeuristic(true);
}
@Override
protected void feasible() {
// Get the objective value
Integer value = Integer.valueOf(pb.lp.getObjectiveValue().intValue());
Double value = Double.valueOf(pb.lp.getObjectiveValue().doubleValue());
fixMaximumNumberOfEmployee(value);
}
......@@ -73,7 +67,7 @@ public class MinEmployeeAssigned extends AbstractConstraintRunnable {
* @param value
* the maximum number of employee.
*/
private void fixMaximumNumberOfEmployee(Integer value) {
private void fixMaximumNumberOfEmployee(Double value) {
// Create or redefine the constraint upper bound.
String name = String.format(CONSTRAINT_NAME, value);
if (this.constraint != null) {
......@@ -88,25 +82,7 @@ public class MinEmployeeAssigned extends AbstractConstraintRunnable {
protected boolean setUp() {
LOGGER.debug("Minimize number of employee."); //$NON-NLS-1$
// Create the following linear function : SUM xi
Double[] coefs = new Double[pb.xi.length];
Arrays.fill(coefs, ONE);
this.linear = pb.lp.createLinear(coefs, pb.xi);
// TODO Use the snapshot value to restrict the feasibility and force
// the solver to improve the solution.
// To reduce the computing time, sets a maximum using a constraint.
if (pb.snapshot != null) {
this.snapshotUpperBound = Integer.valueOf(Linears.compute(linear, pb.snapshot).intValue());
if (ZERO.equals(this.snapshotUpperBound)) {
this.snapshotUpperBound = null;
this.feasible = false;
return false;
}
// Create a constraint to force the solver to improve the
// current solution.
Integer upperBound = Integer.valueOf(this.snapshotUpperBound.intValue() - 1);
String name = String.format(CONSTRAINT_NAME, upperBound);
this.constraint = pb.lp.addConstraint(name, linear, null, upperBound);
}
this.linear = pb.xi().createLinear();
// Set the linear as an objective.
pb.lp.setObjectiveLinear(this.linear);
pb.lp.setObjectiveDirection(LinearProblem.MINIMIZE);
......@@ -117,17 +93,12 @@ public class MinEmployeeAssigned extends AbstractConstraintRunnable {
protected void tearDown() {
// Remove objective
pb.lp.setObjectiveLinear(null);
this.snapshotUpperBound = null;
this.linear = null;
}
@Override
protected void unfeasible() throws GeneratePlanifException {
if (this.snapshotUpperBound != null) {
fixMaximumNumberOfEmployee(this.snapshotUpperBound);
} else {
// It's a maximization problem, it' must be feasible.
throwProblemUnFeasible();
}
// It's a maximization problem, it' must be feasible.
throwProblemUnFeasible();
}
}
\ No newline at end of file