JOpt.SDK and JOpt.NET are software development kits for complementary software partners. These SDKs enable you to access route optimization and automatic scheduling features of the DNA JOpt product familiy and to integrate them into your own applications. JOpt.SDK encapsulates all necessary optimization functionality and provides a comprehensive API that offers a generic optimization interface for different industries. In contrast to JOpt.Personal the SDK focusses on optimization functions as a service. It does not come with user interfaces, map depiction, data bases or distance matrices. These extensions and adjustments are supposed to be introduced by the user of the SDK while integrating it into the application. The SDK will allow for many suitable adjustments through a comprehensive set of functions. This will enable any programmer to design and control his or her own optimization processes.
The JOpt.SDK demo version will give you a good understanding of how to use the SDK and to check whether the offered functions are suitable for your application. The demo version is restricted to few resources and nodes (12 in total) only, but offers unrestricted functionality. Although the problem space is restricted, the basic ideas of what can be done with the SDK and of how to use it can be found out.
We would like to encourage you to modify the examples, try several configurations and to observe the changes of the results. This will give you some more insight into the way how the SDK and the optimization algorithm is working.
After you have obtained the demo version from the download area of the DNA web site extract the content into a suitable directory. The demo version will expand into several sub-directories. Depending whether you have downloaded the Java or the .NET version you will find the following libraries:
or
in the <lib> folder. The libraries will contain the SDK so you will initially have to include the libraries into your development environment. The demo version is shipped with different examples showing how to make use of the library. The example sources are located in the <src> folder. The API despription for each interface function can be accessed via the index.html page located in the <-,doc> folder. Before starting your work with JOpt you should take the time to read our implementation guidelines and our FAQs in order to make your work with JOpt a positive experience.
You finally found JOpt because you are probably dealing with so called NP-complete problems like the VRPTW problem. That means, unless you have unlimited (calculation) time to spend you cannot find THE OPTIMUM solution for your problem. Such kind of problems can only be solved by heuristic algorithms, like JOpt that usually come very close to an optimum within a reasonable timeframe. Still, due to its heuristic nature results may slightly differ from time to time. |
Once having understood this you may also like to read the following implementation guidelines. We have published this guidelines in order to help you getting started with JOpt and to make your work with JOpt a satisfactory experience. In case you will encounter any problems, please don't hesitate to contact us for further assistance. Now, have fun in solving your problems... |
Step One: Find the Master Problem ! |
Try to identify and formulate a problem that is representative for the majority of your planning jobs and thus can serve as a basis to adjust the optimizer in order to properly handle all similar problems of that type in a latter stage. |
Step Two: Start simple ! |
Set up the basic problem with simple settings (e.g. work from 8-18, timewindows from 8-18). At this stage try to avoid any special objectives (e.g. match narrow timewindows, match narrow working times, capacities and skills, etc.) We will address this later... |
Step Three: Stepwise introduce further or more specific objectives ! |
As in real live, having more objectives in parallel will result in less ideal results. In such cases we usualy determine and assign priorities to the respective objectives. In JOpt you can achieve this by setting the right properties. |
Step Four: Check significance ! |
Once you find your result satisfactory, try different scenarios and return to step three until you finally end up with good results for all your scenarios. Congratulations: You have finally found the desired settings and prepared JOpt for the production stage. |
The demo version examples show possible ways to make use of the library. The first example sets up a typical tour optimization problem. The second example points out how specific restrictions for the resources and nodes can be introduced. In the third example the simple distance estimation between the nodes of the first two examples are exchanged by a distance matrix which holds real road distances between the nodes. Examples 4 to 6 show some more advanced techniques like pick-up & delivery problems and load capacities.
The first example Example01.java explains what has to be done to set up a simple tour optimization programme with 2 resources (vehicles with drivers Jack and John) and 10 nodes (tour stops in the towns K&ooml;ln, Düren, Wuppertal, Oberhausen, Essen, Mannheim, Heilbronn, Stuttgart, München and Nürnberg). It is assumed that the two resources will start each tour from their depot located at Frankfurt. In the first step of the example all the nodes are defined and handed over to the optimization algorithm using the function addElement(INode) of the IOptimization class. Each node consists of:
// 0.) define the weekly opening hours for the nodes we want to visit
OpeningHours[] weeklyOpeningHours= new OpeningHours[1];
weeklyOpeningHours[0] = new OpeningHours(new GregorianCalendar(2007,Calendar.MARCH,6,8,0,0),new GregorianCalendar(2007,Calendar.MARCH,6,18,0,0));
// 1.) add the nodes to be visited
TimeWindowGeoNode koeln = new TimeWindowGeoNode("Koeln", 50.9333,6.95,weeklyOpeningHours,1200,1);
koeln.setOpeningHours(weeklyOpeningHours);
this.addElement(koeln);
In the next step the two resources John and Jack are defined. These elements of type CapacityResource comprise:
// 2.) add the resources
WorkingHours[] workingHours= new WorkingHours[1];
workingHours[0] = new WorkingHours(new GregorianCalendar(2007,Calendar.MARCH,6,8,0,0),new GregorianCalendar(2007,Calendar.MARCH,6,20,0,0));
CapacityResource jack = new CapacityResource("Jack",50.1167,7.68333,12.0,2800.0,workingHours);
this.addElement(jack);
CapacityResource john = new CapacityResource("John",50.1167,7.68333,12.0,2800.0,workingHours);
this.addElement(john);
...
Nodes and resources define the problem space for the optimization algorithm wich will now try to find the solution with minimum costs. The optimization programme is initiated using the function startAsynchronousOptimizationRun. The algorithm will generate X generations and will stop the optimization when having reached 400 iterations. During the optimization process a progress information is given, informing you on the percentage of iteration the algorithm has done so far.
Finally, the results are presented in the function onAsynchronousOptimizationResult. In this example the interface variable IOptimizationResult is used to print out all calculated values. The reults comprise:
public void onAsynchronousOptimizationResult(IOptimizationResult result)
{
//read the header
System.out.println("costfunction : " + result.getTotalCost());
System.out.println("total route time : " + result.getTimeTotal()/3600.0 + " [h]");
System.out.println("total driving time : " + result.getTimeTrip()/3600.0 + " [h]");
System.out.println("total service time : " + result.getTimeStop()/3600.0 + " [h]");
System.out.println("total idle time : " + result.getTimeIdle()/3600.0 + " [h]");
System.out.println("total break time : " + result.getTimeBreak()/3600.0 + " [h]");
System.out.println("total distance : " + result.getTotalDistance()/1000.0 + " [km]");
...
For each generated tour (in general there will be two tours, one for each of the resources) you will receive the same values for the particular tour/route but also the sequence of the tour stops (nodes), the arrival times at the stops and the name of the resource chosen for the tour.
// iterate through each tour
for(int i = 0; i < result.getRoutes().length; i++){
System.out.println("\n-----------------------------------------------------------");
System.out.println("Route information");
System.out.println("Route:" + result.getRoutes()[i].getRouteId() + " resource scheduled: " +
result.getRoutes()[i].getResourceId());
System.out.println("-----------------------------------------------------------");
System.out.println("route time : " + result.getRoutes()[i].getTimeTotal()/3600.0 + " [h]");
System.out.println("driving time : " + result.getRoutes()[i].getTimeTrip()/3600.0 + " [h]");
System.out.println("service time : " + result.getRoutes()[i].getTimeStop()/3600.0 + " [h]");
System.out.println("idle time : " + result.getRoutes()[i].getTimeIdle()/3600.0 + " [h]");
System.out.println("break time : " + result.getRoutes()[i].getTimeBreak()/3600.0 + " [h]");
System.out.println("distance : " + result.getRoutes()[i].getTotalDistance()/1000.0 + " [km]");
...
The sequence of the tour stops and their allocation to the resources is the main result of the automatic tour optimization process. During the optimization run, a huge amount of tours and tour stop combinations will be generated, checked and modified in order to find out the optimal resource allocation and node succession for the given problem. The algorithm will always try to minimize the total costs, i.e. normally the distance or time to travel. The better the solution is, the lower the costs will be. Time windows, working overtime and many other things may be introduced as constraints. Constraint violations should be punished by appropriate costs (sometimes extremely high) to tell the algorithm to avoid solutions which will not satisfy boundary conditions. However, it may be that for some problem constellations it is simply not possible to find a solution that will prevent all constraint violations - for example when having contradicting constraints. But if configured properly, serious constraint violations will typically raise the costs remarkably and will therefore lead to acceptable solutions. Constraint violations will always be indicated in the tour summary and appear either on route- or nodelevel. The following violations are indicated:
Level | Class | Type | Values |
---|---|---|---|
Node | CONSTRAINTVIOLATION | TIMECONSTRAINT | EARLY | LATE |
Node | CONSTRAINTVIOLATION | NODETYPE | RESOURCE_NOT_ALLOWED |
Node | CONSTRAINTVIOLATION | CAPACITY | OVERLOAD |
Route | CONSTRAINTVIOLATION | MAXTIME | EXCEEDED |
Route | CONSTRAINTVIOLATION | MAXDISTANCE | EXCEEDED |
Route | CONSTRAINTVIOLATION | WORKINGHOURS | EXCEEDED |
for(int j = 0; j < result.getRoutes()[i].getRouteNodes().length; j++){
IScheduledNode scheduledNode = result.getRoutes()[i].getRouteNodes()[j];
System.out.print("\n"
+ result.getRoutes()[i].getRouteId() + "."
+ scheduledNode.getSequenceNumber() + " "
+ scheduledNode.getNodeId()
+ " arrival time: "
+ scheduledNode.getArrivalTime().get(Calendar.YEAR) + "/"
+ (scheduledNode.getArrivalTime().get(Calendar.MONTH)+1) + "/"
+ scheduledNode.getArrivalTime().get(Calendar.DAY_OF_MONTH) + " "
+ scheduledNode.getArrivalTime().get(Calendar.HOUR_OF_DAY) + ":"
+ scheduledNode.getArrivalTime().get(Calendar.MINUTE));
for(int k = 0; k < scheduledNode.getViolations().length; k++){
IViolation violation = scheduledNode.getViolations()[k];
if(violation instanceof IAttributeValuePair)
{
IAttributeValuePair avp = (IAttributeValuePair) violation;
System.out.print(" [" + avp.getCategory() + "/" + avp.getAttribute() + "/" + avp.getValue() + "]");
}
}
}
In example 2 it is shown that constraints can be used to restrict nodes to a set of special resources. The idea behind is that some nodes should only be visited by a subset of resources who hold a special skill or qualification. Other resources are not allowed to go there and should therefore be excluded from a possible solution. Some vehicles for instance must be qualified to carry dangerous goods, or some machines can only be repaired by special technicians. You can add such a qualification to a node by using the member function setType. In example 2 the qualification "DANGEROUS_GOODS" has been added to the node München and Nürnberg.
//we are going to pick up dangerous goods in munich
// 1.) add the nodes to be visited
TimeWindowGeoNode munich = ...
...
munich.setType("DANGEROUS_GOODS");
this.addElement(munich);
...
The identical qualification "DANGEROUS_GOODS" is also added to the attributes of the resource John using addPermittedNodeType.
CapacityResource john = ...
...
// Allow John to transport dangerous goods
john.addPermittedNodeType("DANGEROUS_GOODS");
this.addElement(john);
...
The optimization algorithm will now take care to allocate München and Nürnberg to John only, since he is the only resource who holds the adequate qualification to go there. As already explained in example 1, the mechanism of constraint violation ensures the desired behaviour of the algorithm. The appearance of München or Nürnberg in the tour of Jack, who is not qualified for handling with dangerous goods, would lead to a constraint violation which in turn would generate higher costs. This also points out that a well-balanced cost structure is necessary if several constraints compete with the overall optimization goal of minimizing tour time or tour distance.
Example 1 and 2 used a very simple model of distances between the tour stops. It was simply the geographic distance between the coordinates of the nodes (linear distance). For many applications this model is not good enough and should be exchanged with real values considering real driving distances and times. These distances are typically available as distance matrices. Such a matrix lists all distances a car or truck will need to go when driving from one node to another. The optimization algorithm needs all distances between each node so that a complete matrix is expected. Note, that there might be a difference between driving from A to B to driving from B to A, e.g. because of one-way roads. Therefore, a complete and non-symmetric matrix is necessary.
//set up the matrix
double[][] matrix = {{0.0,70.0,90.0,45.0,240.0, 580.0,440.0,300.0,380.0,40.0,260.0},
{70.0,0.0,25.0,65.0,300.0,640.0,500.0,360.0,440.0,35.0,280.0},
{90.0,25.0,0.0,80.0,305.0,650.0,510.0,370.0,460.0,25.0,270.0},
{45.0, 65.0,80.0,0.0,275.0,615.0,475.0,330.0,405.0,80.0,300.0},
{240.0,300.0,305.0,275.0,0.0,330.0,300.0,70.0,170.0,280.0,50.0},
{580.0,640.0,650.0,615.0,330.0,0.0,140.0,320.0,230.0,620.0,340.0},
{440.0,500.0,510.0,475.0,300.0,140.0,0.0,200.0,240.0,480.0,200.0},
{300.0,360.0,370.0,330.0,70.0,320.0,200.0,0.0,80.0,330.0,110.0},
{380.0,440.0,460.0,405.0,170.0,230.0,240.0,80.0,0.0,405.0,200.0},
{40.0,35.0,25.0,80.0,280.0,620.0,480.0,330.0,405.0,0.0,285.0},
{260.0,280.0,270.0,300.0,50.0,340.0,200.0,110.0,200.0,285.0,0.0}};
//add the nodes to be visited
TimeWindowGeoNode koeln = ...
koeln.setDistMatrixId(0);
this.addElement(koeln);
TimeWindowGeoNode oberhausen = ...
oberhausen.setDistMatrixId(1);
this.addElement(oberhausen);
TimeWindowGeoNode essen = ...
essen.setDistMatrixId(2);
this.addElement(essen);
...
In the example all the distances between the towns of example 1 are given as the variable double [][] matrix in kilometres. In order to enable rapid access to the matrix, you must allocate the proper matrix index as the node ID using the function setDistMatrixId(matrix_index). The matrix itself is added to the program by using addDistanceMatrix(matrix).
this.addDistanceMatrix(matrix);
...
Once having defined the real driving distance you may also want to set up a time matrix. A time matrix defines the travel times between each pair of nodes in seconds.
double[][] timematrix = new double[11][11];
for(int i = 0; i < timematrix.length; i++)
{
for(int j = 0; j < timematrix[i].length; j++)
{
timematrix[i][j] = matrix[i][j] * 1000.0 / 22.0; // km * 1000 / speed [m/s] = time [s]
}
}
this.addTimeMatrix(timematrix);
Time matrices have been introduced in order to respect real travel times for different road types instead of linear calculated times by means of distance/ resources' average speed. Hence the resources' average speed will be ignored when having set the time matrix. However, you can define individual vehicle speeds by adding a vehicle specific time factor to the resource. In the example below Jack will travel twice as fast as John (e.g. Jack needs only half of the standard travel times).
CapacityResource john = new CapacityResource("John",50.1167,7.68333,12.0,1200.0,workingHours);
john.setTimeMatrixFactor(1.0);
john.setDistMatrixId(10);
this.addElement(john);
CapacityResource jack = new CapacityResource("Jack",50.1167,7.68333,12.0,1200.0,workingHours);
jack.setTimeMatrixFactor(0.5);
jack.setDistMatrixId(10);
this.addElement(jack);
this.addDistanceMatrix(matrix);
Example 4 is identical to example 3 except that we have used different optimization properties. Optimization properties can be used to influence the optimzation results according to multiple objectives. Like in real life, multiple objectives (optimum distance, timewindows, load balance) will lead to a competetive condition between these objectives and we will have to make a trade-off decision and prioritise objectives to our needs. To demonstrate this we have chosen a tight time window (15:00-16:00) for the node 'oberhausen'.
Initially, we have set the 'JOptWeight.TotalDistance' and 'JOptWeight.TimeWindow' to 100 to tell the optimizer that both objectives are very important. With this setting all time windows are adhered to and the route also looks quite good at a first glance. However, decreasing the 'JOptWeight.TimeWindow' to 10 will improve geographic results while allowing small time deviations (eg. be early in oberhausen). We can even further decrease 'JOptWeight.TimeWindow'to 1.0 which will further minimize the maximum distance while allowing more significant deviations.
Please use your own figures to see how the behaviour of the program will change when modifying weight values. The values are typically allowed in the range of 1.0 (less important) to 100.0 (very important). The following properties can be set:
Property | Values | Description |
---|---|---|
JOptExitCondition.Type | JOptGenerationCount | JOptCostConvergency | exit condition |
JOptExitCondition.Count | 1..N | number of generations |
JOptExitCondition.JOptConvergencyCount | 1..N | number of generations |
JOpt.RouteType | CLOSED | OPEN | plan open or closed routes by default |
JOptWeight.TimeWindow | 1..N | weight timewindows |
JOptWeight.TotalDistance | 1..N | weight total distance |
JOptWeight.RouteDistance | 1..N | weight route distance |
JOptWeight.RouteTime | 1..N | weight route time |
JOptWeight.Capacity | 1..N | weight resource capacity |
JOptWeight.NodeType | 1..N | weight node type |
JOptWeight.ResourceLoadBalance | 1..N | weight exploit working hours |
JOpt.OptimizationRule | DAILY | WEEKLY | WEEKLY: automatically add overnight stays if required |
...
Properties props = new Properties();
props.setProperty("JOptExitCondition.JOptGenerationCount","1000"); // Maximum Number of Generations
props.setProperty("JOptExitCondition.Type","JOptCostConvergency"); // The algorithm may stop in advance if there is a cost convergency for more than n Generations
props.setProperty("JOptExitCondition.JOptConvergencyCount","300");
props.setProperty("JOptWeight.TotalDistance","100.0"); // 100 = most important
props.setProperty("JOptWeight.RouteDistance","1.0");
props.setProperty("JOptWeight.NodeType","1.0");
props.setProperty("JOptWeight.TimeWindow","10.0"); // 10 = also very important.
//Try more moderate weights of 5.0 and 1.0 to gain more optimum distances while allowing more and more timewindow deviations
...
Example 05 introduces the usage of relationships and capacities. Relationships define how nodes are related to each other. You may use relationships to define pick up and delivery relations within your schedule. Within this example we define several pick up nodes and one drop node. Through the usage of relationships JOpt will take care that each pick up node will be visited before the drop node.
...
// create a relationship: e.g pick up goods and deliver them to wuppertal
// 1st we define the 'Wuppertal' part of the relationship
Relationship[] pickUpGoodsForWuppertal = new Relationship[1];
Relationship rs = new Relationship();
rs.setRelationshipType(1);
rs.setRelatedNode("Wuppertal");
pickUpGoodsForWuppertal[0] = rs;
...
...
// Second we assign the relationship to 'Oberhausen' in order to create a relationship between wuppertal and oberhausen
TimeWindowGeoNode oberhausen = ...
oberhausen.setRelationships(pickUpGoodsForWuppertal);
...
Furthermore Example05 shows the usage of capacity and loads. JOpt allows for arbitrary capacity and load vectors. By assigning the capacity vector to a resource, you define a set of capacities for this resource (e.g. [max weight, max volume, max pieces]). While calculating the route JOpt now reduces the given capacity by the given loadvector of each visited node. Positive load means goods are picked up, negative load means goods are dropped. JOpt automatically takes care, that your resource will not be overloaded alongside the route.
...
CapacityResource rep1 = new CapacityResource("Jack",50.1167,7.68333,12.0,2800.0,workingHours);.
double[] truck1InitialLoad = { 0 }; .
rep1.setInitialLoad(truck1InitialLoad); .
rep1.addCapacity(30); .
.
double[] essenLoad = {15}; // 15 parcels (or an arbitrary unit) will be picked up in essen
essen.setLoad(essenLoad);
...
double[] wuppertalLoad = {-30}; // 30 parcels (or an arbitrary unit) will be dropped in wuppertal
wuppertal.setLoad(wuppertalLoad);
...
This Example shows how you can schedule resources for multiple days (e.g. Monday till Friday) within one run. Each node has multiple TimeWindows, one for each day and each resource is assumed to return to its depot each day after all respective nodes have been visited.
...
// 0.) define the weekly opening hours for the nodes we want to visit .
OpeningHours[] weeklyOpeningHours= new OpeningHours[3];.
weeklyOpeningHours[0] = new OpeningHours(new GregorianCalendar(2008,Calendar.NOVEMBER,6,8,0,0),...);.
weeklyOpeningHours[1] = new OpeningHours(new GregorianCalendar(2008,Calendar.NOVEMBER,7,8,0,0),...);.
weeklyOpeningHours[2] = new OpeningHours(new GregorianCalendar(2008,Calendar.NOVEMBER,8,8,0,0),...);.
.
.
// 1.) add the nodes to be visited .
TimeWindowGeoNode koeln = new TimeWindowGeoNode("Koeln", 50.9333,6.95,weeklyOpeningHours,1200,1);.
this.addElement(koeln);.
.
TimeWindowGeoNode oberhausen = new TimeWindowGeoNode("Oberhausen", 51.4667,6.85,weeklyOpeningHours,1200,1);.
this.addElement(oberhausen);.
.
...
Now define the working hours for each day, assign it to the respective resource and add that resource once for each day. (e.g. if you want the resource to work from Monday till Friday add the resource 5 times)
...
WorkingHours[] workingHours= new WorkingHours[2];
workingHours[0] = new WorkingHours(new GregorianCalendar(2008,Calendar.NOVEMBER,6,8,0,0),...);
workingHours[1] = new WorkingHours(new GregorianCalendar(2008,Calendar.NOVEMBER,7,8,0,0),...);
// We have to add the resource once for each day it is available
CapacityResource rep1 = new CapacityResource("Jack",50.1167,7.68333,9.0,1000.0,workingHours);
this.addElement(rep1);
CapacityResource rep2 = new CapacityResource("Jack",50.1167,7.68333,9.0,1000.0,workingHours);
this.addElement(rep2);
...
In real life each resource will have fix and running costs. To address this JOpt allows to set the vehicles fix, per kilometer and per hour cost. During optimization JOpt tries to reduce overall cost. Depending on the fix cost this may lead to a reduction of vehicles for the same number of orders. Of course this only applies as long as the savings due to reduction of vehicles is not compensated by the raise of cost for late arrivals or additional kilometers.
...
CapacityResource rep1 = new CapacityResource("Jack",50.1167,7.68333,9.0,1000.0,workingHours);
double fixCost = 1000.0;
double perHourCost = 10.0;
double perKilometerCost = 1.0;
rep1.setCost(fixCost,perHourCost,perKilometerCost);
this.addElement(rep1);
...
Node priorities can be used to define whether a timewindow violation is acceptable or mission critical for an individual node. Use a high priority if it is not acceptable to be late at a specific node and a low priority whenever any arrival time violation is justifiable in order to gain better results with respect to other objectives like total driving time, total distance, etc.
...
int prioHigh = 100;
int prioLow = 1;
TimeWindowGeoNode important = new TimeWindowGeoNode("Important", 49.4883,8.46472,weeklyOpeningHoursTight,1200,prioHigh);
this.addElement(important);
TimeWindowGeoNode regular = new TimeWindowGeoNode("Regular", 49.4883,8.46472,weeklyOpeningHoursTight,1200,prioLow);
this.addElement(important);
...
Basically JOpt knows 2 optimization modes Fuzzy Mode and Strict Mode where each mode has again different strategies to adopt specific classes of problems.
In Fuzzy Mode (default mode) JOpt tries to allocate each given node to an appropriate resource and creates as many routes as required to fulfill the given optimization task. Even if the number of resources will not be sufficient for the given problem or if some objectives cannot be satisfied within the given scenario JOpt will still schedule each node and marks the violating nodes and resources with a violation respectively.
Hence, in Fuzzy Mode you should always consider to put plenty resources to the optimization problem in order to avoid violations. Jopt will never use more than the minimum number of resources required in order to satisfy all objectives and leaves the remaining resources unused. So putting plenty resources to JOpt „a priori“ is not a disadvantage at all but prevents from unwanted violations.
...
Properties props = new Properties();
props.setProperty("JOpt.Assisted","TRUE");
props.setProperty("JOpt.AssistedMode","SINGLEDEPOT_MIXEDFLEET");
this.addElement(props); // “this” is derived from class optimization
...
In Strict Mode JOpt will only schedule as many nodes as it can without causing a violation. All other nodes remain unscheduled and are returned as unassigned nodes. The user will then have to handle the unassigned nodes in a separate step (e.g. set up another optimization run to schedule the remaining nodes)
...
Properties props = new Properties();
props.setProperty("JOpt.Assisted","TRUE");
props.setProperty("JOpt.AssistedMode","STRICT_SINGLEDEPOT_MIXEDFLEET");
this.addElement(props); // “this” is derived from class optimization
...
In some applications not all nodes are equal. Some of the locations are more important to be included in a route than others. When using Strict Mode you can assign each node a different importance. JOpt will then setup a combined criteria that takes into account the node importance as well as the "how-optimal-will-that-node-fit-into-the-schedule" factor. This combined factor will then decide whether a specific node will be included in the schedule or excluded. Hence more important nodes are more likely to be included in the schedule, but not at any price (e.g. if including an important node would lead to a schedule that is far from optimal this specific node may also be excluded in order to gain a more optimal schedule). To set a node's importance just add the following code:
...
TimeWindowGeoNode node = new TimeWindowGeoNode(“foo”,lat,lon,weeklyOpeningHours,1200,1);
node.setImportance(10); // 1 is the default value. Importance should always be >=1 !
...
In order to obtain the unassigned nodes add the following lines of code to the onAsynchronousOptimizationResult(IOptimizationResult result) callback.
...
public void onAsynchronousOptimizationResult(IOptimizationResult result)
{
for(int k = 0; k < result.getUnassignedNodes().length; k++)
{
System.out.println(result.getUnassignedNodes()[k].getNodeId());
}
}
...
If you are planning multiple days you can use the compacting factor to compact the schedule at the beginning. In addition you can use the node priority in order to have the highly prioritized nodes served early in the schedule (The Node Priority will be multiplied with the compacting factor for the specific node and strengthen the compacting effect).
...
Properties props = new Properties();
props.setProperty("JOptWeight.CompactingFactor","10");
this.addElement(props);
...