diff --git a/src/wolfgoatcabbage/CP.java b/src/wolfgoatcabbage/CP.java new file mode 100644 index 0000000000000000000000000000000000000000..0e43d95a1fa8f79996fabaf6b0ae03a1ece1dada --- /dev/null +++ b/src/wolfgoatcabbage/CP.java @@ -0,0 +1,153 @@ +package wolfgoatcabbage; + +import ilog.concert.IloConstraint; +import ilog.concert.IloException; +import ilog.concert.IloIntVar; +import ilog.cp.IloCP; +import static wolfgoatcabbage.Problem.CABBAGE; +import static wolfgoatcabbage.Problem.GOAT; +import static wolfgoatcabbage.Problem.ITEMCOUNT; +import static wolfgoatcabbage.Problem.TMAX; +import static wolfgoatcabbage.Problem.WOLF; + +/** + * CP implements a constraint programming model for the logic puzzle. + * + * @author Paul A. Rubin (rubin@msu.edu) + */ +public final class CP implements AutoCloseable { + + /* CPO objects. + * For the variables `far` and `farmer`, value 1 signals the entity ends the + * period on the far bank while value 0 signals the entity ends the period + * on the near bank. + */ + private final IloCP cp; // the model instance + private final IloIntVar[][] far; // far[t][i] is the location of item i + // at time t + private final IloIntVar[][] tFar; // transpose of far + private final IloIntVar[] farmer; // location of the farmer at time t + private final IloIntVar[] carry; // carry[t] is the index of the item + // carried across the river at time t + // (ITEMCOUNT if the farmer is deadheading) + private final IloIntVar done; // the time period at which the problem + // is done + + /** + * Constructor. + * @throws IloException if the model cannot be created + */ + public CP() throws IloException { + // Instantiate the model. + cp = new IloCP(); + // Create the variables. + done = cp.intVar(0, TMAX - 1); + far = new IloIntVar[TMAX][ITEMCOUNT + 1]; + tFar = new IloIntVar[ITEMCOUNT + 1][TMAX]; + carry = new IloIntVar[TMAX]; + farmer = new IloIntVar[TMAX]; + for (int t = 0; t < TMAX; t++) { + carry[t] = cp.intVar(0, ITEMCOUNT); + farmer[t] = cp.boolVar(); + for (int i = 0; i < ITEMCOUNT + 1; i++) { + far[t][i] = cp.boolVar(); + tFar[i][t] = far[t][i]; + } + } + // The objective is to minimize the time at which the trip is done. + cp.addMinimize(done); + // All inventory is on the near bank at the end of period 0. + for (int i = 0; i < ITEMCOUNT + 1; i++) { + cp.addEq(far[0][i], 0); + } + // The farmer also starts on the near bank. + cp.addEq(farmer[0], 0); + // Nothing is carried in period 0. + cp.addEq(carry[0], ITEMCOUNT); + // The necessary condition for being done is that all items be on the + // far bank. + for (int i = 0; i < ITEMCOUNT; i++) { + cp.addEq(cp.element(tFar[i], done), 1); + } + // In each period, the status of the item carried (if any) flips while + // the status of other items does not change. + for (int t = 1; t < TMAX; t++) { + for (int i = 0; i < ITEMCOUNT; i++) { + IloConstraint c1 = cp.eq(carry[t], i); + IloConstraint c2 = cp.eq(cp.sum(far[t - 1][i], far[t][i]), 1); + IloConstraint c3 = cp.eq(far[t - 1][i], far[t][i]); + cp.add(cp.ifThenElse(c1, c2, c3)); + } + } + // In odd numbered periods, trips are from near bank to far bank, and only + // items on the near bank can be carried. In even numbered periods, trips + // are from far bank to near bank, and only items on the far bank can + // be carried. + for (int t = 1; t < TMAX; t++) { + int z = (t % 2 == 0) ? 1 : 0; + cp.addEq(cp.element(far[t - 1], carry[t]), z); + } + // Until the last trip, the farmer ends up on the far bank at the end of + // odd numbered periods and the near bank at the end of even numbered + // periods. After the last trip, the farmer stays on the far bank. + for (int t = 1; t < TMAX; t++) { + IloConstraint c1 = cp.le(done, t); + IloConstraint c2 = cp.eq(farmer[t], 1); + IloConstraint c3 = cp.eq(farmer[t], t % 2); + cp.add(cp.ifThenElse(c1, c2, c3)); + } + // If the wolf and goat are in the same place at the end of any period, + // the farmer must also be there. The same is true if the goat and cabbage + // are in the same place. + for (int t = 1; t < TMAX; t++) { + IloConstraint c1 = cp.eq(far[t][WOLF], far[t][GOAT]); + IloConstraint c2 = cp.eq(far[t][GOAT], far[t][CABBAGE]); + IloConstraint c3 = cp.eq(far[t][GOAT], farmer[t]); + cp.add(cp.ifThen(c1, c3)); + cp.add(cp.ifThen(c2, c3)); + } + // Suppress solver output. + cp.setOut(null); + } + + /** + * Solves the model. + * @return the number of trips required + * @throws IloException if the solver fails + */ + public int solve() throws IloException { + cp.solve(); + return cp.getIntValue(done); + } + + /** + * Gets the solution to the puzzle. + * @return a string displaying the solution + * @throws IloException if the solution does not exist + */ + public String getSolution() throws IloException { + int nTrips = cp.getIntValue(done); + double[][] x = new double[nTrips + 1][ITEMCOUNT]; + // Get the values of the far[][] array for actual items and trips used. + for (int t = 0; t <= nTrips; t++) { + for (int i = 0; i < ITEMCOUNT; i++) { + x[t][i] = cp.getValue(far[t][i]); + } + } + // Get the values of the carry[] array for trips actually used. + double[] y = new double[nTrips + 1]; + for (int t = 0; t <= nTrips; t++) { + y[t] = cp.getValue(carry[t]); + } + return Problem.report(nTrips, x, y); + } + + /** + * Closes the model. + */ + @Override + public void close() { + cp.end(); + } + +} diff --git a/src/wolfgoatcabbage/WolfGoatCabbage.java b/src/wolfgoatcabbage/WolfGoatCabbage.java index 7f4e010ef4e07bcbc617b356340f9965659a129d..754f08fb188c27ece90ff2cb83a58acd1d20d969 100644 --- a/src/wolfgoatcabbage/WolfGoatCabbage.java +++ b/src/wolfgoatcabbage/WolfGoatCabbage.java @@ -18,6 +18,7 @@ public final class WolfGoatCabbage { * @param args the command line arguments (ignored) */ public static void main(final String[] args) { + System.out.println("Applying the MIP model."); try (MIP model = new MIP()) { double z = model.solve(); System.out.println("Optimal number of trips = " + z + "."); @@ -26,6 +27,15 @@ public final class WolfGoatCabbage { } catch (IloException ex) { System.out.println("CPLEX exception:\n" + ex.getMessage()); } + System.out.println("\nApplying the CP model:"); + try (CP model = new CP()) { + int z = model.solve(); + System.out.println("Optimal number of trips = " + z + "."); + System.out.println("Solution:"); + System.out.println(model.getSolution()); + } catch (IloException ex) { + System.out.println("CPOptimizer exception:\n" + ex.getMessage()); + } } }