/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.components.cluster.capacitated.data;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.components.cluster.capacitated.solver.ContinueCallback;
import com.opendoorlogistics.components.cluster.capacitated.solver.EvaluatedSolution;
import com.opendoorlogistics.components.cluster.capacitated.solver.FilterCallbackEvents;
import com.opendoorlogistics.components.cluster.capacitated.solver.Problem;
import com.opendoorlogistics.components.cluster.capacitated.solver.Solver;
import com.opendoorlogistics.components.cluster.capacitated.solver.Solver.HeuristicType;
import com.opendoorlogistics.core.api.impl.ODLApiImpl;
import com.opendoorlogistics.core.utils.strings.Strings;
final public class ExampleClustererData {
private boolean logToConsole;
private boolean heterogeneousCostPerUnitTravel = false;
private boolean hetereogenousClusterCapacity = false;
private boolean fixedClusterSubset=false;
private static final double POSITION_RANGE = 100;
public ExampleClustererData(boolean logToConsole) {
this.logToConsole = logToConsole;
}
private static double[] randomAllocation(int nbAssignments, double total, Random random) {
// divide used capacity
double[] randoms = new double[nbAssignments - 1];
for (int i = 0; i < randoms.length; i++) {
randoms[i] = total * random.nextDouble();
}
Arrays.sort(randoms);
double sum = 0;
double[] ret = new double[nbAssignments];
for (int i = 0; i < nbAssignments; i++) {
// get quantity
double lastValue = 0;
if (i > 0) {
lastValue = randoms[i - 1];
}
double value = total;
if (i < nbAssignments - 1) {
value = randoms[i];
}
ret[i] = value - lastValue;
sum += ret[i];
// if (logToConsole) {
// System.out.println("Assignment " + i + ": " + ret[i] + ", total allocated=" + sum);
// }
}
// if (logToConsole) {
// System.out.println("Check total quantity: " + sum + " should be approximately equal to " + total);
// }
return ret;
}
public Problem createProblem(ODLApi api,int nbLocations, int nbClusters, double percentageUsedCapacity) {
double totalCapacity = 1000;
double usedCapacity = totalCapacity * percentageUsedCapacity / 100.0;
double capacityPerCluster = totalCapacity / nbClusters;
if (logToConsole) {
System.out.println("Total capacity: " + totalCapacity);
System.out.println("Used capacity: " + usedCapacity);
}
// divide used capacity up by nbCustomers
Random random = new Random(1);
double[] randoms = randomAllocation(nbLocations, usedCapacity, random);
// create customers
ArrayList<Point2D.Double> pnts = new ArrayList<>(nbLocations);
ArrayList<Location> locations = new ArrayList<Location>(nbLocations);
for (int i = 0; i < nbLocations; i++) {
// get position
pnts.add(new Point2D.Double(random.nextDouble() * POSITION_RANGE, random.nextDouble() * POSITION_RANGE));
Location loc = new Location();
locations.add(loc);
loc.setQuantity(randoms[i]);
loc.setId("Location" + Integer.toString(i));
// if testing cost per unit travel, set the cost arbitrarily high on first p customers
// so they will always be chosen as cluster centres
if (heterogeneousCostPerUnitTravel && i < nbClusters) {
loc.setCostPerUnitTravel(1000.0 * nbLocations);
}
}
// create clusters
Cluster[] clusters = new Cluster[nbClusters];
for (int i = 0; i < nbClusters; i++) {
clusters[i] = new Cluster();
clusters[i].setClusterId(Integer.toString(i + 1));
clusters[i].setCapacity(capacityPerCluster);
}
if (hetereogenousClusterCapacity) {
double[] capacities = randomAllocation(nbClusters, totalCapacity, random);
for (int i = 0; i < nbClusters; i++) {
clusters[i].setCapacity(capacities[i]);
}
}
if(fixedClusterSubset){
for (int i = 0; i < nbClusters; i++) {
if(i%2==0){
// create dummy location
double factor = (double)POSITION_RANGE * ((double)i /(nbClusters-1) );
pnts.add(new Point2D.Double(factor, factor));
Location location = new Location();
location.setId("Cluster" + i);
location.setQuantity(0);
locations.add(location);
// set cluster to use it
clusters[i].setFixedLocation(1);
clusters[i].setLocationKey(location.getId());
}
}
}
// create travel
nbLocations = locations.size();
Travel[] travel = new Travel[nbLocations * nbLocations];
int indx = 0;
for (int i = 0; i < nbLocations; i++) {
Point2D.Double from = pnts.get(i);
for (int j = 0; j < nbLocations; j++) {
Point2D.Double to = pnts.get(j);
double cost = from.distance(to);
Travel trv = new Travel();
trv.setFromLocation(locations.get(i).getId());
trv.setToLocation(locations.get(j).getId());
trv.setCost(cost);
travel[indx++] = trv;
}
}
return new Problem(api,locations, Arrays.asList(clusters),Arrays.asList(travel));
}
public boolean isHeterogeneousCostPerUnitTravel() {
return heterogeneousCostPerUnitTravel;
}
public void setHeterogeneousCostPerUnitTravel(boolean heterogeneousCostPerUnitTravel) {
this.heterogeneousCostPerUnitTravel = heterogeneousCostPerUnitTravel;
}
public boolean isHetereogenousClusterCapacity() {
return hetereogenousClusterCapacity;
}
public void setHetereogenousClusterCapacity(boolean hetereogenousClusterCapacity) {
this.hetereogenousClusterCapacity = hetereogenousClusterCapacity;
}
public boolean isFixedClusterSubset() {
return fixedClusterSubset;
}
public void setFixedClusterSubset(boolean fixedClusterSubset) {
this.fixedClusterSubset = fixedClusterSubset;
}
public static void main(String[] args) {
ExampleClustererData exampleClustererData = new ExampleClustererData(true);
// exampleClustererData.testLimitedCandidates = true;
exampleClustererData.setFixedClusterSubset(true);
Problem problem = exampleClustererData.createProblem(new ODLApiImpl(),100, 10, 90);
final FilterCallbackEvents filter = new FilterCallbackEvents();
System.out.println("Starting solver");
Solver solver = new Solver(problem, new ContinueCallback() {
@Override
public ContinueOption continueOptimisation(int nbSteps, HeuristicType type, EvaluatedSolution best) {
if (nbSteps > 10) {
return ContinueOption.FINISH_NOW;
}
if (filter.hasStateChanged(nbSteps, type, best) && best != null) {
System.out.println(new Date().toString() + " - Step: " + nbSteps + " (" + Strings.convertEnumToDisplayFriendly(type.name()) + ")"
+ " Best: " + best.getCost());
}
return ContinueOption.KEEP_GOING;
}
});
solver.setUseSwapMoves(true);
EvaluatedSolution sol = solver.run();
System.out.println(sol);
}
}