1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00
This commit is contained in:
Stefan Schroeder 2013-06-04 10:25:47 +02:00
commit 3581d6e097
435 changed files with 46952 additions and 0 deletions

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry including="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry including="**/*.java" kind="src" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

23
jsprit-analysis/.project Normal file
View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>jsprit-analysis</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,7 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding//src/test/resources=UTF-8
encoding/<project>=UTF-8
encoding/src=UTF-8

View file

@ -0,0 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

339
jsprit-analysis/license.txt Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

50
jsprit-analysis/pom.xml Normal file
View file

@ -0,0 +1,50 @@
<!--
Copyright (C) 2013 Stefan Schroeder
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Contributors:
Stefan Schroeder - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>jsprit</groupId>
<artifactId>jsprit-project</artifactId>
<version>0.0.1</version>
<relativePath>../jsprit-project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jsprit-analysis</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.14</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jsprit-core</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,149 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package analysis;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.algo.IterationEndsListener;
/**
* VehicleRoutingAlgorithm-Listener to record the solution-search-progress.
*
* <p>Register this listener in VehicleRoutingAlgorithm.
*
* @author stefan schroeder
*
*/
public class AlgorithmSearchProgressChartListener implements IterationEndsListener, AlgorithmEndsListener {
private static Logger log = Logger.getLogger(AlgorithmSearchProgressChartListener.class);
private double[] bestResults;
private double[] worstResults;
private double[] avgResults;
private List<Double> bestResultList = new ArrayList<Double>();
private List<Double> worstResultList = new ArrayList<Double>();
private List<Double> avgResultList = new ArrayList<Double>();
private String filename;
/**
* Constructs chart listener with target png-file (filename plus path).
*
* @param pngFileName
*/
public AlgorithmSearchProgressChartListener(String pngFileName) {
super();
this.filename = pngFileName;
}
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
log.info("create chart " + filename);
if(bestResultList.isEmpty()){
log.warn("cannot create chart since no results available.");
return;
}
bestResults = new double[bestResultList.size()];
worstResults = new double[worstResultList.size()];
avgResults = new double[avgResultList.size()];
double maxValue = 0.0;
double minValue = Double.MAX_VALUE;
double[] iteration = new double[bestResultList.size()];
for (int i = 0; i < bestResultList.size(); i++) {
if(bestResultList.get(i) < minValue) minValue = bestResultList.get(i);
if(worstResultList.get(i) < minValue) minValue = worstResultList.get(i);
if(avgResultList.get(i) < minValue) minValue = avgResultList.get(i);
if(bestResultList.get(i) > maxValue) maxValue = bestResultList.get(i);
if(worstResultList.get(i) > maxValue) maxValue = worstResultList.get(i);
if(avgResultList.get(i) > maxValue) maxValue = avgResultList.get(i);
bestResults[i] = bestResultList.get(i);
worstResults[i] = worstResultList.get(i);
avgResults[i] = avgResultList.get(i);
iteration[i] = i;
}
XYSeriesCollection coll = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createXYLineChart("search-progress","iterations", "results",coll, PlotOrientation.VERTICAL,true,true,false);
addSeries("bestResults", iteration, bestResults, coll);
addSeries("worstResults", iteration, worstResults, coll);
addSeries("avgResults", iteration, avgResults, coll);
XYPlot plot = chart.getXYPlot();
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
Range rangeY = new Range(minValue-0.05*minValue,maxValue + 0.05*maxValue);
yAxis.setRange(rangeY);
try {
ChartUtilities.saveChartAsJPEG(new File(filename), chart, 1000, 600);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void addSeries(String string, double[] iteration, double[] results, XYSeriesCollection coll) {
XYSeries series = new XYSeries(string, true, true);
for(int i=0;i<iteration.length;i++){
series.add(iteration[i], results[i]);
}
coll.addSeries(series);
}
@Override
public void informIterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
double worst = 0.0;
double best = Double.MAX_VALUE;
double sum = 0.0;
for(VehicleRoutingProblemSolution sol : solutions){
if(sol.getCost() > worst) worst = sol.getCost();
if(sol.getCost() < best) best = sol.getCost();
sum += sol.getCost();
}
bestResultList.add(best);
worstResultList.add(worst);
avgResultList.add(sum/(double)solutions.size());
}
}

View file

@ -0,0 +1,204 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package analysis;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.Range;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import util.Coordinate;
import util.Locations;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
/**
* A plotter to plot vehicle-routing-solution and routes respectively.
*
* @author stefan schroeder
*
*/
public class SolutionPlotter {
private static Logger log = Logger.getLogger(SolutionPlotter.class);
/**
* Plots the solution to pngFile, with title.
*
* <p>This can only plot if vehicles and jobs have locationIds and coordinates (@see Coordinate). Otherwise a warning message is logged
* and method returns but does not plot.
*
* @param vrp
* @param solution
* @param pngFile target path with filename.
* @see VehicleRoutingProblem, VehicleRoutingProblemSolution
*/
public static void plotSolutionAsPNG(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution, String pngFile, String plotTitle){
final Map<String,Coordinate> locs = new HashMap<String, Coordinate>();
boolean locationsRetrieved = retrieveLocations(locs,vrp);
if(!locationsRetrieved) return;
Locations locations = new Locations(){
@Override
public Coordinate getCoord(String id) {
return locs.get(id);
}
};
plotRoutesAsPNG(solution.getRoutes(), locations, pngFile, plotTitle);
}
/**
* Plots the a collection of routes to pngFile.
*
*
* @param routes
* @param locations indicating the locations for the tour-activities.
* @param pngFile target path with filename.
* @param plotTitle
* @see VehicleRoute
*/
public static void plotRoutesAsPNG(Collection<VehicleRoute> routes, Locations locations, String pngFile, String plotTitle) {
log.info("plot routes to " + pngFile);
XYSeriesCollection coll = new XYSeriesCollection();
int counter = 1;
double maxX = 0;
double minX = Double.MAX_VALUE;
double maxY = 0;
double minY = Double.MAX_VALUE;
for(VehicleRoute route : routes){
if(route.isEmpty()) continue;
XYSeries series = new XYSeries(counter, false, true);
Coordinate startCoord = locations.getCoord(route.getStart().getLocationId());
series.add(startCoord.getX(), startCoord.getY());
if(startCoord.getX() > maxX) maxX = startCoord.getX();
if(startCoord.getY() > maxY) maxY = startCoord.getY();
if(startCoord.getX() < minX) minX = startCoord.getX();
if(startCoord.getY() < minY) minY = startCoord.getY();
for(TourActivity act : route.getTourActivities().getActivities()){
Coordinate coord = locations.getCoord(act.getLocationId());
series.add(coord.getX(), coord.getY());
if(coord.getX() > maxX) maxX = coord.getX();
if(coord.getY() > maxY) maxY = coord.getY();
if(coord.getX() < minX) minX = coord.getX();
if(coord.getY() < minY) minY = coord.getY();
}
Coordinate endCoord = locations.getCoord(route.getEnd().getLocationId());
series.add(endCoord.getX(), endCoord.getY());
if(endCoord.getX() > maxX) maxX = endCoord.getX();
if(endCoord.getY() > maxY) maxY = endCoord.getY();
if(endCoord.getX() < minX) minX = endCoord.getX();
if(endCoord.getY() < minY) minY = endCoord.getY();
coll.addSeries(series);
counter++;
}
JFreeChart chart = ChartFactory.createXYLineChart(plotTitle,"x-coordinate", "y-coordinate",coll, PlotOrientation.VERTICAL,true,true,false);
XYPlot plot = chart.getXYPlot();
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setBaseShapesVisible(true);
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
xAxis.setTickUnit(new NumberTickUnit(10));
// Range rangeX = new Range(minX - 0.05*minX, maxX+0.05*maxX);
Range rangeX = new Range(minX, maxX);
xAxis.setRange(rangeX);
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
// yAxis.setTickUnit(new NumberTickUnit(10));
// Range rangeY = new Range(minY-0.05*minY,maxY + 0.05*maxY);
Range rangeY = new Range(minY,maxY);
yAxis.setRange(rangeY);
try {
ChartUtilities.saveChartAsPNG(new File(pngFile), chart, 1000, 600);
} catch (IOException e) {
log.error("cannot plot");
log.error(e);
e.printStackTrace();
}
}
private static boolean retrieveLocations(Map<String, Coordinate> locs, VehicleRoutingProblem vrp) {
for(Vehicle v : vrp.getVehicles()){
String locationId = v.getLocationId();
if(locationId == null){
log.warn("cannot plot solution, since vehicle " + v + " has no locationId.");
return false;
}
Coordinate coord = v.getCoord();
if(coord == null){
log.warn("cannot plot solution, since vehicle " + v + " has no location-coordinate.");
return false;
}
locs.put(locationId, coord);
}
for(Job j : vrp.getJobs().values()){
if(j instanceof Service){
String locationId = ((Service) j).getLocationId();
if(locationId == null){
log.warn("cannot plot solution, since job " + j + " has no locationId.");
return false;
}
Coordinate coord = ((Service) j).getCoord();
if(coord == null){
log.warn("cannot plot solution, since job " + j + " has no location-coordinate.");
return false;
}
locs.put(locationId, coord);
}
else{
throw new IllegalStateException("job is not a service. this is not supported yet.");
}
}
return true;
}
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package analysis;
import basics.VehicleRoutingProblemSolution;
import basics.route.DefaultVehicleRouteCostCalculator;
import basics.route.VehicleRoute;
/**
* Printer to print the details of a vehicle-routing-problem solution.
*
* @author stefan schroeder
*
*/
public class SolutionPrinter {
/**
* Enum to indicate verbose-level.
*
* <p> Print.CONCISE and Print.VERBOSE are available.
*
* @author stefan schroeder
*
*/
public enum Print {
CONCISE,VERBOSE
}
/**
* Prints costs and #vehicles to stdout (System.out.println).
*
* @param solution
*/
public static void print(VehicleRoutingProblemSolution solution){
System.out.println("[costs="+solution.getCost() + "]");
System.out.println("[#vehicles="+solution.getRoutes().size() + "]");
}
/**
* Prints the details of the solution according to a print-level, i.e. Print.CONCISE or PRINT.VERBOSE.
*
* <p>CONCISE prints total-costs and #vehicles.
* <p>VERBOSE prints the route-details additionally. If the DefaultVehicleRouteCostCalculator (which is the standard-calculator)
* is used in VehicleRoute, then route-costs are differentiated further between transport, activity, vehicle, driver and other-costs.
*
* @param solution
* @param level
*/
public static void print(VehicleRoutingProblemSolution solution, Print level){
if(level.equals(Print.CONCISE)){
print(solution);
}
else{
print(solution);
System.out.println("routes");
int routeCount = 1;
for(VehicleRoute route : solution.getRoutes()){
System.out.println("[route="+routeCount+"][departureTime="+route.getStart().getEndTime()+"[total=" + route.getCost() + "]");
if(route.getVehicleRouteCostCalculator() instanceof DefaultVehicleRouteCostCalculator){
DefaultVehicleRouteCostCalculator defaultCalc = (DefaultVehicleRouteCostCalculator) route.getVehicleRouteCostCalculator();
System.out.println("[transport=" + defaultCalc.getTpCosts() + "][activity=" + defaultCalc.getActCosts() +
"][vehicle=" + defaultCalc.getVehicleCosts() + "][driver=" + defaultCalc.getDriverCosts() + "][other=" + defaultCalc.getOther() + "]");
}
routeCount++;
}
}
}
}

View file

@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package analysis;
import java.util.Collection;
import org.apache.log4j.Logger;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.algo.AlgorithmStartsListener;
public class StopWatch implements AlgorithmStartsListener, AlgorithmEndsListener{
private static Logger log = Logger.getLogger(StopWatch.class);
private double ran;
private double startTime;
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
reset();
start();
}
public double getCompTimeInSeconds(){
return (ran)/1000.0;
}
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
stop();
log.info("computation time [in sec]: " + getCompTimeInSeconds());
}
public void stop(){
ran += System.currentTimeMillis() - startTime;
}
public void start(){
startTime = System.currentTimeMillis();
}
public void reset(){
startTime = 0;
ran = 0;
}
@Override
public String toString() {
return "stopWatch: " + getCompTimeInSeconds() + " sec";
}
public double getCurrTimeInSeconds() {
return (System.currentTimeMillis()-startTime)/1000.0;
}
}

BIN
jsprit-core/.DS_Store vendored Normal file

Binary file not shown.

30
jsprit-core/.classpath Normal file
View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry including="**/*.java" kind="src" path="src/test/resources"/>
<classpathentry including="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

29
jsprit-core/.project Normal file
View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>jsprit-core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.maven.ide.eclipse.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,7 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding//src/test/resources=UTF-8
encoding/<project>=UTF-8
encoding/src=UTF-8

View file

@ -0,0 +1,8 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View file

@ -0,0 +1,9 @@
#Fri Mar 15 09:09:12 CET 2013
activeProfiles=
eclipse.preferences.version=1
fullBuildGoals=process-test-resources
includeModules=false
resolveWorkspaceProjects=true
resourceFilterGoals=process-resources resources\:testResources
skipCompilerPlugin=true
version=1

339
jsprit-core/license.txt Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

60
jsprit-core/pom.xml Normal file
View file

@ -0,0 +1,60 @@
<!--
Copyright (C) 2013 Stefan Schroeder
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Contributors:
Stefan Schroeder - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>jsprit</groupId>
<artifactId>jsprit-project</artifactId>
<version>0.0.1</version>
<relativePath>../jsprit-project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jsprit-core</artifactId>
<name>jsprit-core</name>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math</artifactId>
<version>2.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xerces</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.log4j.Logger;
import basics.Job;
import basics.algo.InsertionEndsListener;
import basics.algo.InsertionListener;
import basics.algo.InsertionStartsListener;
import basics.algo.JobInsertedListener;
import basics.route.VehicleRoute;
abstract class AbstractInsertionStrategy implements InsertionStrategy{
private static Logger log = Logger.getLogger(AbstractInsertionStrategy.class);
private Collection<InsertionListener> listener = new ArrayList<InsertionListener>();
public abstract RouteAlgorithm getRouteAlgorithm();
public void informJobInserted(int nOfJobs2Recreate, Job insertedJob, VehicleRoute insertedIn){
for(InsertionListener l : listener){
if(l instanceof JobInsertedListener){
((JobInsertedListener)l).informJobInserted(nOfJobs2Recreate, insertedJob, insertedIn);
}
}
}
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route){
for(InsertionListener l : listener){
if(l instanceof BeforeJobInsertionListener){
((BeforeJobInsertionListener)l).informBeforeJobInsertion(job, data, route);
}
}
}
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate){
for(InsertionListener l : listener){
if(l instanceof InsertionStartsListener){
((InsertionStartsListener)l).informInsertionStarts(vehicleRoutes,nOfJobs2Recreate);
}
}
}
public void informInsertionEndsListeners(Collection<VehicleRoute> vehicleRoutes) {
for(InsertionListener l : listener){
if(l instanceof InsertionEndsListener){
((InsertionEndsListener)l).informInsertionEnds(vehicleRoutes);
}
}
}
public Collection<InsertionListener> getListener() {
return Collections.unmodifiableCollection(listener);
}
public void addListener(InsertionListener l){
log.info("add insertion-listener " + l);
listener.add(l);
}
public void addAllListener(List<InsertionListener> list) {
for(InsertionListener l : list) addListener(l);
}
}

View file

@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Iterator;
import java.util.List;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.TourActivity;
import basics.route.Vehicle;
final class AuxilliaryCostCalculator {
private final VehicleRoutingTransportCosts routingCosts;
private final VehicleRoutingActivityCosts activityCosts;
public AuxilliaryCostCalculator(final VehicleRoutingTransportCosts routingCosts, final VehicleRoutingActivityCosts costFunction) {
super();
this.routingCosts = routingCosts;
this.activityCosts = costFunction;
}
/**
*
* @param path
* @param depTime
* @param driver
* @param vehicle
* @return
*/
public double costOfPath(final List<TourActivity> path, final double depTime, final Driver driver, final Vehicle vehicle){
if(path.isEmpty()){
return 0.0;
}
double cost = 0.0;
Iterator<TourActivity> actIter = path.iterator();
TourActivity prevAct = actIter.next();
double startCost = 0.0;
cost += startCost;
double departureTimePrevAct = depTime;
while(actIter.hasNext()){
TourActivity act = actIter.next();
double transportCost = routingCosts.getTransportCost(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
double transportTime = routingCosts.getTransportTime(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
cost += transportCost;
double actStartTime = departureTimePrevAct + transportTime;
double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
double actEndTime = earliestOperationStartTime + act.getOperationTime();
departureTimePrevAct = actEndTime;
cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
prevAct = act;
}
return cost;
}
public double costOfPath(String startLocationId, final double startTime, final List<TourActivity> path, String endLocationId, final Driver driver, final Vehicle vehicle){
if(path.isEmpty()){
return 0.0;
}
double cost = 0.0;
// Iterator<TourActivity> actIter = path.iterator();
String prevActLocation = startLocationId;
// TourActivity prevAct = actIter.next();
double startCost = 0.0;
cost += startCost;
double departureTimePrevAct = startTime;
for(TourActivity act : path){
// TourActivity act = actIter.next();
double transportCost = routingCosts.getTransportCost(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
double transportTime = routingCosts.getTransportTime(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
cost += transportCost;
double actStartTime = departureTimePrevAct + transportTime;
double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
double actEndTime = earliestOperationStartTime + act.getOperationTime();
departureTimePrevAct = actEndTime;
cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
prevActLocation = act.getLocationId();
}
/*
*!!! ENDLOCATION
=> Start u. End können primitiv sein.
*/
return cost;
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.algo.InsertionListener;
import basics.route.VehicleRoute;
interface BeforeJobInsertionListener extends InsertionListener{
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route);
}

View file

@ -0,0 +1,160 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.VehicleRoute;
/**
* Simplest recreation strategy. All removed customers are inserted where
* insertion costs are minimal. I.e. each tour-agent is asked for minimal
* marginal insertion costs. The tour-agent offering the lowest marginal
* insertion costs gets the customer/shipment.
*
* @author stefan schroeder
*
*/
final class BestInsertion extends AbstractInsertionStrategy{
public static BestInsertion newInstance(RouteAlgorithm routeAlgorithm){
return new BestInsertion(routeAlgorithm);
}
private static Logger logger = Logger.getLogger(BestInsertion.class);
private Random random = RandomNumberGeneration.getRandom();
private RouteAlgorithm routeAlgorithm;
private Map<String,VehicleRoute> experimentalPreferredRoute = new HashMap<String, VehicleRoute>();
public void setExperimentalPreferredRoute(Map<String, VehicleRoute> experimentalPreferredRoute) {
this.experimentalPreferredRoute = experimentalPreferredRoute;
}
private boolean allowUnassignedJobs = false;
private boolean fixRouteSet = false;
private boolean minVehiclesFirst = false;
public void setFixRouteSet(boolean fixRouteSet) {
this.fixRouteSet = fixRouteSet;
}
public void setRandom(Random random) {
this.random = random;
}
public BestInsertion(RouteAlgorithm routeAlgorithm) {
super();
this.routeAlgorithm = routeAlgorithm;
logger.info("initialise " + this);
}
public RouteAlgorithm getRouteAlgorithm(){
return routeAlgorithm;
}
@Override
public String toString() {
return "[name=bestInsertion]";
}
@Override
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
informInsertionStarts(vehicleRoutes,unassignedJobs.size());
int inserted = 0;
List<String> reasons = new ArrayList<String>();
for(Job unassignedJob : unassignedJobList){
VehicleRoute insertIn = null;
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
for(VehicleRoute vehicleRoute : vehicleRoutes){
InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
if(iData instanceof NoInsertionFound) {
continue;
}
if(iData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(vehicleRoute,iData);
bestInsertionCost = iData.getInsertionCost();
}
}
if(!minVehiclesFirst){
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData newIData = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(newIData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(newRoute,newIData);
bestInsertionCost = newIData.getInsertionCost();
vehicleRoutes.add(newRoute);
}
}
if(bestInsertion != null){
informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
insertIn = bestInsertion.getRoute();
// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
}
else {
if(fixRouteSet){
if(allowUnassignedJobs) logger.warn("cannot insert job yet " + unassignedJob);
else throw new IllegalStateException("given the vehicles, could not insert job\n");
}
else{
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound){
if(allowUnassignedJobs){
logger.warn("cannot insert job yet " + unassignedJob);
}
else {
for(String s : reasons){
System.out.println("reason="+s);
}
throw new IllegalStateException("given the vehicles, could not insert job\n" +
"\t" + unassignedJob + "\n\t");
}
}
else{
insertIn = newRoute;
informBeforeJobInsertion(unassignedJob,bestI,newRoute);
routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
vehicleRoutes.add(newRoute);
}
}
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
}
informInsertionEndsListeners(vehicleRoutes);
}
}

View file

@ -0,0 +1,191 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.VehicleRoute;
/**
* Simplest recreation strategy. All removed customers are inserted where
* insertion costs are minimal. I.e. each tour-agent is asked for minimal
* marginal insertion costs. The tour-agent offering the lowest marginal
* insertion costs gets the customer/shipment.
*
* @author stefan schroeder
*
*/
final class BestInsertionConcurrent extends AbstractInsertionStrategy{
static class Batch {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
}
private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class);
private Random random = RandomNumberGeneration.getRandom();
private RouteAlgorithm routeAlgorithm;
// private ExecutorService executor;
private int nuOfBatches;
private ExecutorCompletionService<Insertion> completionService;
public void setRandom(Random random) {
this.random = random;
}
public BestInsertionConcurrent(RouteAlgorithm routeAlgorithm, ExecutorService executor, int nuOfThreads) {
super();
this.routeAlgorithm = routeAlgorithm;
// this.executor = executor;
logger.info("initialise " + this);
this.nuOfBatches = nuOfThreads;
completionService = new ExecutorCompletionService<Insertion>(executor);
}
@Override
public String toString() {
return "[name=concurrentBestInsertion]";
}
@Override
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
List<Job> unassignedJobList = new ArrayList<Job>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
informInsertionStarts(vehicleRoutes,unassignedJobs.size());
int inserted = 0;
for(final Job unassignedJob : unassignedJobList){
VehicleRoute insertIn = null;
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
List<Batch> batches = distributeRoutes(vehicleRoutes,nuOfBatches);
for(final Batch batch : batches){
completionService.submit(new Callable<Insertion>() {
@Override
public Insertion call() throws Exception {
return getBestInsertion(batch,unassignedJob);
}
});
}
try{
for(int i=0;i<batches.size();i++){
Future<Insertion> futureIData = completionService.take();
Insertion insertion = futureIData.get();
if(insertion == null) continue;
if(insertion.getInsertionData().getInsertionCost() < bestInsertionCost){
bestInsertion = insertion;
bestInsertionCost = insertion.getInsertionData().getInsertionCost();
}
}
}
catch(InterruptedException e){
Thread.currentThread().interrupt();
}
catch (ExecutionException e) {
e.printStackTrace();
logger.error(e.getCause().toString());
System.exit(1);
}
if(bestInsertion != null){
informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
insertIn = bestInsertion.getRoute();
// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
}
else {
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound)
throw new IllegalStateException("given the vehicles, could not create a valid solution.\n\tthe reason might be" +
" inappropriate vehicle capacity.\n\tthe job that does not fit in any vehicle anymore is \n\t" + unassignedJob);
insertIn = newRoute;
informBeforeJobInsertion(unassignedJob,bestI,newRoute);
routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
vehicleRoutes.add(newRoute);
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
}
}
private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
for(VehicleRoute vehicleRoute : batch.routes){
InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
if(iData instanceof NoInsertionFound) continue;
if(iData.getInsertionCost() < bestInsertionCost){
bestInsertion = new Insertion(vehicleRoute,iData);
bestInsertionCost = iData.getInsertionCost();
}
}
return bestInsertion;
}
private List<Batch> distributeRoutes(Collection<VehicleRoute> vehicleRoutes, int nuOfBatches) {
List<Batch> batches = new ArrayList<Batch>();
for(int i=0;i<nuOfBatches;i++) batches.add(new Batch());
if(vehicleRoutes.size()<nuOfBatches){
int nOfNewRoutes = nuOfBatches-vehicleRoutes.size();
for(int i=0;i<nOfNewRoutes;i++){
vehicleRoutes.add(VehicleRoute.emptyRoute());
}
}
int count = 0;
for(VehicleRoute route : vehicleRoutes){
if(count == nuOfBatches) count=0;
batches.get(count).routes.add(route);
count++;
}
return batches;
}
@Override
public RouteAlgorithm getRouteAlgorithm() {
return routeAlgorithm;
}
}

View file

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.Start;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
final class CalculatesActivityInsertionWithHardTimeWindows {
private static Logger logger = Logger.getLogger(CalculatesActivityInsertionWithHardTimeWindows.class);
private RouteStates routeStates;
private VehicleRoutingTransportCosts routingCosts;
private VehicleRoutingActivityCosts activityCosts;
public CalculatesActivityInsertionWithHardTimeWindows(RouteStates activityStates, VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts){
this.routeStates = activityStates;
this.routingCosts = routingCosts;
this.activityCosts = activityCosts;
logger.info("initialise " + this);
}
public double calculate(VehicleRoute vehicleRoute, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, Driver driver, Vehicle vehicle) {
boolean prevIsStart = false;
if(prevAct instanceof Start){
prevIsStart = true;
}
double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double newAct_arrTime = prevAct.getEndTime() + tp_time_prevAct_newAct;
double newAct_operationStartTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime());
double newAct_endTime = newAct_operationStartTime + newAct.getOperationTime();
double act_costs_newAct = activityCosts.getActivityCost(newAct, newAct_arrTime, driver, vehicle);
double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, driver, vehicle);
double activityInsertionCosts;
double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct + act_costs_newAct + act_costs_nextAct;
if(nextAct_arrTime > getLatestOperationStart(nextAct)){
activityInsertionCosts = Double.MAX_VALUE;
}
else if(vehicleRoute.isEmpty()){
activityInsertionCosts = totalCosts;
}
else{
double oldCostOfPrevAct;
if(prevIsStart) oldCostOfPrevAct = 0.0;
else oldCostOfPrevAct = state(prevAct).getCurrentCost();
double oldCostOfNextAct;
if(nextAct instanceof End) oldCostOfNextAct = routeStates.getRouteState(vehicleRoute).getCosts();
else oldCostOfNextAct = state(nextAct).getCurrentCost();
activityInsertionCosts = (totalCosts) - (oldCostOfNextAct-oldCostOfPrevAct);
}
return activityInsertionCosts;
}
private ActivityState state(TourActivity act) {
return routeStates.getState(act);
}
private double getLatestOperationStart(TourActivity act) {
if(state(act) != null){
return state(act).getLatestOperationStart();
}
return act.getTheoreticalLatestOperationStartTime();
}
@Override
public String toString() {
return "[name=calculatesHardTimeWindowActivityInsertion]";
}
}

View file

@ -0,0 +1,209 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import util.Neighborhood;
import algorithms.RouteStates.ActivityState;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.ServiceActivity;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class CalculatesServiceInsertion implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertion.class);
private RouteStates routeStates;
private VehicleRoutingTransportCosts routingCosts;
private VehicleRoutingActivityCosts activityCosts;
private Start start;
private End end;
private Neighborhood neighborhood = new Neighborhood() {
@Override
public boolean areNeighbors(String location1, String location2) {
return true;
}
};
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
logger.info("initialise neighborhood " + neighborhood);
}
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
public CalculatesServiceInsertion(VehicleRoutingTransportCosts vehicleRoutingTransportCosts, VehicleRoutingActivityCosts vehicleRoutingActivityCosts) {
super();
this.routingCosts = vehicleRoutingTransportCosts;
this.activityCosts = vehicleRoutingActivityCosts;
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertion]";
}
/**
* Calculates the marginal cost of inserting job i locally. This is based on the
* assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1.
*
*/
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) {
if(jobToInsert == null) throw new IllegalStateException("jobToInsert is missing.");
if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("newVehicle is missing.");
TourActivities tour = currentRoute.getTourActivities();
double bestCost = bestKnownCosts;
Service service = (Service)jobToInsert;
if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
return InsertionData.noInsertionFound();
}
int insertionIndex = InsertionData.NO_INDEX;
TourActivity deliveryAct2Insert = ServiceActivity.newInstance(service);
// TourActivity deliveryAct2Insert = actStates.getActivity(service, true);
initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
TourActivity prevAct = start;
double prevCostInOriginalTour = 0.0;
int actIndex = 0;
for(TourActivity nextAct : tour.getActivities()){
double nextCostInOriginalTour = state(nextAct).getCurrentCost();
if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, nextCostInOriginalTour - prevCostInOriginalTour);
if(mc < bestCost){
bestCost = mc;
insertionIndex = actIndex;
}
}
prevCostInOriginalTour = nextCostInOriginalTour;
prevAct = nextAct;
actIndex++;
}
End nextAct = end;
if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, routeStates.getRouteState(currentRoute).getCosts() - prevCostInOriginalTour);
if(mc < bestCost){
bestCost = mc;
insertionIndex = actIndex;
}
}
if(insertionIndex == InsertionData.NO_INDEX) {
return InsertionData.noInsertionFound();
}
InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
private void initialiseStartAndEnd(final Vehicle newVehicle,
double newVehicleDepartureTime) {
if(start == null){
start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
else{
start.setLocationId(newVehicle.getLocationId());
start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
if(end == null){
end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
}
else{
end.setLocationId(newVehicle.getLocationId());
end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
}
}
public double calculate(TourActivities tour, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, Driver driver, Vehicle vehicle, double bestKnownCosts, double costWithoutNewJob) {
double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
double newAct_arrTime = prevAct.getEndTime() + tp_time_prevAct_newAct;
double newAct_operationStartTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime());
double newAct_endTime = newAct_operationStartTime + newAct.getOperationTime();
double act_costs_newAct = activityCosts.getActivityCost(newAct, newAct_arrTime, driver, vehicle);
if((tp_costs_prevAct_newAct + act_costs_newAct - costWithoutNewJob) > bestKnownCosts){
return Double.MAX_VALUE;
}
double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, driver, vehicle);
double activityInsertionCosts;
double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct + act_costs_newAct + act_costs_nextAct;
if(totalCosts - costWithoutNewJob > bestKnownCosts){
activityInsertionCosts = Double.MAX_VALUE;
}
if(nextAct_arrTime > getLatestOperationStart(nextAct)){
activityInsertionCosts = Double.MAX_VALUE;
}
else{
activityInsertionCosts = totalCosts - costWithoutNewJob;
}
return activityInsertionCosts;
}
private double getLatestOperationStart(TourActivity act) {
if(state(act) != null){
return state(act).getLatestOperationStart();
}
return act.getTheoreticalLatestOperationStartTime();
}
}

View file

@ -0,0 +1,116 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
import basics.route.VehicleRoute;
final class CalculatesServiceInsertionConsideringFixCost implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionConsideringFixCost.class);
private final JobInsertionCalculator standardServiceInsertion;
private double weight_deltaFixCost = 0.5;
private double solution_completeness_ratio = 0.5;
private RouteStates routeStates;
public CalculatesServiceInsertionConsideringFixCost(final JobInsertionCalculator standardInsertionCalculator, RouteStates routeStates) {
super();
this.standardServiceInsertion = standardInsertionCalculator;
this.routeStates = routeStates;
logger.info("inialise " + this);
}
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownPrice) {
double relFixCost = getDeltaRelativeFixCost(currentRoute, newVehicle, jobToInsert);
double absFixCost = getDeltaAbsoluteFixCost(currentRoute, newVehicle, jobToInsert);
double deltaFixCost = (1-solution_completeness_ratio)*relFixCost + solution_completeness_ratio*absFixCost;
double fixcost_contribution = weight_deltaFixCost*solution_completeness_ratio*deltaFixCost;
if(fixcost_contribution > bestKnownPrice){
return InsertionData.noInsertionFound();
}
InsertionData iData = standardServiceInsertion.calculate(currentRoute, jobToInsert, newVehicle, newVehicleDepartureTime, newDriver, bestKnownPrice);
if(iData instanceof NoInsertionFound){
return iData;
}
double totalInsertionCost = iData.getInsertionCost() + fixcost_contribution;
InsertionData insertionData = new InsertionData(totalInsertionCost, iData.getPickupInsertionIndex(), iData.getDeliveryInsertionIndex(), newVehicle, newDriver);
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
public void setWeightOfFixCost(double weight){
weight_deltaFixCost = weight;
logger.info("set weightOfFixCostSaving to " + weight);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionConsideringFixCost][weightOfFixedCostSavings="+weight_deltaFixCost+"]";
}
public void setSolutionCompletenessRatio(double ratio){
solution_completeness_ratio = ratio;
}
private double getDeltaAbsoluteFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
double load = routeStates.getRouteState(route).getLoad() + job.getCapacityDemand();
double currentFix = 0.0;
if(route.getVehicle() != null){
if(!(route.getVehicle() instanceof NoVehicle)){
currentFix += route.getVehicle().getType().vehicleCostParams.fix;
}
}
if(newVehicle.getCapacity() < load){
return Double.MAX_VALUE;
}
return newVehicle.getType().vehicleCostParams.fix - currentFix;
}
private double getDeltaRelativeFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
int currentLoad = routeStates.getRouteState(route).getLoad();
double load = currentLoad + job.getCapacityDemand();
double currentRelFix = 0.0;
if(route.getVehicle() != null){
if(!(route.getVehicle() instanceof NoVehicle)){
currentRelFix += route.getVehicle().getType().vehicleCostParams.fix*currentLoad/route.getVehicle().getCapacity();
}
}
if(newVehicle.getCapacity() < load){
return Double.MAX_VALUE;
}
double relativeFixCost = newVehicle.getType().vehicleCostParams.fix*(load/newVehicle.getCapacity()) - currentRelFix;
return relativeFixCost;
}
}

View file

@ -0,0 +1,356 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import org.apache.log4j.Logger;
import util.Neighborhood;
import algorithms.RouteStates.ActivityState;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class CalculatesServiceInsertionOnRouteLevel implements JobInsertionCalculator{
private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionOnRouteLevel.class);
private final VehicleRoutingTransportCosts transportCosts;
private final VehicleRoutingActivityCosts activityCosts;
private AuxilliaryCostCalculator auxilliaryPathCostCalculator;
private RouteStates routeStates;
private int nuOfActsForwardLooking = 0;
private int memorySize = 2;
private Start start;
private End end;
private Neighborhood neighborhood = new Neighborhood() {
@Override
public boolean areNeighbors(String location1, String location2) {
return true;
}
};
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
logger.info("initialise neighborhood " + neighborhood);
}
public void setMemorySize(int memorySize) {
this.memorySize = memorySize;
logger.info("set [solutionMemory="+memorySize+"]");
}
public CalculatesServiceInsertionOnRouteLevel(VehicleRoutingTransportCosts vehicleRoutingCosts, VehicleRoutingActivityCosts costFunc) {
super();
this.transportCosts = vehicleRoutingCosts;
this.activityCosts = costFunc;
auxilliaryPathCostCalculator = new AuxilliaryCostCalculator(transportCosts, activityCosts);
logger.info("initialise " + this);
}
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
void setNuOfActsForwardLooking(int nOfActsForwardLooking) {
this.nuOfActsForwardLooking = nOfActsForwardLooking;
logger.info("set [forwardLooking="+nOfActsForwardLooking+"]");
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionOnRouteLevel][solutionMemory="+memorySize+"][forwardLooking="+nuOfActsForwardLooking+"]";
}
/**
* Calculates the insertion costs of job i on route level (which is based on the assumption that inserting job i does not only
* have local effects but affects the entire route).
* Calculation is conducted by two steps. In the first step, promising insertion positions are identified by appromiximating their
* marginal insertion cost. In the second step, marginal cost of the best M positions are calculated exactly.
*
*
*/
@Override
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double best_known_insertion_costs) {
if(jobToInsert == null) throw new IllegalStateException("job is null. cannot calculate the insertion of a null-job.");
if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("no vehicle given. set para vehicle!");
/**
* map that memorizes the costs with newVehicle, which is a cost-snapshot at tour-activities.
*/
Map<TourActivity,Double> activity2costWithNewVehicle = new HashMap<TourActivity,Double>();
/**
* priority queue that stores insertion-data by insertion-costs in ascending order.
*/
PriorityQueue<InsertionData> bestInsertionsQueue = new PriorityQueue<InsertionData>(Math.max(2, currentRoute.getTourActivities().getActivities().size()), getComparator());
TourActivities tour = currentRoute.getTourActivities();
double best_insertion_costs = best_known_insertion_costs;
Service service = (Service)jobToInsert;
/**
* pre-check whether vehicle-capacity of new vehicle is sufficient to load service.
*/
if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
return InsertionData.noInsertionFound();
}
/**
* some inis
*/
TourActivity serviceAct2Insert = routeStates.getActivity(service, true);
int best_insertion_index = InsertionData.NO_INDEX;
initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
TourActivity prevAct = start;
int actIndex = 0;
double sumOf_prevCosts_newVehicle = 0.0;
double prevActDepTime_newVehicle = start.getEndTime();
/**
* inserting serviceAct2Insert in route r={0,1,...,i-1,i,j,j+1,...,n(r),n(r)+1}
* i=prevAct
* j=nextAct
* k=serviceAct2Insert
*/
for(TourActivity nextAct : tour.getActivities()){
if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
/**
* builds a path on this route forwardPath={i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking}
*/
List<TourActivity> path = new ArrayList<TourActivity>();
path.add(prevAct); path.add(serviceAct2Insert); path.add(nextAct);
if(nuOfActsForwardLooking > 0){ path.addAll(getForwardLookingPath(currentRoute,actIndex)); }
/**
* calculates the path costs with new vehicle, c(forwardPath,newVehicle).
*/
double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
/**
* insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
*/
double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
/**
* memorize it in insertion-queue
*/
if(insertion_cost_approximation < best_known_insertion_costs){
bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation, InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
}
}
/**
* calculate transport and activity costs with new vehicle (without inserting k)
*/
double transportCost_prevAct_nextAct_newVehicle = transportCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
double transportTime_prevAct_nextAct_newVehicle = transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
double arrTime_nextAct_newVehicle = prevActDepTime_newVehicle + transportTime_prevAct_nextAct_newVehicle;
double activityCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct_newVehicle, newDriver, newVehicle);
/**
* memorize transport and activity costs with new vehicle without inserting k
*/
sumOf_prevCosts_newVehicle += transportCost_prevAct_nextAct_newVehicle + activityCost_nextAct;
activity2costWithNewVehicle.put(nextAct, sumOf_prevCosts_newVehicle);
/**
* departure time at nextAct with new vehicle
*/
double depTime_nextAct_newVehicle = Math.max(arrTime_nextAct_newVehicle, nextAct.getTheoreticalEarliestOperationStartTime()) + nextAct.getOperationTime();
/**
* set previous to next
*/
prevAct = nextAct;
prevActDepTime_newVehicle = depTime_nextAct_newVehicle;
actIndex++;
}
End nextAct = end;
if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
/**
* calculates the path costs with new vehicle, c(forwardPath,newVehicle).
*/
List<TourActivity> path = Arrays.asList(prevAct,serviceAct2Insert,end);
double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
/**
* insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
*/
double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
/**
* memorize it in insertion-queue
*/
if(insertion_cost_approximation < best_known_insertion_costs){
bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation,InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
}
}
/**
* the above calculations approximate insertion costs. now calculate the exact insertion costs for the most promising (according to the approximation)
* insertion positions.
*
*/
for(int i=0;i<memorySize;i++){
InsertionData data = bestInsertionsQueue.poll();
if(data == null){
continue;
}
/**
* build tour with new activity.
*/
List<TourActivity> wholeTour = new ArrayList<TourActivity>();
wholeTour.add(start);
wholeTour.addAll(currentRoute.getTourActivities().getActivities());
wholeTour.add(end);
wholeTour.add(data.getDeliveryInsertionIndex()+1, serviceAct2Insert);
/**
* compute cost-diff of tour with and without new activity --> insertion_costs
*/
double insertion_costs = auxilliaryPathCostCalculator.costOfPath(wholeTour, start.getEndTime(), newDriver, newVehicle) - routeStates.getRouteState(currentRoute).getCosts();
/**
* if better than best known, make it the best known
*/
if(insertion_costs < best_insertion_costs){
best_insertion_index = data.getDeliveryInsertionIndex();
best_insertion_costs = insertion_costs;
}
}
if(best_insertion_index == InsertionData.NO_INDEX) return InsertionData.noInsertionFound();
return new InsertionData(best_insertion_costs, InsertionData.NO_INDEX, best_insertion_index, newVehicle, newDriver);
}
/**
* initialize start and end of tour.
*
* @param newVehicle
* @param newVehicleDepartureTime
*/
private void initialiseStartAndEnd(final Vehicle newVehicle, double newVehicleDepartureTime) {
if(start == null){
start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
else{
start.setLocationId(newVehicle.getLocationId());
start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
start.setEndTime(newVehicleDepartureTime);
}
if(end == null){
end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
}
else{
end.setLocationId(newVehicle.getLocationId());
end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
}
}
private double pathCost_oldVehicle(VehicleRoute vehicleRoute, List<TourActivity> path) {
TourActivity act = path.get(path.size()-1);
if(act instanceof End){
return routeStates.getRouteState(vehicleRoute).getCosts();
}
return state(act).getCurrentCost();
}
/**
* returns the path or the partial route r_partial = {j+1,j+2,...,j+nuOfActsForwardLooking}
*
* @param route
* @param actIndex
* @return
*/
private List<TourActivity> getForwardLookingPath(VehicleRoute route, int actIndex) {
List<TourActivity> forwardLookingPath = new ArrayList<TourActivity>();
int nuOfActsInPath = 0;
int index = actIndex + 1;
while(index < route.getTourActivities().getActivities().size() && nuOfActsInPath < nuOfActsForwardLooking){
forwardLookingPath.add(route.getTourActivities().getActivities().get(index));
index++;
nuOfActsInPath++;
}
if(nuOfActsInPath < nuOfActsForwardLooking){
forwardLookingPath.add(route.getEnd());
}
return forwardLookingPath;
}
/**
* creates a comparator to sort insertion-data in insertionQueue in ascending order according insertion costs.
* @return
*/
private Comparator<InsertionData> getComparator() {
return new Comparator<InsertionData>() {
@Override
public int compare(InsertionData o1, InsertionData o2) {
if(o1.getInsertionCost() < o2.getInsertionCost()){
return -1;
}
else {
return 1;
}
}
};
}
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Logger;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCalculator{
private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class);
private JobInsertionCalculator jic;
private Random random = new Random();
private int nOfDepartureTimes = 3;
private double timeSlice = 900.0;
public CalculatesServiceInsertionWithTimeScheduling(JobInsertionCalculator jic, double timeSlice, int neighbors) {
super();
this.jic = jic;
this.timeSlice = timeSlice;
this.nOfDepartureTimes = neighbors;
log.info("initialise " + this);
}
@Override
public String toString() {
return "[name=calculatesServiceInsertionWithTimeScheduling][timeSlice="+timeSlice+"][#timeSlice="+nOfDepartureTimes+"]";
}
@Override
public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore) {
List<Double> vehicleDepartureTimes = new ArrayList<Double>();
double currentStart;
if(currentRoute.getStart() == null){
currentStart = newVehicleDepartureTime;
}
else currentStart = currentRoute.getStart().getEndTime();
vehicleDepartureTimes.add(currentStart);
double earliestDeparture = newVehicle.getEarliestDeparture();
double latestEnd = newVehicle.getLatestArrival();
for(int i=0;i<nOfDepartureTimes;i++){
double neighborStartTime_earlier = currentStart - (i+1)*timeSlice;
if(neighborStartTime_earlier > earliestDeparture) vehicleDepartureTimes.add(neighborStartTime_earlier);
double neighborStartTime_later = currentStart + (i+1)*timeSlice;
if(neighborStartTime_later < latestEnd) vehicleDepartureTimes.add(neighborStartTime_later);
}
InsertionData bestIData = null;
for(Double departureTime : vehicleDepartureTimes){
InsertionData iData = jic.calculate(currentRoute, jobToInsert, newVehicle, departureTime, newDriver, bestKnownScore);
if(bestIData == null) bestIData = iData;
else if(iData.getInsertionCost() < bestIData.getInsertionCost()){
iData.setVehicleDepartureTime(departureTime);
bestIData = iData;
}
}
// log.info(bestIData);
return bestIData;
}
}

View file

@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import algorithms.VehicleFleetManager.TypeKey;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
import basics.route.VehicleImpl.VehicleType;
import basics.route.VehicleRoute;
final class CalculatesVehTypeDepServiceInsertion implements JobInsertionCalculator{
private Logger logger = Logger.getLogger(CalculatesVehTypeDepServiceInsertion.class);
private final VehicleFleetManager fleetManager;
private final JobInsertionCalculator insertionCalculator;
public CalculatesVehTypeDepServiceInsertion(final VehicleFleetManager fleetManager, final JobInsertionCalculator jobInsertionCalc) {
this.fleetManager = fleetManager;
this.insertionCalculator = jobInsertionCalc;
logger.info("inialise " + this);
}
@Override
public String toString() {
return "[name=vehicleTypeDependentServiceInsertion]";
}
public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle vehicle, double newVehicleDepartureTime, final Driver driver, final double bestKnownCost) {
Vehicle selectedVehicle = currentRoute.getVehicle();
Driver selectedDriver = currentRoute.getDriver();
InsertionData bestIData = InsertionData.noInsertionFound();
double bestKnownCost_ = bestKnownCost;
Collection<Vehicle> relevantVehicles = new ArrayList<Vehicle>();
if(!(selectedVehicle instanceof NoVehicle)) relevantVehicles.add(selectedVehicle);
for(TypeKey typeKey : fleetManager.getAvailableVehicleTypes()){
if(!(currentRoute.getVehicle() instanceof NoVehicle)){
TypeKey key = makeTypeKey(currentRoute.getVehicle().getType(),currentRoute.getVehicle().getLocationId());
if(typeKey.equals(key)){
continue;
}
}
relevantVehicles.add(fleetManager.getEmptyVehicle(typeKey));
}
for(Vehicle v : relevantVehicles){
double depTime = v.getEarliestDeparture();
InsertionData iData = insertionCalculator.calculate(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_);
if(iData instanceof NoInsertionFound) {
if(bestIData instanceof NoInsertionFound) bestIData = iData;
continue;
}
if(iData.getInsertionCost() < bestKnownCost_){
bestIData = iData;
bestKnownCost_ = iData.getInsertionCost();
}
}
return bestIData;
}
private TypeKey makeTypeKey(VehicleType type, String locationId) {
return new TypeKey(type,locationId);
}
}

View file

@ -0,0 +1,268 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.configuration.XMLConfiguration;
import util.NeighborhoodImpl;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.algo.InsertionListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
import basics.costs.VehicleRoutingActivityCosts;
class CalculatorBuilder {
private static class CalculatorPlusListeners {
private JobInsertionCalculator calculator;
public JobInsertionCalculator getCalculator() {
return calculator;
}
private List<PrioritizedVRAListener> algorithmListener = new ArrayList<PrioritizedVRAListener>();
private List<InsertionListener> insertionListener = new ArrayList<InsertionListener>();
public CalculatorPlusListeners(JobInsertionCalculator calculator) {
super();
this.calculator = calculator;
}
public List<PrioritizedVRAListener> getAlgorithmListener() {
return algorithmListener;
}
public List<InsertionListener> getInsertionListener() {
return insertionListener;
}
}
private List<InsertionListener> insertionListeners;
private List<PrioritizedVRAListener> algorithmListeners;
private VehicleRoutingProblem vrp;
private RouteStates activityStates;
private boolean local = true;
private int forwardLooking = 0;
private int memory = 1;
private boolean considerFixedCost = false;
private double weightOfFixedCost = 0;
private VehicleFleetManager fleetManager;
private boolean timeScheduling;
private double timeSlice;
private int neighbors;
/**
* Constructs the builder.
*
* <p>Some calculators require information from the overall algorithm or the higher-level insertion procedure. Thus listeners inform them.
* These listeners are cached in the according list and can thus be added when its time to add them.
*
* @param insertionListeners
* @param algorithmListeners
*/
public CalculatorBuilder(List<InsertionListener> insertionListeners, List<PrioritizedVRAListener> algorithmListeners) {
super();
this.insertionListeners = insertionListeners;
this.algorithmListeners = algorithmListeners;
}
/**
* Sets activityStates. MUST be set.
*
* @param activityStates
* @return
*/
public CalculatorBuilder setActivityStates(RouteStates activityStates){
this.activityStates = activityStates;
return this;
}
/**
* Sets routingProblem. MUST be set.
*
* @param vehicleRoutingProblem
* @return
*/
public CalculatorBuilder setVehicleRoutingProblem(VehicleRoutingProblem vehicleRoutingProblem){
this.vrp = vehicleRoutingProblem;
return this;
}
/**
* Sets fleetManager. MUST be set.
*
* @param fleetManager
* @return
*/
public CalculatorBuilder setVehicleFleetManager(VehicleFleetManager fleetManager){
this.fleetManager = fleetManager;
return this;
}
/**
* Sets a flag to build a calculator based on local calculations.
*
* <p>Insertion of a job and job-activity is evaluated based on the previous and next activity.
*/
public void setLocalLevel(){
local = true;
}
/**
* Sets a flag to build a calculator that evaluates job insertion on route-level.
*
* @param forwardLooking
* @param memory
*/
public void setRouteLevel(int forwardLooking, int memory){
local = false;
this.forwardLooking = forwardLooking;
this.memory = memory;
}
/**
* Sets a flag to consider also fixed-cost when evaluating the insertion of a job. The weight of the fixed-cost can be determined by setting
* weightofFixedCosts.
*
* @param weightOfFixedCosts
*/
public void considerFixedCosts(double weightOfFixedCosts){
considerFixedCost = true;
this.weightOfFixedCost = weightOfFixedCosts;
}
public void experimentalTimeScheduler(double timeSlice, int neighbors){
timeScheduling = true;
this.timeSlice = timeSlice;
this.neighbors = neighbors;
}
/**
* Builds the jobInsertionCalculator.
*
* @return jobInsertionCalculator.
* @throws IllegalStateException if vrp == null or activityStates == null or fleetManager == null.
*/
public JobInsertionCalculator build(){
if(vrp == null) throw new IllegalStateException("vehicle-routing-problem is null, but it must be set (this.setVehicleRoutingProblem(vrp))");
if(activityStates == null) throw new IllegalStateException("activity states is null, but is must be set (this.setActivityStates(states))");
if(fleetManager == null) throw new IllegalStateException("fleetManager is null, but it must be set (this.setVehicleFleetManager(fleetManager))");
JobInsertionCalculator baseCalculator = null;
CalculatorPlusListeners standardLocal = null;
if(local){
standardLocal = createStandardLocal(vrp, activityStates);
}
else{
standardLocal = createStandardRoute(vrp, activityStates,forwardLooking,memory);
}
baseCalculator = standardLocal.getCalculator();
addAlgorithmListeners(standardLocal.getAlgorithmListener());
addInsertionListeners(standardLocal.getInsertionListener());
if(considerFixedCost){
CalculatorPlusListeners withFixed = createCalculatorConsideringFixedCosts(vrp, baseCalculator, activityStates, weightOfFixedCost);
baseCalculator = withFixed.getCalculator();
addAlgorithmListeners(withFixed.getAlgorithmListener());
addInsertionListeners(withFixed.getInsertionListener());
}
if(timeScheduling){
baseCalculator = new CalculatesServiceInsertionWithTimeScheduling(baseCalculator,timeSlice,neighbors);
}
return createFinalInsertion(fleetManager, baseCalculator, activityStates);
}
private void addInsertionListeners(List<InsertionListener> list) {
for(InsertionListener iL : list){
insertionListeners.add(iL);
}
}
private void addAlgorithmListeners(List<PrioritizedVRAListener> list) {
for(PrioritizedVRAListener aL : list){
algorithmListeners.add(aL);
}
}
private CalculatorPlusListeners createStandardLocal(VehicleRoutingProblem vrp, RouteStates activityStates){
JobInsertionCalculator standardServiceInsertion = new CalculatesServiceInsertion(vrp.getTransportCosts(), vrp.getActivityCosts());
((CalculatesServiceInsertion) standardServiceInsertion).setActivityStates(activityStates);
((CalculatesServiceInsertion) standardServiceInsertion).setNeighborhood(vrp.getNeighborhood());
CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(standardServiceInsertion);
return calcPlusListeners;
}
private CalculatorPlusListeners createCalculatorConsideringFixedCosts(VehicleRoutingProblem vrp, JobInsertionCalculator baseCalculator, RouteStates activityStates, double weightOfFixedCosts){
final CalculatesServiceInsertionConsideringFixCost withFixCost = new CalculatesServiceInsertionConsideringFixCost(baseCalculator, activityStates);
withFixCost.setWeightOfFixCost(weightOfFixedCosts);
CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(withFixCost);
calcPlusListeners.getInsertionListener().add(new ConfigureFixCostCalculator(vrp, withFixCost));
return calcPlusListeners;
}
private CalculatorPlusListeners createStandardRoute(VehicleRoutingProblem vrp, RouteStates activityStates, int forwardLooking, int solutionMemory){
int after = forwardLooking;
JobInsertionCalculator jobInsertionCalculator = new CalculatesServiceInsertionOnRouteLevel(vrp.getTransportCosts(), vrp.getActivityCosts());
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNuOfActsForwardLooking(after);
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setMemorySize(solutionMemory);
((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNeighborhood(vrp.getNeighborhood());
((CalculatesServiceInsertionOnRouteLevel) jobInsertionCalculator).setActivityStates(activityStates);
CalculatorPlusListeners calcPlusListener = new CalculatorPlusListeners(jobInsertionCalculator);
return calcPlusListener;
}
private JobInsertionCalculator createFinalInsertion(VehicleFleetManager fleetManager, JobInsertionCalculator baseCalc, RouteStates routeStates){
return new CalculatesVehTypeDepServiceInsertion(fleetManager, baseCalc);
}
}

View file

@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.algo.InsertionStartsListener;
import basics.algo.JobInsertedListener;
import basics.route.VehicleRoute;
final class ConfigureFixCostCalculator implements InsertionStartsListener, JobInsertedListener{
private static Logger log = Logger.getLogger(ConfigureFixCostCalculator.class);
VehicleRoutingProblem vrp;
CalculatesServiceInsertionConsideringFixCost calcConsideringFix;
public ConfigureFixCostCalculator(VehicleRoutingProblem vrp, CalculatesServiceInsertionConsideringFixCost calcConsideringFix) {
super();
this.vrp = vrp;
this.calcConsideringFix = calcConsideringFix;
}
@Override
public String toString() {
return "[name=configureFixCostCalculator]";
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> routes, int nOfJobs2Recreate) {
double completenessRatio = (1-((double)nOfJobs2Recreate/(double)vrp.getJobs().values().size()));
calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
// log.debug("initialise completenessRatio to " + completenessRatio);
}
@Override
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
double completenessRatio = (1-((double)nOfJobsStill2Recreate/(double)vrp.getJobs().values().size()));
calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
// log.debug("set completenessRatio to " + completenessRatio);
}
}

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
/* *********************************************************************** *
* project: org.matsim.*
* IniSolution.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2011 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */
package algorithms;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.route.DriverImpl;
import basics.route.TourActivities;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
final class CreateInitialSolution implements InitialSolutionFactory {
private static final Logger logger = Logger.getLogger(CreateInitialSolution.class);
private final AbstractInsertionStrategy insertion;
private boolean generateAsMuchAsRoutesAsVehiclesExist = false;
public void setGenerateAsMuchAsRoutesAsVehiclesExist(boolean generateAsMuchAsRoutesAsVehiclesExist) {
this.generateAsMuchAsRoutesAsVehiclesExist = generateAsMuchAsRoutesAsVehiclesExist;
}
public CreateInitialSolution(AbstractInsertionStrategy insertion) {
super();
this.insertion = insertion;
}
@Override
public VehicleRoutingProblemSolution createInitialSolution(final VehicleRoutingProblem vrp) {
logger.info("create initial solution.");
List<VehicleRoute> vehicleRoutes = new ArrayList<VehicleRoute>();
if(generateAsMuchAsRoutesAsVehiclesExist){
for(Vehicle vehicle : vrp.getVehicles()){
vehicleRoutes.add(VehicleRoute.newInstance(TourActivities.emptyTour(), DriverImpl.noDriver(), vehicle));
}
}
insertion.run(vehicleRoutes, getUnassignedJobs(vrp), Double.MAX_VALUE);
double totalCost = getTotalCost(vehicleRoutes);
logger.info("creation done");
return new VehicleRoutingProblemSolution(vehicleRoutes, totalCost);
}
private double getTotalCost(List<VehicleRoute> serviceProviders) {
double c = 0.0;
for(VehicleRoute a : serviceProviders){
c += a.getCost();
}
return c;
}
private List<Job> getUnassignedJobs(VehicleRoutingProblem vrp) {
List<Job> jobs = new ArrayList<Job>(vrp.getJobs().values());
return jobs;
}
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class FindCheaperVehicle implements InsertionStartsListener{
FindCheaperVehicleAlgo findCheaperVehicle;
public FindCheaperVehicle(FindCheaperVehicleAlgo findCheaperVehicle) {
super();
this.findCheaperVehicle = findCheaperVehicle;
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
List<VehicleRoute> newRoutes = new ArrayList<VehicleRoute>();
for(VehicleRoute route : vehicleRoutes){
if(route.isEmpty()) continue;
VehicleRoute cheaperRoute = findCheaperVehicle.runAndGetVehicleRoute(route);
newRoutes.add(cheaperRoute);
}
vehicleRoutes.clear();
vehicleRoutes.addAll(newRoutes);
}
@Override
public String toString() {
return "[name=findCheaperVehicle]";
}
}

View file

@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import algorithms.VehicleFleetManager.TypeKey;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
import basics.route.VehicleImpl.NoVehicle;
final class FindCheaperVehicleAlgo {
private static Logger log = Logger.getLogger(FindCheaperVehicleAlgo.class);
private VehicleFleetManager fleetManager;
private VehicleRouteUpdater tourStateCalculator;
private AuxilliaryCostCalculator auxilliaryCostCalculator;
private double weightFixCosts = 1.0;
private RouteStates states;
public void setWeightFixCosts(double weightFixCosts) {
this.weightFixCosts = weightFixCosts;
}
public void setStates(RouteStates states) {
this.states = states;
}
public FindCheaperVehicleAlgo(VehicleFleetManager fleetManager, VehicleRouteUpdater tourStateCalculator, AuxilliaryCostCalculator auxilliaryCostCalculator) {
super();
this.fleetManager = fleetManager;
this.tourStateCalculator = tourStateCalculator;
this.auxilliaryCostCalculator = auxilliaryCostCalculator;
}
public VehicleRoute runAndGetVehicleRoute(VehicleRoute vehicleRoute) {
if(vehicleRoute.getVehicle() instanceof NoVehicle){
return vehicleRoute;
}
if(vehicleRoute.getTourActivities() == null || vehicleRoute.getVehicle() == null){
return vehicleRoute;
}
Collection<TypeKey> availableVehicleTypes = fleetManager.getAvailableVehicleTypes(new TypeKey(vehicleRoute.getVehicle().getType(),vehicleRoute.getVehicle().getLocationId()));
double bestSaving = 0.0;
Vehicle bestVehicle = null;
List<TourActivity> path = new ArrayList<TourActivity>();
path.add(vehicleRoute.getStart());
path.addAll(vehicleRoute.getTourActivities().getActivities());
path.add(vehicleRoute.getEnd());
for(TypeKey vehicleType : availableVehicleTypes){
Vehicle vehicle = fleetManager.getEmptyVehicle(vehicleType);
if(vehicle.getType().typeId.equals(vehicleRoute.getVehicle().getType().typeId)){
continue;
}
if(states.getRouteState(vehicleRoute).getLoad() <= vehicle.getCapacity()){
double fixCostSaving = vehicleRoute.getVehicle().getType().vehicleCostParams.fix - vehicle.getType().vehicleCostParams.fix;
double departureTime = vehicleRoute.getStart().getEndTime();
double newCost = auxilliaryCostCalculator.costOfPath(path, departureTime, vehicleRoute.getDriver(), vehicle);
double varCostSaving = states.getRouteState(vehicleRoute).getCosts() - newCost;
double totalCostSaving = varCostSaving + weightFixCosts*fixCostSaving;
if(totalCostSaving > bestSaving){
bestSaving = totalCostSaving;
bestVehicle = vehicle;
}
}
}
if(bestVehicle != null){
try{
fleetManager.unlock(vehicleRoute.getVehicle());
fleetManager.lock(bestVehicle);
}
catch(IllegalStateException e){
throw new IllegalStateException(e);
}
TourActivities newTour = TourActivities.copyOf(vehicleRoute.getTourActivities());
tourStateCalculator.updateRoute(vehicleRoute);
return VehicleRoute.newInstance(newTour,vehicleRoute.getDriver(),bestVehicle);
}
return vehicleRoute;
}
}

View file

@ -0,0 +1,207 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.SearchStrategyModule;
import basics.algo.SearchStrategyModuleListener;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
import basics.route.TourActivity.JobActivity;
import util.RandomNumberGeneration;
final class GendreauPostOpt implements SearchStrategyModule{
private final static Logger log = Logger.getLogger(GendreauPostOpt.class);
private final static String NAME = "gendreauPostOpt";
private final RuinStrategy ruin;
private final VehicleRoutingProblem vrp;
private final AbstractInsertionStrategy insertionStrategy;
private final RouteAlgorithm routeAlgorithm;
private VehicleFleetManager fleetManager;
private Random random = RandomNumberGeneration.getRandom();
private int nOfIterations = 10;
private double shareOfJobsToRuin = 0.15;
public void setShareOfJobsToRuin(double shareOfJobsToRuin) {
this.shareOfJobsToRuin = shareOfJobsToRuin;
}
public GendreauPostOpt(VehicleRoutingProblem vrp, RuinStrategy ruin, AbstractInsertionStrategy insertionStrategy) {
super();
this.routeAlgorithm = insertionStrategy.getRouteAlgorithm();
this.ruin = ruin;
this.vrp = vrp;
this.insertionStrategy = insertionStrategy;
}
@Override
public String toString() {
return "[name=gendreauPostOpt][iterations="+nOfIterations+"][share2ruin="+shareOfJobsToRuin+"]";
}
public void setRandom(Random random) {
this.random = random;
}
public void setNuOfIterations(int nOfIterations) {
this.nOfIterations = nOfIterations;
}
public void setFleetManager(VehicleFleetManager vehicleFleetManager) {
this.fleetManager = vehicleFleetManager;
}
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
// log.info("run gendreau postopt");
VehicleRoutingProblemSolution bestSolution = vrpSolution;
int itersWithoutImprovement = 0;
for(int i=0;i<nOfIterations;i++){
List<VehicleRoute> copiedRoutes = copyRoutes(bestSolution.getRoutes());
iniFleet(copiedRoutes);
VehicleRoute route2split = pickRouteThatHasAtLeastTwoJobs(copiedRoutes);
if(route2split == null) continue;
List<Job> jobsInRoute = getJobs(route2split);
Set<Job> unassignedJobs = new HashSet<Job>();
unassignedJobs.addAll(jobsInRoute);
copiedRoutes.remove(route2split);
Collections.shuffle(jobsInRoute,random);
Job targetJob = jobsInRoute.get(0);
int nOfJobs2BeRemovedAdditionally = (int) (shareOfJobsToRuin*(double)vrp.getJobs().size());
Collection<Job> unassignedJobsList = ruin.ruin(copiedRoutes, targetJob, nOfJobs2BeRemovedAdditionally);
unassignedJobs.addAll(unassignedJobsList);
VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute();
copiedRoutes.add(emptyRoute1);
routeAlgorithm.insertJob(targetJob, routeAlgorithm.calculateBestInsertion(emptyRoute1, targetJob, Double.MAX_VALUE), emptyRoute1);
unassignedJobs.remove(targetJob);
VehicleRoute emptyRoute2 = VehicleRoute.emptyRoute();
copiedRoutes.add(emptyRoute2);
Job job2 = jobsInRoute.get(1);
routeAlgorithm.insertJob(job2, routeAlgorithm.calculateBestInsertion(emptyRoute2, job2, Double.MAX_VALUE), emptyRoute2);
unassignedJobs.remove(job2);
insertionStrategy.run(copiedRoutes, unassignedJobs, Double.MAX_VALUE);
double cost = getCost(copiedRoutes);
if(cost < bestSolution.getCost()){
// log.info("BING - new: " + cost + " old: " + bestSolution.getCost());
bestSolution = new VehicleRoutingProblemSolution(copiedRoutes, cost);
itersWithoutImprovement=0;
}
else{
itersWithoutImprovement++;
if(itersWithoutImprovement > 200){
// log.info("BREAK i="+i);
break;
}
}
}
return bestSolution;
}
private List<VehicleRoute> copyRoutes(Collection<VehicleRoute> routes) {
List<VehicleRoute> routeList = new ArrayList<VehicleRoute>();
for(VehicleRoute r : routes){
routeList.add(VehicleRoute.copyOf(r));
}
return routeList;
}
private void iniFleet(Collection<VehicleRoute> routes) {
fleetManager.unlockAll();
for(VehicleRoute route : routes){
if(!route.isEmpty()){
fleetManager.lock(route.getVehicle());
}
}
}
private double getCost(Collection<VehicleRoute> routes) {
double c = 0.0;
for(VehicleRoute r : routes){
c+=r.getCost();
}
return c;
}
private List<Job> getJobs(VehicleRoute route2split) {
Set<Job> jobs = new HashSet<Job>();
for(TourActivity act : route2split.getTourActivities().getActivities()){
if(act instanceof JobActivity){
jobs.add(((JobActivity) act).getJob());
}
}
return new ArrayList<Job>(jobs);
}
private VehicleRoute pickRouteThatHasAtLeastTwoJobs(Collection<VehicleRoute> routeList) {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>();
for(VehicleRoute r : routeList){
if(getJobs(r).size() > 1){
routes.add(r);
}
}
if(routes.isEmpty()) return null;
Collections.shuffle(routes,random);
return routes.get(0);
}
@Override
public String getName() {
return NAME;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.route.Vehicle;
import basics.route.VehicleImpl.VehicleType;
class InfiniteVehicles implements VehicleFleetManager{
static class TypeKeyComparator implements Comparator<TypeKey>{
@Override
public int compare(TypeKey k1, TypeKey k2) {
double k1_fix = k1.type.getVehicleCostParams().fix;
double k2_fix = k2.type.getVehicleCostParams().fix;
return (int)(k1_fix - k2_fix);
}
}
private static Logger logger = Logger.getLogger(InfiniteVehicles.class);
private Map<TypeKey,Vehicle> types = new HashMap<TypeKey, Vehicle>();
private List<TypeKey> sortedTypes = new ArrayList<VehicleFleetManager.TypeKey>();
public InfiniteVehicles(Collection<Vehicle> vehicles){
extractTypes(vehicles);
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=infiniteVehicle]";
}
private void extractTypes(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
types.put(typeKey,v);
sortedTypes.add(typeKey);
}
Collections.sort(sortedTypes, new TypeKeyComparator());
}
@Override
public Vehicle getEmptyVehicle(TypeKey typeId) {
return types.get(typeId);
}
@Override
public Collection<TypeKey> getAvailableVehicleTypes() {
return sortedTypes;
}
@Override
public void lock(Vehicle vehicle) {
}
@Override
public void unlock(Vehicle vehicle) {
}
@Override
public Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType) {
Set<TypeKey> typeSet = new HashSet<TypeKey>(types.keySet());
typeSet.remove(withoutThisType);
return typeSet;
}
@Override
public boolean isLocked(Vehicle vehicle) {
return false;
}
@Override
public void unlockAll() {
}
}

View file

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
interface InitialSolutionFactory {
public VehicleRoutingProblemSolution createInitialSolution(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.Driver;
import basics.route.Vehicle;
class InsertionData {
static class NoInsertionFound extends InsertionData{
public NoInsertionFound() {
super(Double.MAX_VALUE, NO_INDEX, NO_INDEX, null, null);
}
}
private static InsertionData noInsertion = new NoInsertionFound();
public static InsertionData noInsertionFound(){
return noInsertion;
}
static int NO_INDEX = -1;
private final double insertionCost;
private final int pickupInsertionIndex;
private final int deliveryInsertionIndex;
private final Vehicle selectedVehicle;
private final Driver selectedDriver;
private double departureTime;
public InsertionData(double insertionCost, int pickupInsertionIndex, int deliveryInsertionIndex, Vehicle vehicle, Driver driver){
this.insertionCost = insertionCost;
this.pickupInsertionIndex = pickupInsertionIndex;
this.deliveryInsertionIndex = deliveryInsertionIndex;
this.selectedVehicle = vehicle;
this.selectedDriver = driver;
}
@Override
public String toString() {
return "[iCost="+insertionCost+"][iIndex="+deliveryInsertionIndex+"][depTime="+departureTime+"][vehicle="+selectedVehicle+"][driver="+selectedDriver+"]";
}
public int getDeliveryInsertionIndex(){
return deliveryInsertionIndex;
}
public int getPickupInsertionIndex(){
return pickupInsertionIndex;
}
public double getInsertionCost() {
return insertionCost;
}
public Vehicle getSelectedVehicle() {
return selectedVehicle;
}
public Driver getSelectedDriver(){
return selectedDriver;
}
/**
* @return the departureTime
*/
public double getVehicleDepartureTime() {
return departureTime;
}
/**
* @param departureTime the departureTime to set
*/
public void setVehicleDepartureTime(double departureTime) {
this.departureTime = departureTime;
}
}

View file

@ -0,0 +1,132 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.algo.InsertionListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
class InsertionFactory {
private static Logger log = Logger.getLogger(InsertionFactory.class);
public static AbstractInsertionStrategy createInsertion(VehicleRoutingProblem vrp, HierarchicalConfiguration config,
VehicleFleetManager vehicleFleetManager, RouteStates activityStates, List<PrioritizedVRAListener> algorithmListeners){
if(config.containsKey("[@name]")){
String insertionName = config.getString("[@name]");
if(!insertionName.equals("bestInsertion") && !insertionName.equals("regretInsertion")){
new IllegalStateException(insertionName + " is not supported. use either \"bestInsertion\" or \"regretInsertion\"");
}
AbstractInsertionStrategy insertionStrategy = null;
List<InsertionListener> insertionListeners = new ArrayList<InsertionListener>();
List<PrioritizedVRAListener> algoListeners = new ArrayList<PrioritizedVRAListener>();
CalculatorBuilder calcBuilder = new CalculatorBuilder(insertionListeners, algorithmListeners);
calcBuilder.setActivityStates(activityStates);
calcBuilder.setVehicleRoutingProblem(vrp);
calcBuilder.setVehicleFleetManager(vehicleFleetManager);
if(config.containsKey("level")){
String level = config.getString("level");
if(level.equals("local")){
calcBuilder.setLocalLevel();
}
else if(level.equals("route")){
int forwardLooking = 0;
int memory = 1;
String forward = config.getString("level[@forwardLooking]");
String mem = config.getString("level[@memory]");
if(forward != null) forwardLooking = Integer.parseInt(forward);
else log.warn("parameter route[@forwardLooking] is missing. by default it is 0 which equals to local level");
if(mem != null) memory = Integer.parseInt(mem);
else log.warn("parameter route[@memory] is missing. by default it is 1");
calcBuilder.setRouteLevel(forwardLooking, memory);
}
else throw new IllegalStateException("level " + level + " is not known. currently it only knows \"local\" or \"route\"");
}
else calcBuilder.setLocalLevel();
if(config.containsKey("considerFixedCosts") || config.containsKey("considerFixedCost")){
String val = config.getString("considerFixedCosts");
if(val == null) val = config.getString("considerFixedCost");
if(val.equals("true")){
double fixedCostWeight = 0.5;
String weight = config.getString("considerFixedCosts[@weight]");
if(weight == null) weight = config.getString("considerFixedCost[@weight]");
if(weight != null) fixedCostWeight = Double.parseDouble(weight);
else log.warn("parameter considerFixedCosts[@weight] is missing. by default, it is 0.5.");
calcBuilder.considerFixedCosts(fixedCostWeight);
}
}
String timeSliceString = config.getString("experimental[@timeSlice]");
String neighbors = config.getString("experimental[@neighboringSlices]");
if(timeSliceString != null && neighbors != null){
calcBuilder.experimentalTimeScheduler(Double.parseDouble(timeSliceString),Integer.parseInt(neighbors));
}
JobInsertionCalculator jic = calcBuilder.build();
TourStateUpdater tourStateCalculator = new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts());
RouteAlgorithm routeAlgorithm = RouteAlgorithmImpl.newInstance(jic, tourStateCalculator);
routeAlgorithm.getListeners().add(new VehicleSwitched(vehicleFleetManager));
((RouteAlgorithmImpl) routeAlgorithm).setActivityStates(activityStates);
if(insertionName.equals("bestInsertion")){
insertionStrategy = BestInsertion.newInstance(routeAlgorithm);
}
else if(insertionName.equals("regretInsertion")){
insertionStrategy = RegretInsertion.newInstance(routeAlgorithm);
}
// else if(insertionName.equals("concurrentBestInsertion")){
// String processorsString = config.getString("[@processors]");
// int processors = 1;
// if(processorsString != null) processors = Integer.parseInt(processorsString);
//// BestInsertionConcurrent.newInstance(routeAlgorithm,)
//
//
// }
insertionStrategy.addListener(new RemoveEmptyVehicles());
insertionStrategy.addListener(new ResetAndIniFleetManager(vehicleFleetManager));
insertionStrategy.addAllListener(insertionListeners);
// insertionStrategy.addListener(new FindCheaperVehicle(
// new FindCheaperVehicleAlgoNew(vehicleFleetManager, tourStateCalculator, auxCalculator)));
algorithmListeners.addAll(algoListeners);
return insertionStrategy;
}
else throw new IllegalStateException("cannot create insertionStrategy, since it has no name.");
}
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.Job;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
interface InsertionStrategy {
class Insertion {
private final VehicleRoute route;
private final InsertionData insertionData;
public Insertion(VehicleRoute vehicleRoute, InsertionData insertionData) {
super();
this.route = vehicleRoute;
this.insertionData = insertionData;
}
public VehicleRoute getRoute() {
return route;
}
public InsertionData getInsertionData() {
return insertionData;
}
}
/**
* Assigns the unassigned jobs to service-providers
*
* @param vehicleRoutes
* @param unassignedJobs
* @param result2beat
*/
public void run(Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat);
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
interface InsertionStrategyFactory {
public InsertionStrategy createStrategy(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
interface JobDistance {
public double calculateDistance(Job i, Job j);
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.Service;
import basics.costs.VehicleRoutingTransportCosts;
class JobDistanceAvgCosts implements JobDistance {
private VehicleRoutingTransportCosts costs;
public JobDistanceAvgCosts(VehicleRoutingTransportCosts costs) {
super();
this.costs = costs;
}
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
}
} else {
throw new UnsupportedOperationException(
"currently, this class just works with shipments and services.");
}
return avgCost;
}
private double calcDist(String from, String to) {
return costs.getTransportCost(from, to, 0.0, null, null);
}
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import util.CrowFlyCosts;
import util.Locations;
import basics.Job;
import basics.Service;
class JobDistanceBeeline implements JobDistance {
private Locations locations;
public JobDistanceBeeline(Locations locations) {
super();
this.locations = locations;
}
@Override
public double calculateDistance(Job i, Job j) {
double avgCost = 0.0;
// if (i instanceof Shipment && j instanceof Shipment) {
// if (i.equals(j)) {
// avgCost = 0.0;
// } else {
// Shipment s_i = (Shipment) i;
// Shipment s_j = (Shipment) j;
// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
// }
// } else
if (i instanceof Service && j instanceof Service) {
if (i.equals(j)) {
avgCost = 0.0;
} else {
Service s_i = (Service) i;
Service s_j = (Service) j;
avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
}
} else {
throw new UnsupportedOperationException(
"currently, this class just works with shipments and services.");
}
return avgCost;
}
private double calcDist(String from, String to) {
return new CrowFlyCosts(locations).getTransportCost(from, to, 0.0,null, null);
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.Driver;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
interface JobInsertionCalculator {
public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore);
}

View file

@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import algorithms.RouteStates.ActivityState;
import basics.algo.AlgorithmEndsListener;
import basics.algo.JobInsertedListener;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
import basics.route.TourActivity.JobActivity;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
class JobObserver implements JobInsertedListener, BeforeJobInsertionListener, AlgorithmEndsListener{
private static class Info {
double depTime;
double tourSize;
double insertionIndex;
double error;
public Info(double depTime, double tourSize, double insertionIndex,
double error) {
super();
this.depTime = depTime;
this.tourSize = tourSize;
this.insertionIndex = insertionIndex;
this.error = error;
}
}
private String locationId = "70";
private double routeCostBefore;
private double estimatedMC;
private boolean beforeFirst = false;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
Collection<Info> infos = new ArrayList<Info>();
@Override
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
if(job2insert instanceof Service){
if(((Service) job2insert).getLocationId().equals(locationId)){
double actualMC = insertedIn.getCost()-routeCostBefore;
TourActivity act = getAct(job2insert,insertedIn);
double error = (estimatedMC-actualMC);
int tourSize = insertedIn.getTourActivities().getActivities().size();
int insertionIndex = getIndexOf(job2insert, insertedIn);
// infos.add(new Info())
double depTime = state(act).getEarliestOperationStart()+act.getOperationTime();
infos.add(new Info(depTime,tourSize,insertionIndex,error));
// System.out.println("[id=1][tourSize="+tourSize+"][index="+insertionIndex+
// "][earliestDeparture="+depTime+
// "][tourCostBefore="+routeCostBefore+"][routeCostAfter="+insertedIn.getCost()+"]" +
// "[estimated="+Math.round(estimatedMC)+"][actual="+Math.round(actualMC)+"][error(abs)="+error +
// "][errorPerNextCustomer="+ (error/(double)(tourSize-insertionIndex)) + "]");
routeCostBefore = 0.0;
estimatedMC = 0.0;
if(!beforeFirst) throw new IllegalStateException("ähhh");
beforeFirst = false;
}
}
}
private TourActivity getAct(Job job2insert, VehicleRoute insertedIn) {
for(TourActivity act : insertedIn.getTourActivities().getActivities()){
if(act instanceof JobActivity){
if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
return act;
}
}
}
return null;
}
private int getIndexOf(Job job2insert, VehicleRoute insertedIn) {
int index=0;
for(TourActivity act : insertedIn.getTourActivities().getActivities()){
if(act instanceof JobActivity){
if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
return index;
}
}
index++;
}
return -1;
}
@Override
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
if(job instanceof Service){
if(((Service) job).getLocationId().equals(locationId)){
// System.out.println("[id=1][tourSize="+route.getTour().getActivities().size()+"][tourCost="+route.getCost()+"]" +
// "[estimatedMarginalInsertionCost="+data.getInsertionCost()+"]");
routeCostBefore = route.getCost();
estimatedMC = data.getInsertionCost();
beforeFirst = true;
}
}
}
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("output/errorAna.txt"));
for(Info info : infos){
writer.write(new StringBuilder().append(info.depTime).append(";").append(info.tourSize).append(";").append(info.insertionIndex).append(";")
.append(info.error).append("\n").toString());
}
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
interface JobRemover {
/**
* Removes jobs from vehicRoute and return true if job has been successfully removed.
*
* @return true if job removed successfully, otherwise false
*/
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.List;
import basics.Job;
import basics.route.VehicleRoute;
class JobRemoverImpl implements JobRemover{
interface RemoverListener {
public void informRemovedJob(Job j, VehicleRoute r);
}
private List<RemoverListener> remListeners = new ArrayList<RemoverListener>();
@Override
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
boolean jobRemoved = vehicleRoute.getTourActivities().removeJob(job);
if(jobRemoved) informRemovedJob(job,vehicleRoute);
return jobRemoved;
}
private void informRemovedJob(Job job, VehicleRoute vehicleRoute) {
for(RemoverListener l : remListeners) l.informRemovedJob(job, vehicleRoute);
}
public List<RemoverListener> getRemListeners() {
return remListeners;
}
}

View file

@ -0,0 +1,134 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.log4j.Logger;
import util.CrowFlyCosts;
import util.EuclideanDistanceCalculator;
import util.Locations;
import util.NeighborhoodImpl;
import util.Solutions;
import algorithms.selectors.SelectBest;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.VehicleRoutingAlgorithmFactory;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
public class NeighborhoodThresholdInitialiser implements AlgorithmStartsListener{
private static Logger log = Logger.getLogger(NeighborhoodThresholdInitialiser.class);
private NeighborhoodImpl neighborhood;
private VehicleRoutingAlgorithmFactory routingAlgorithmFactory = new VehicleRoutingAlgorithmFactory() {
@Override
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp) {
VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "resources/config.xml");
return algorithm;
}
};
private int crowFlySpeed = 20;
public NeighborhoodThresholdInitialiser(NeighborhoodImpl neighborhood) {
this.neighborhood = neighborhood;
}
/**
* @param crowFlySpeed the crowFlySpeed to set
*/
public void setCrowFlySpeed(int crowFlySpeed) {
this.crowFlySpeed = crowFlySpeed;
}
/**
* @param routingAlgorithmFactory the routingAlgorithm to set
*/
public void setRoutingAlgorithmFactory(VehicleRoutingAlgorithmFactory routingAlgorithmFactory) {
this.routingAlgorithmFactory = routingAlgorithmFactory;
}
public void initialise(VehicleRoutingProblem problem){
informAlgorithmStarts(problem, null, null);
}
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
builder.addAllJobs(problem.getJobs().values());
builder.addAllVehicles(problem.getVehicles());
VehicleRoutingProblem pblm = builder.build();
CrowFlyCosts crowFly = new CrowFlyCosts(builder.getLocations());
crowFly.speed = crowFlySpeed;
pblm.setTransportCosts(crowFly);
VehicleRoutingAlgorithm algo = routingAlgorithmFactory.createAlgorithm(pblm);
Collection<VehicleRoutingProblemSolution> mySolutions = algo.searchSolutions();
double threshold = determineThreshold(pblm,builder.getLocations(), mySolutions);
neighborhood.setThreshold(threshold);
neighborhood.initialise();
}
private double determineThreshold(VehicleRoutingProblem pblm, Locations locations, Collection<VehicleRoutingProblemSolution> mySolutions) {
VehicleRoutingProblemSolution bestSolution = Solutions.getBest(mySolutions);
double[] distances = new double[bestSolution.getRoutes().size()+pblm.getJobs().size()];
getDistances(distances,bestSolution,locations);
Mean mean = new Mean();
double meanValue = mean.evaluate(distances);
StandardDeviation dev = new StandardDeviation();
double devValue = dev.evaluate(distances, meanValue);
log.info("mean="+meanValue+", dev="+devValue);
return meanValue + devValue;
// + 2*devValue;
// return Double.MAX_VALUE;
}
private void getDistances(double[] distances, VehicleRoutingProblemSolution bestSolution, Locations locations) {
int index = 0;
for(VehicleRoute route : bestSolution.getRoutes()){
TourActivity prev = null;
for(TourActivity act : route.getTourActivities().getActivities()){
if(prev == null){ prev = act; continue; }
double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(act.getLocationId()));
// log.info("dist="+dist);
distances[index] = dist;
index++;
prev = act;
}
// double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(route.getEnd().getLocationId()));
// distances[index] = dist;
// index++;
}
}
}

View file

@ -0,0 +1,229 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
///*******************************************************************************
// * Copyright (c) 2011 Stefan Schroeder.
// * eMail: stefan.schroeder@kit.edu
// *
// * All rights reserved. This program and the accompanying materials
// * are made available under the terms of the GNU Public License v2.0
// * which accompanies this distribution, and is available at
// * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
// *
// * Contributors:
// * Stefan Schroeder - initial API and implementation
// ******************************************************************************/
//package algorithms;
//
//import java.util.ArrayList;
//import java.util.Collection;
//import java.util.List;
//import java.util.concurrent.Callable;
//import java.util.concurrent.CompletionService;
//import java.util.concurrent.ExecutionException;
//import java.util.concurrent.ExecutorCompletionService;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Future;
//
//import org.apache.log4j.Logger;
//
//import basics.InsertionData;
//import basics.Job;
//import basics.Service;
//import basics.Shipment;
//import basics.VehicleRoute;
//import basics.InsertionData.NoInsertionFound;
//
//
//
//
//
//
//
///**
// * Simplest recreation strategy. All removed customers are inserted where insertion costs are minimal. I.e. each tour-agent is asked for
// * minimal marginal insertion costs. The tour-agent offering the lowest marginal insertion costs gets the customer/shipment.
// *
// * @author stefan schroeder
// *
// */
//
//final class ParRegretInsertion extends AbstractRecreationStrategy{
//
//
// private Logger logger = Logger.getLogger(ParRegretInsertion.class);
//
//
// public static double scoreParam_of_timeWindowLegth = 0.0;
//
// private ExecutorService executor;
//
// public static double scoreParam_of_distance = 0.5;
//
// private DepotDistance depotDistance;
//
// private RouteAlgorithm routeAlgorithm;
//
// private VehicleRouteFactory vehicleRouteFactory;
//
// public ParRegretInsertion(ExecutorService executor, RouteAlgorithm routeAlgorithm, VehicleRouteFactory routeFactory, DepotDistance depotDistance) {
// super();
// this.executor = executor;
// this.routeAlgorithm = routeAlgorithm;
// this.depotDistance = depotDistance;
// this.vehicleRouteFactory = routeFactory;
// }
//
//
// @Override
// public void recreate(final Collection<VehicleRoute> vehicleRoutes, Collection<Job> unassignedJobs, double result2beat) {
// List<Job> jobs = new ArrayList<Job>(unassignedJobs);
// informRecreationStart(unassignedJobs.size());
//
// while(!jobs.isEmpty()){
// List<Job> unassignedJobList = new ArrayList<Job>(jobs);
// ScoredJob bestScoredJob = null;
// double bestScore = -1*Double.MAX_VALUE;
// CompletionService<ScoredJob> completionService = new ExecutorCompletionService<ScoredJob>(executor);
//
// for(final Job unassignedJob : unassignedJobList){
// completionService.submit(new Callable<ScoredJob>(){
//
// @Override
// public ScoredJob call() throws Exception {
// return getScoredJob(vehicleRoutes, unassignedJob);
// }
//
// });
//
// }
// try{
// for(int i=0;i<unassignedJobList.size();i++){
// Future<ScoredJob> fsj = completionService.take();
// ScoredJob scoredJob = fsj.get();
// if(scoredJob == null){
// continue;
// }
// if(scoredJob.getScore() > bestScore){
// bestScoredJob = scoredJob;
// bestScore = scoredJob.getScore();
// }
// }
// }
// catch(InterruptedException e){
// Thread.currentThread().interrupt();
// }
// catch (ExecutionException e) {
// e.printStackTrace();
// logger.error(e.getCause().toString());
// System.exit(1);
// }
// if(bestScoredJob == null){
// Job job = unassignedJobList.get(0);
// VehicleRoute newRoute = vehicleRouteFactory.createVehicleRoute();
// InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
// if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
// routeAlgorithm.insertJob(job,bestI,newRoute);
// vehicleRoutes.add(newRoute);
// jobs.remove(job);
//
// }
// else{
// routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(),bestScoredJob.getRoute());
// jobs.remove(bestScoredJob.getJob());
// }
// informJobInsertion(null, (unassignedJobList.size()-1), null);
// }
// }
//
// private ScoredJob getScoredJob(Collection<VehicleRoute> vehicleRoutes, Job job){
// InsertionData best = null;
// InsertionData secondBest = null;
// VehicleRoute bestRoute = null;
// double benchmark = Double.MAX_VALUE;
// for(VehicleRoute route : vehicleRoutes){
// if(secondBest != null){
// benchmark = secondBest.getInsertionCost();
// }
// InsertionData iData = routeAlgorithm.calculateBestInsertion(route, job, benchmark);
// if(iData instanceof NoInsertionFound) continue;
// if(best == null) {
// best = iData;
// bestRoute = route;
// }
// else if(iData.getInsertionCost() < best.getInsertionCost()){
// secondBest = best;
// best = iData;
// bestRoute = route;
// }
// else if(secondBest == null) secondBest = iData;
// else if(iData.getInsertionCost() < secondBest.getInsertionCost()) secondBest = iData;
// }
// if(best == null){
// return null;
// }
// double score = score(job,best,secondBest);
// return new ScoredJob(job, score, best, bestRoute);
// }
//
// private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
// /*
// * wieder so eine bescheuerte fallunterscheidung. hier will ich
// * doch einfach nur das maßgebende zeitfenster des jobs
// * job.getTimeWindow()
// * Problem: eine Shipment hat zwei TWs, sowohl ein PickupTW als auch
// * ein DeliveryTW
// */
// double twStart = 0.0;
// double twEnd = 0.0;
// if(unassignedJob instanceof Shipment){
// twStart = ((Shipment) unassignedJob).getDeliveryTW().getStart();
// twEnd = ((Shipment) unassignedJob).getDeliveryTW().getEnd();
// }
// else if(unassignedJob instanceof Service){
// twStart = ((Service) unassignedJob).getTimeWindow().getStart();
// twEnd = ((Service) unassignedJob).getTimeWindow().getEnd();
// }
// if(best == null){
// throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
// }
// if(secondBest == null){
// return Double.MAX_VALUE;
// }
//// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) + scoreParam_of_distance*getDistance(unassignedJob) - scoreParam_of_timeWindowLegth*(twEnd-twStart);
//// logger.info("priceDiff="+ (secondBest.getPrice()-best.getPrice()) + "; param*dist="
// double timeWindowInfluence = scoreParam_of_timeWindowLegth*(twEnd-twStart);
// double distanceInfluence = scoreParam_of_distance*getDistance(unassignedJob);
// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) - timeWindowInfluence
// + distanceInfluence;
// return score;
// }
//
// private double getDistance(Job unassignedJob) {
// return depotDistance.calcDistance(unassignedJob);
// }
//
//
// public double getTimeParam() {
// return scoreParam_of_timeWindowLegth;
// }
//
//
//}

View file

@ -0,0 +1,210 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.Service;
import basics.route.VehicleRoute;
/**
* Insertion based an regret approach.
*
* <p>Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference
* between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction.
* The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this
* customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later.
*
* @author stefan schroeder
*
*/
final class RegretInsertion extends AbstractInsertionStrategy{
/**
* Scorer to include other impacts on score such as time-window length or distance to depot.
*
* @author schroeder
*
*/
static interface ScoringFunction {
public double score(Job job);
}
/**
* Scorer that includes the length of the time-window when scoring a job. The wider the time-window, the lower the score.
*
* <p>This is the default scorer, i.e.: score = (secondBest - firstBest) + this.TimeWindowScorer.score(job)
*
* @author schroeder
*
*/
static class TimeWindowScorer implements ScoringFunction {
private double tw_scoringParam = - 0.1;
@Override
public double score(Job job) {
double twStart = 0.0;
double twEnd = 0.0;
// if(job instanceof Shipment){
// twStart = ((Shipment) job).getDeliveryTW().getStart();
// twEnd = ((Shipment) job).getDeliveryTW().getEnd();
// }
// else
if(job instanceof Service){
twStart = ((Service) job).getTimeWindow().getStart();
twEnd = ((Service) job).getTimeWindow().getEnd();
}
return (twEnd-twStart)*tw_scoringParam;
}
@Override
public String toString() {
return "[name=timeWindowScorer][scoringParam="+tw_scoringParam+"]";
}
}
public static RegretInsertion newInstance(RouteAlgorithm routeAlgorithm) {
return new RegretInsertion(routeAlgorithm);
}
private Logger logger = Logger.getLogger(RegretInsertion.class);
private RouteAlgorithm routeAlgorithm;
private ScoringFunction scoringFunction = new TimeWindowScorer();
/**
* Sets the scoring function.
*
* <p>By default, the this.TimeWindowScorer is used.
*
* @param scoringFunction
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
this.scoringFunction = scoringFunction;
}
public RegretInsertion(RouteAlgorithm routeAlgorithm) {
super();
this.routeAlgorithm = routeAlgorithm;
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=regretInsertion][additionalScorer="+scoringFunction+"]";
}
public RouteAlgorithm getRouteAlgorithm(){
return routeAlgorithm;
}
/**
* Runs insertion.
*
* <p>Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
*
*/
@Override
public void run(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs, double resultToBeat) {
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
informInsertionStarts(routes,unassignedJobs.size());
int inserted = 0;
while(!jobs.isEmpty()){
List<Job> unassignedJobList = new ArrayList<Job>(jobs);
ScoredJob bestScoredJob = null;
double bestScore = -1*Double.MAX_VALUE;
VehicleRoute insertIn = null;
for(Job unassignedJob : unassignedJobList){
InsertionData best = null;
InsertionData secondBest = null;
VehicleRoute bestRoute = null;
double benchmark = Double.MAX_VALUE;
for(VehicleRoute route : routes){
if(secondBest != null){
benchmark = secondBest.getInsertionCost();
}
InsertionData iData = routeAlgorithm.calculateBestInsertion(route, unassignedJob, benchmark);
if(iData instanceof NoInsertionFound) continue;
if(best == null){
best = iData;
bestRoute = route;
}
else if(iData.getInsertionCost() < best.getInsertionCost()){
secondBest = best;
best = iData;
bestRoute = route;
}
else if(secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())){
secondBest = iData;
}
}
if(best == null){
break;
}
double score = score(unassignedJob,best,secondBest);
if(score > bestScore){
bestScoredJob = new ScoredJob(unassignedJob,score,best,bestRoute);
bestScore = score;
}
}
Job assignedJob;
if(bestScoredJob == null){
Job job = unassignedJobList.get(0);
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
insertIn=newRoute;
assignedJob=job;
routeAlgorithm.insertJob(job,bestI,newRoute);
routes.add(newRoute);
jobs.remove(job);
}
else{
routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
insertIn=bestScoredJob.getRoute();
assignedJob=bestScoredJob.getJob();
jobs.remove(bestScoredJob.getJob());
}
inserted++;
informJobInserted((unassignedJobList.size()-inserted), assignedJob, insertIn);
}
}
private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
if(best == null){
throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
}
if(secondBest == null){
return Double.MAX_VALUE;
}
return (secondBest.getInsertionCost()-best.getInsertionCost()) + scoringFunction.score(unassignedJob);
}
}

View file

@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import basics.algo.InsertionEndsListener;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class RemoveEmptyVehicles implements InsertionStartsListener, InsertionEndsListener{
private static Logger log = Logger.getLogger(RemoveEmptyVehicles.class);
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
// List<VehicleRoute> routes = new ArrayList<VehicleRoute>(vehicleRoutes);
// for(VehicleRoute route : routes){
// if(route.isEmpty()) { vehicleRoutes.remove(route); }
// }
}
@Override
public String toString() {
return "[name=removeEmptyVehicles]";
}
@Override
public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes) {
List<VehicleRoute> routes = new ArrayList<VehicleRoute>(vehicleRoutes);
for(VehicleRoute route : routes){
if(route.isEmpty()) { vehicleRoutes.remove(route); }
}
}
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import org.apache.log4j.Logger;
import basics.algo.InsertionStartsListener;
import basics.route.VehicleRoute;
class ResetAndIniFleetManager implements InsertionStartsListener{
private static Logger log = Logger.getLogger(ResetAndIniFleetManager.class);
private VehicleFleetManager vehicleFleetManager;
ResetAndIniFleetManager(VehicleFleetManager vehicleFleetManager) {
super();
this.vehicleFleetManager = vehicleFleetManager;
}
@Override
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate) {
vehicleFleetManager.unlockAll();
for(VehicleRoute route : vehicleRoutes){
if(!route.isEmpty()){
vehicleFleetManager.lock(route.getVehicle());
}
}
// log.info("#lockedVehicles=" + ((VehicleFleetManagerImpl)vehicleFleetManager).sizeOfLockedVehicles() + " #routes="+vehicleRoutes.size());
}
@Override
public String toString() {
return "[name=resetAndIniFleetManager]";
}
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.Job;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
interface RouteAlgorithm {
interface RouteAlgorithmListener {
}
interface JobRemovedListener extends RouteAlgorithmListener{
public void removed(VehicleRoute route, Job job);
}
interface JobInsertedListener extends RouteAlgorithmListener{
public void inserted(VehicleRoute route, Job job);
}
interface VehicleSwitchedListener extends RouteAlgorithmListener{
public void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle);
}
/**
* Calculates the best insertion position and the corresponding marginal costs of inserting the job (according to the insertionCostCalculator).
* This does not affect any input parameter, thus the vehicleRoute and its data will not be changed/affected.
*
* @param VehicleRoute, Job, double
* @return InsertionData
*/
public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownPrice);
/**
* Removes job from vehicleRoute and does not update the resulting tour. Thus the tour state might not be valid anymore.
* Note that this changes vehicleRoute!
*
* @return true if job removed successfully, otherwise false
*/
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
/**
* Removes job from input parameter vehicleRoute AND updates the state of the resulting tour with tourStateCalc.
* Note that this changes para vehicleRoute!
*/
public boolean removeJob(Job job, VehicleRoute vehicleRoute);
/**
* Inserts job into vehicleRoute.getTour().
* Please note, that this changes the parameter vehicleRoute!
*/
public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData);
/**
* Inserts job into vehicleRoute.getTour().
* Please note, that this changes the parameter vehicleRoute!
*/
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute);
/**
* Updates vehicleRoute, i.e. uses the tourStateCalculator to update for example timeWindows and loads on vehicleRoute.getTour()
* Note that this changes the parameter vehicleRoute!
*/
public void updateTour(VehicleRoute vehicleRoute);
public Collection<RouteAlgorithmListener> getListeners();
}

View file

@ -0,0 +1,181 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import algorithms.InsertionData.NoInsertionFound;
import basics.Job;
import basics.Service;
import basics.route.ServiceActivity;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
final class RouteAlgorithmImpl implements RouteAlgorithm {
private static Logger logger = Logger.getLogger(RouteAlgorithmImpl.class);
static double NO_DEPARTURE_TIME = -12345.12345;
private String algoDescription = "algorithm to remove and insert jobs from vehicleRoutes. it also calculates marginal costs of the best insertion of a " +
"job into the given vehicle route";
public static RouteAlgorithmImpl newInstance(JobInsertionCalculator jobInsertionCalculator, VehicleRouteUpdater tourStateCalculator){
return new RouteAlgorithmImpl(jobInsertionCalculator, tourStateCalculator);
}
private Collection<RouteAlgorithmListener> listeners = new ArrayList<RouteAlgorithmListener>();
private VehicleRouteUpdater tourCalculator;
private JobInsertionCalculator insertionCostCalculator;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
private RouteAlgorithmImpl(JobInsertionCalculator insertionCostCalculator, VehicleRouteUpdater tourCalculator){
this.tourCalculator = tourCalculator;
this.insertionCostCalculator = insertionCostCalculator;
}
public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownCost) {
return insertionCostCalculator.calculate(vehicleRoute, job, null, NO_DEPARTURE_TIME, null, bestKnownCost);
}
public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
boolean removed = vehicleRoute.getTourActivities().removeJob(job);
if(removed){
jobRemoved(vehicleRoute,job);
}
return removed;
}
private void jobRemoved(VehicleRoute vehicleRoute, Job job) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof JobRemovedListener){
((JobRemovedListener) l).removed(vehicleRoute, job);
}
}
}
public boolean removeJob(Job job, VehicleRoute vehicleRoute){
boolean removed = removeJobWithoutTourUpdate(job, vehicleRoute);
if(removed) updateTour(vehicleRoute);
return removed;
}
public void updateTour(VehicleRoute vehicleRoute){
boolean tourIsFeasible = tourCalculator.updateRoute(vehicleRoute);
if(!tourIsFeasible){
throw new IllegalStateException("At this point tour should be feasible. but it is not. \n currentTour=" + vehicleRoute.getTourActivities() +
"\n error sources: check jobInsertionCostCalculators and actInsertionCalculators. somehow an insertion is made, althought a hard constraint is broken. Here, hard constraints refer to \n" +
"hard time-window constraints. If you want to deal with such constraints, make sure a violation is penalized properly (such that it can never be the best insertion position). \n" +
"If you use CalculatesServiceInsertion and CalculatesActivityInsertion, the only hard constraint is the vehicle-capacity constraints. A violation of time-windows must be penalized in \n" +
"the vehicleRouteCostFunction. For example: in handleActivity(....) one can check whether the act start-time is higher than the latestOperationStartTime. If so penalize it with a very high value. \n" +
"For example: \n" +
"public void handleActivity(TourActivity tourAct, double startTime, double endTime) {\n" +
"\tif(startTime > tourAct.getLatestOperationStartTime()){\n" +
"\t\tcost += Double.MAX_VALUE;\n" +
"\t}\n" +
"});");
}
}
@Override
public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData) {
if(insertionData == null || (insertionData instanceof NoInsertionFound)) throw new IllegalStateException("insertionData null. cannot insert job.");
if(job == null) throw new IllegalStateException("cannot insert null-job");
if(!(vehicleRoute.getVehicle().getId().toString().equals(insertionData.getSelectedVehicle().getId().toString()))){
vehicleSwitched(vehicleRoute.getVehicle(),insertionData.getSelectedVehicle());
vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
}
if(job instanceof Service) {
vehicleRoute.getTourActivities().addActivity(insertionData.getDeliveryInsertionIndex(), ServiceActivity.newInstance((Service)job));
// if(vehicleRoute.getStart().getEndTime() != insertionData.getVehicleDepartureTime()){
// logger.info("depTime switched from " + vehicleRoute.getStart().getEndTime() + " to " + insertionData.getVehicleDepartureTime());
// }
vehicleRoute.setDepartureTime(insertionData.getVehicleDepartureTime());
}
else throw new IllegalStateException("neither service nor shipment. this is not supported.");
jobInserted(vehicleRoute,job);
}
private void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof VehicleSwitchedListener){
((VehicleSwitchedListener) l).vehicleSwitched(oldVehicle,newVehicle);
}
}
}
private void jobInserted(VehicleRoute vehicleRoute, Job job) {
for(RouteAlgorithmListener l : listeners){
if(l instanceof JobInsertedListener){
((JobInsertedListener) l).inserted(vehicleRoute, job);
}
}
}
public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){
insertJobWithoutTourUpdate(vehicleRoute, job, insertionData);
updateTour(vehicleRoute);
}
@Override
public String toString() {
return algoDescription;
}
private void insertJob(Service service, int serviceInsertionIndex, VehicleRoute vehicleRoute) {
// TourActivity del = actStates.getActivity(service,true);
vehicleRoute.getTourActivities().addActivity(serviceInsertionIndex, ServiceActivity.newInstance(service));
}
public Collection<RouteAlgorithmListener> getListeners() {
return listeners;
}
public void setAlgoDescription(String algoDescription) {
this.algoDescription = algoDescription;
}
}

View file

@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import basics.Job;
import basics.Service;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.route.ServiceActivity;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
class RouteStates implements IterationStartsListener{
Logger log = Logger.getLogger(RouteStates.class);
static class RouteState {
private double costs;
private int load;
private VehicleRoute route;
public RouteState(VehicleRoute route) {
super();
this.route = route;
}
/**
* @return the costs
*/
public double getCosts() {
return costs;
}
/**
* @param costs the costs to set
*/
public void setCosts(double costs) {
this.costs = costs;
}
/**
* @return the load
*/
public int getLoad() {
return load;
}
/**
* @param load the load to set
*/
public void setLoad(int load) {
this.load = load;
}
}
static class ActivityState {
private double earliestOperationStart;
private double latestOperationStart;
private double currentLoad;
private double currentCost;
private TourActivity act;
public ActivityState(TourActivity activity){
this.earliestOperationStart=activity.getTheoreticalEarliestOperationStartTime();
this.latestOperationStart=activity.getTheoreticalLatestOperationStartTime();
this.act = activity;
}
@Override
public String toString() {
return "[earliestStart="+earliestOperationStart+"][latestStart="+
latestOperationStart+"][currLoad="+currentLoad+"][currCost="+currentCost+"]";
}
public double getEarliestOperationStart() {
return earliestOperationStart;
}
void setEarliestOperationStart(double earliestOperationStart) {
this.earliestOperationStart = earliestOperationStart;
}
public double getLatestOperationStart() {
return latestOperationStart;
}
void setLatestOperationStart(double latestOperationStart) {
this.latestOperationStart = latestOperationStart;
}
public double getCurrentLoad() {
return currentLoad;
}
void setCurrentLoad(double currentLoad) {
this.currentLoad = currentLoad;
}
public double getCurrentCost() {
return currentCost;
}
void setCurrentCost(double currentCost) {
this.currentCost = currentCost;
}
public void reset() {
earliestOperationStart = act.getTheoreticalEarliestOperationStartTime();
latestOperationStart = act.getTheoreticalLatestOperationStartTime() ;
currentLoad = 0.0;
currentCost = 0.0;
}
}
private Map<TourActivity, ActivityState> activityStates;
private Map<Service, TourActivity> tourActivities;
private Map<VehicleRoute, RouteState> routeStates;
public RouteStates() {
activityStates = new HashMap<TourActivity, RouteStates.ActivityState>();
tourActivities = new HashMap<Service,TourActivity>();
routeStates = new HashMap<VehicleRoute, RouteStates.RouteState>();
}
ActivityState getState(TourActivity act){
if(!activityStates.containsKey(act)) return null;
return activityStates.get(act);
}
public void clearStates(){
activityStates.clear();
}
public Map<TourActivity, ActivityState> getActivityStates() {
return activityStates;
}
TourActivity getActivity(Service service, boolean resetState){
TourActivity tourActivity = tourActivities.get(service);
getState(tourActivity).reset();
return tourActivity;
}
public void resetRouteStates(){
routeStates.clear();
}
public RouteState getRouteState(VehicleRoute route){
RouteState routeState = routeStates.get(route);
if(routeState == null){
routeState = new RouteState(route);
putRouteState(route, routeState);
}
return routeState;
}
private void putRouteState(VehicleRoute route, RouteState routeState){
routeStates.put(route, routeState);
}
void initialiseStateOfJobs(Collection<Job> jobs){
for(Job job : jobs){
if(job instanceof Service){
ServiceActivity service = ServiceActivity.newInstance((Service)job);
ActivityState state = new ActivityState(service);
tourActivities.put((Service) job, service);
activityStates.put(service, state);
}
else{
throw new IllegalStateException();
}
}
}
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
resetRouteStates();
}
}

View file

@ -0,0 +1,207 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeSet;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import util.StopWatch;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.SearchStrategyModule;
import basics.route.VehicleRoute;
final class RuinRadial implements RuinStrategy {
private final static String NAME = "radialRuin";
/**
* returns a new creation of instance of ruinRadial
* @param vrp
* @param fraction TODO
* @param jobDistance
* @param jobRemover TODO
* @param routeUpdater TODO
* @return
*/
static RuinRadial newInstance(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
return new RuinRadial(vrp, fraction, jobDistance, jobRemover, routeUpdater);
}
static class ReferencedJob {
private Job job;
private double distance;
public ReferencedJob(Job job, double distance) {
super();
this.job = job;
this.distance = distance;
}
public Job getJob() {
return job;
}
public double getDistance() {
return distance;
}
}
private Logger logger = Logger.getLogger(RuinRadial.class);
private VehicleRoutingProblem vrp;
private double fractionOfAllNodes2beRuined;
private Map<String, TreeSet<ReferencedJob>> distanceNodeTree = new HashMap<String, TreeSet<ReferencedJob>>();
private Random random = RandomNumberGeneration.getRandom();
private JobDistance jobDistance;
private JobRemover jobRemover;
private VehicleRouteUpdater routeUpdater;
public void setRandom(Random random) {
this.random = random;
}
public RuinRadial(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater) {
super();
this.vrp = vrp;
this.jobDistance = jobDistance;
this.jobRemover = jobRemover;
this.routeUpdater = routeUpdater;
this.fractionOfAllNodes2beRuined = fraction;
calculateDistancesFromJob2Job();
logger.info("intialise " + this);
}
public void setRuinFraction(double fractionOfAllNodes) {
this.fractionOfAllNodes2beRuined = fractionOfAllNodes;
logger.info("fraction set " + this);
}
private void calculateDistancesFromJob2Job() {
logger.info("preprocess distances between locations ...");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int nuOfDistancesStored = 0;
for (Job i : vrp.getJobs().values()) {
TreeSet<ReferencedJob> treeSet = new TreeSet<ReferencedJob>(
new Comparator<ReferencedJob>() {
@Override
public int compare(ReferencedJob o1, ReferencedJob o2) {
if (o1.getDistance() <= o2.getDistance()) {
return 1;
} else {
return -1;
}
}
});
distanceNodeTree.put(i.getId(), treeSet);
for (Job j : vrp.getJobs().values()) {
double distance = jobDistance.calculateDistance(i, j);
ReferencedJob refNode = new ReferencedJob(j, distance);
treeSet.add(refNode);
nuOfDistancesStored++;
}
}
stopWatch.stop();
logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
(distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
}
@Override
public String toString() {
return "[name=radialRuin][fraction="+fractionOfAllNodes2beRuined+"]";
}
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
if(vehicleRoutes.isEmpty()){
return Collections.EMPTY_LIST;
}
int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
if (nOfJobs2BeRemoved == 0) {
return Collections.EMPTY_LIST;
}
Job randomJob = pickRandomJob();
Collection<Job> unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
return unassignedJobs;
}
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
List<Job> unassignedJobs = new ArrayList<Job>();
TreeSet<ReferencedJob> tree = distanceNodeTree.get(targetJob.getId());
Iterator<ReferencedJob> descendingIterator = tree.descendingIterator();
int counter = 0;
while (descendingIterator.hasNext() && counter < nOfJobs2BeRemoved) {
ReferencedJob refJob = descendingIterator.next();
Job job = refJob.getJob();
unassignedJobs.add(job);
counter++;
boolean removed = false;
for (VehicleRoute route : vehicleRoutes) {
removed = jobRemover.removeJobWithoutTourUpdate(job, route);
if (removed) {
break;
}
}
}
for(VehicleRoute route : vehicleRoutes){
routeUpdater.updateRoute(route);
}
return unassignedJobs;
}
private Job pickRandomJob() {
int totNuOfJobs = vrp.getJobs().values().size();
int randomIndex = random.nextInt(totNuOfJobs);
Job job = new ArrayList<Job>(vrp.getJobs().values()).get(randomIndex);
return job;
}
private int getNuOfJobs2BeRemoved() {
return (int) Math.ceil(vrp.getJobs().values().size()
* fractionOfAllNodes2beRuined);
}
// @Override
// public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
// ruin(vrpSolution.getRoutes());
// return vrpSolution;
// }
//
// @Override
// public String getName() {
// return NAME;
// }
}

View file

@ -0,0 +1,151 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Logger;
import util.RandomNumberGeneration;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.route.VehicleRoute;
/**
* Ruin strategy that ruins current solution randomly. I.e.
* customer are removed randomly from current solution.
*
* @author stefan schroeder
*
*/
final class RuinRandom implements RuinStrategy {
public static RuinRandom newInstance(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
return new RuinRandom(vrp, fraction, jobRemover, routeUpdater);
}
private Logger logger = Logger.getLogger(RuinRandom.class);
private VehicleRoutingProblem vrp;
private double fractionOfAllNodes2beRuined;
private Random random = RandomNumberGeneration.getRandom();
private JobRemover jobRemover;
private VehicleRouteUpdater vehicleRouteUpdater;
public void setRandom(Random random) {
this.random = random;
}
/**
* Constructs ruinRandom.
*
* @param vrp
* @param fraction which is the fraction of total c
* @param jobRemover
* @param vehicleRouteUpdater
*/
public RuinRandom(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater vehicleRouteUpdater) {
super();
this.vrp = vrp;
this.jobRemover = jobRemover;
this.vehicleRouteUpdater = vehicleRouteUpdater;
this.fractionOfAllNodes2beRuined = fraction;
logger.info("initialise " + this);
logger.info("done");
}
/**
* Removes a fraction of jobs from vehicleRoutes.
*
* <p>The number of jobs is calculated as follows: Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined).
*/
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes) {
List<Job> unassignedJobs = new ArrayList<Job>();
int nOfJobs2BeRemoved = selectNuOfJobs2BeRemoved();
ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
return unassignedJobs;
}
/**
* Removes nOfJobs2BeRemoved from vehicleRoutes, including targetJob.
*/
@Override
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved) {
List<Job> unassignedJobs = new ArrayList<Job>();
if(targetJob != null){
boolean removed = false;
for (VehicleRoute route : vehicleRoutes) {
removed = jobRemover.removeJobWithoutTourUpdate(targetJob, route);
if (removed) {
nOfJobs2BeRemoved--;
unassignedJobs.add(targetJob);
break;
}
}
}
ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
return unassignedJobs;
}
public void setRuinFraction(double fractionOfAllNodes2beRuined) {
this.fractionOfAllNodes2beRuined = fractionOfAllNodes2beRuined;
logger.info("fraction set " + this);
}
private void ruin(Collection<VehicleRoute> vehicleRoutes,int nOfJobs2BeRemoved, List<Job> unassignedJobs) {
LinkedList<Job> availableJobs = new LinkedList<Job>(vrp.getJobs().values());
for (int i = 0; i < nOfJobs2BeRemoved; i++) {
Job job = pickRandomJob(availableJobs);
unassignedJobs.add(job);
availableJobs.remove(job);
for (VehicleRoute route : vehicleRoutes) {
boolean removed = jobRemover.removeJobWithoutTourUpdate(job, route);
if (removed) break;
}
}
updateRoutes(vehicleRoutes);
}
private void updateRoutes(Collection<VehicleRoute> vehicleRoutes) {
for(VehicleRoute route : vehicleRoutes){
vehicleRouteUpdater.updateRoute(route);
}
}
@Override
public String toString() {
return "[name=randomRuin][fraction="+fractionOfAllNodes2beRuined+"]";
}
private Job pickRandomJob(LinkedList<Job> availableJobs) {
int randomIndex = random.nextInt(availableJobs.size());
return availableJobs.get(randomIndex);
}
private int selectNuOfJobs2BeRemoved() {
return (int) Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined);
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.List;
import basics.Job;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
interface RuinStrategy {
public static interface RuinListener {
}
/**
* Ruins a current solution, i.e. removes jobs from service providers and
* returns a collection of these removed, and thus unassigned, jobs.
*
* @param vehicleRoutes
* @return
*/
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes);
public Collection<Job> ruin(Collection<VehicleRoute> vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved);
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.VehicleRoutingProblem;
interface RuinStrategyFactory {
public RuinStrategy createStrategy(VehicleRoutingProblem vrp);
}

View file

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.net.URL;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
/**
* Factory that creates the {@link VehicleRoutingAlgorithm} as proposed by Schrimpf et al., 2000 with the following parameters:
*
* <p>
* R&R_random (prob=0.5, F=0.5);
* R&R_radial (prob=0.5, F=0.3);
* threshold-accepting with exponentialDecayFunction (alpha=0.1, warmup-iterations=100);
* nuOfIterations=2000
*
* <p>Gerhard Schrimpf, Johannes Schneider, Hermann Stamm- Wilbrandt, and Gunter Dueck.
* Record breaking optimization results using the ruin and recreate principle.
* Journal of Computational Physics, 159(2):139 171, 2000. ISSN 0021-9991. doi: 10.1006/jcph.1999. 6413.
* URL http://www.sciencedirect.com/science/article/ pii/S0021999199964136
*
* <p>algorithm-xml-config is available at src/main/resources/schrimpf.xml.
*
* @author stefan schroeder
*
*/
public class SchrimpfFactory {
/**
* Creates the {@link VehicleRoutingAlgorithm}.
*
* @param vrp
* @return algorithm
*/
public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
URL resource = this.getClass().getClassLoader().getResource("schrimpf.xml");
new AlgorithmConfigXmlReader(algorithmConfig).read(resource.getPath());
return VehicleRoutingAlgorithms.createAlgorithm(vrp, algorithmConfig);
}
}

View file

@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
class ScoredJob {
private final Job job;
private final double score;
private final InsertionData insertionData;
private final VehicleRoute route;
public ScoredJob(final Job job, final double score, final InsertionData insertionData, final VehicleRoute route) {
super();
this.job = job;
this.score = score;
this.insertionData = insertionData;
this.route = route;
}
public InsertionData getInsertionData() {
return insertionData;
}
public VehicleRoute getRoute() {
return route;
}
public Job getJob() {
return job;
}
public double getScore() {
return score;
}
}

View file

@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import basics.Job;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmEndsListener;
import basics.route.VehicleRoute;
class SolutionVerifier implements AlgorithmEndsListener{
@Override
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
for(VehicleRoutingProblemSolution solution : solutions){
Set<Job> jobsInSolution = new HashSet<Job>();
for(VehicleRoute route : solution.getRoutes()){
jobsInSolution.addAll(route.getTourActivities().getJobs());
}
if(jobsInSolution.size() != problem.getJobs().size()){
throw new IllegalStateException("we are at the end of the algorithm and still have not found a valid solution." +
"This cannot be.");
}
}
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.Job;
import basics.route.VehicleRoute;
class TourConstraintEngine {
// void ini(VehicleRoute route, Job job){
//
// }
//
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.VehicleRoute;
/**
* Updates tour state, i.e. the tour as well as each activity in that tour has a state such as currentLoad, currentCost, earliestOperationTime and
* latestOperationTime. Each time the tour is changed (for instance by removing or adding an activity), tour and activity states
* might change, thus this updater updates activity states.
* This includes:
* - update load and totalCost at tour-level
* - update currentLoad and currentCost at activity-level
* - update earliest- and latestOperationStart values at activity-level
*
* If ensureFeasibility is true, then it additionally checks whether the earliestOperationStartTime is higher than the latestOperationStartTime.
* If it is, it returns a false value to indicate that the tour is not feasible. This makes only sense for hard-timewindows.
*
* If softTimeWindow is set to true, latestOperationStartTimes are not updated and the tour is always feasible.
*
* @author stefan schroeder
*
*/
class TourStateUpdater implements VehicleRouteUpdater{
// public final static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger logger = Logger.getLogger(TourStateUpdater.class);
private boolean ensureFeasibility = true;
private UpdateTourStatesForwardInTime forwardUpdate;
private UpdateTourStatesBackwardInTime backwardUpdate;
private boolean updateTimeWindows = true;
private RouteStates actStates;
public TourStateUpdater(RouteStates activityStates, VehicleRoutingTransportCosts costs, VehicleRoutingActivityCosts costFunction) {
super();
forwardUpdate = new UpdateTourStatesForwardInTime(costs, costs, costFunction);
backwardUpdate = new UpdateTourStatesBackwardInTime(costs);
actStates=activityStates;
forwardUpdate.setActivityStates(actStates);
backwardUpdate.setActivityStates(actStates);
}
/*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
if(updateTimeWindows){
backwardUpdate.checkFeasibility = ensureFeasibility;
backwardUpdate.updateRoute(vehicleRoute);
}
forwardUpdate.updateRoute(vehicleRoute);
boolean tourIsFeasible = true;
return tourIsFeasible;
}
public void setTimeWindowUpdate(boolean updateTimeWindows) {
this.updateTimeWindows = updateTimeWindows;
logger.info("set timeWindowUpdate to " + updateTimeWindows);
}
public void setEnsureFeasibility(boolean ensureFeasibility) {
this.ensureFeasibility = ensureFeasibility;
}
}

View file

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Iterator;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.BackwardTransportTime;
import basics.route.Start;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.VehicleRoute;
/**
*
* @author stefan schroeder
*
*/
class UpdateTourStatesBackwardInTime implements VehicleRouteUpdater{
// public static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger log = Logger.getLogger(UpdateTourStatesBackwardInTime.class);
public boolean checkFeasibility = true;
private BackwardTransportTime transportTime;
private RouteStates actStates;
public void setActivityStates(RouteStates actStates){
this.actStates = actStates;
}
public ActivityState state(TourActivity act){
return actStates.getState(act);
}
public UpdateTourStatesBackwardInTime(BackwardTransportTime transportTime) {
super();
this.transportTime = transportTime;
}
/*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
boolean ok = update(vehicleRoute);
return ok;
}
private boolean update(VehicleRoute vehicleRoute) {
TourActivities tour = vehicleRoute.getTourActivities();
int tourSize = tour.getActivities().size();
Iterator<TourActivity> reverseActIter = vehicleRoute.getTourActivities().reverseActivityIterator();
TourActivity prevAct;
boolean feasible = true;
prevAct = vehicleRoute.getEnd();
double startAtPrevAct = prevAct.getTheoreticalLatestOperationStartTime();
int count = 0;
while(reverseActIter.hasNext()){
TourActivity currAct = reverseActIter.next();
double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, currAct, startAtPrevAct);
ActivityState state = state(currAct);
state.setLatestOperationStart(latestOperationStartTime);
prevAct = currAct;
startAtPrevAct = latestOperationStartTime;
count++;
}
// Start start = vehicleRoute.getStart();
// double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, start, startAtPrevAct);
assert count == tourSize;
return feasible;
}
private double latestOperationStartTime(VehicleRoute vehicleRoute,
TourActivity prevAct, TourActivity currAct, double startAtPrevAct) {
double latestDepTimeAtCurrAct = startAtPrevAct - transportTime.getBackwardTransportTime(currAct.getLocationId(), prevAct.getLocationId(), startAtPrevAct, vehicleRoute.getDriver(),vehicleRoute.getVehicle());
double potentialLatestOperationStartTimeAtCurrAct = latestDepTimeAtCurrAct - currAct.getOperationTime();
double latestOperationStartTime = Math.min(currAct.getTheoreticalLatestOperationStartTime(), potentialLatestOperationStartTimeAtCurrAct);
return latestOperationStartTime;
}
}

View file

@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import org.apache.log4j.Logger;
import algorithms.RouteStates.ActivityState;
import basics.costs.ForwardTransportCost;
import basics.costs.ForwardTransportTime;
import basics.costs.VehicleRoutingActivityCosts;
import basics.route.Driver;
import basics.route.End;
import basics.route.ServiceActivity;
import basics.route.TourActivities;
import basics.route.TourActivity;
import basics.route.Vehicle;
import basics.route.VehicleRoute;
/**
*
* @author sschroeder
*
*/
class UpdateTourStatesForwardInTime implements VehicleRouteUpdater{
// public static Counter counter = new Counter("#updateTWProcesses: ");
private static Logger log = Logger.getLogger(UpdateTourStatesForwardInTime.class);
public boolean checkFeasibility = true;
private VehicleRoutingActivityCosts activityCost;
private ForwardTransportTime transportTime;
private ForwardTransportCost transportCost;
private RouteStates routeStates;
private boolean activityStatesSet = false;
public void setActivityStates(RouteStates actStates){
this.routeStates = actStates;
activityStatesSet = true;
}
public ActivityState state(TourActivity act){
return routeStates.getState(act);
}
public UpdateTourStatesForwardInTime(ForwardTransportTime transportTime, ForwardTransportCost transportCost, VehicleRoutingActivityCosts activityCost) {
super();
this.transportTime = transportTime;
this.transportCost = transportCost;
this.activityCost = activityCost;
}
/**
*
*
*/
public boolean updateRoute(VehicleRoute vehicleRoute) {
vehicleRoute.getVehicleRouteCostCalculator().reset();
Vehicle vehicle = vehicleRoute.getVehicle();
Driver driver = vehicleRoute.getDriver();
TourActivity prevAct = vehicleRoute.getStart();
double startAtPrevAct = vehicleRoute.getStart().getEndTime();
double totalOperationCost = 0.0;
int totalLoadPicked = 0;
int currentLoadState = 0;
for(TourActivity currentAct : vehicleRoute.getTourActivities().getActivities()){
totalLoadPicked += getPickedLoad(currentAct);
currentLoadState += getCapDemand(currentAct);
double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
double operationStartTime = Math.max(currentAct.getTheoreticalEarliestOperationStartTime(), arrivalTimeAtCurrAct);
double operationEndTime = operationStartTime + currentAct.getOperationTime();
currentAct.setArrTime(arrivalTimeAtCurrAct);
currentAct.setEndTime(operationEndTime);
double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double actCost = activityCost.getActivityCost(currentAct, arrivalTimeAtCurrAct, driver, vehicle);
vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
vehicleRoute.getVehicleRouteCostCalculator().addActivityCost(actCost);
totalOperationCost += transportCost;
totalOperationCost += actCost;
if(activityStatesSet){
ActivityState currentState = state(currentAct);
currentState.setEarliestOperationStart(operationStartTime);
currentState.setCurrentLoad(currentLoadState);
currentState.setCurrentCost(totalOperationCost);
}
prevAct = currentAct;
startAtPrevAct = operationEndTime;
}
End currentAct = vehicleRoute.getEnd();
double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
currentAct.setArrTime(arrivalTimeAtCurrAct);
currentAct.setEndTime(arrivalTimeAtCurrAct);
totalOperationCost += transportCost;
routeStates.getRouteState(vehicleRoute).setCosts(totalOperationCost);
routeStates.getRouteState(vehicleRoute).setLoad(totalLoadPicked);
vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getDriver());
vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getVehicle());
vehicleRoute.getVehicleRouteCostCalculator().finish();
return true;
}
private int getCapDemand(TourActivity currentAct) {
return currentAct.getCapacityDemand();
}
private double getPickedLoad(TourActivity currentAct) {
if(currentAct instanceof ServiceActivity){
return currentAct.getCapacityDemand();
}
// else if(currentAct instanceof Pickup){
// return currentAct.getCapacityDemand();
// }
return 0.0;
}
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.Collection;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.VehicleType;
interface VehicleFleetManager {
public static class TypeKey {
public final VehicleType type;
public final String locationId;
public TypeKey(VehicleType type, String locationId) {
super();
this.type = type;
this.locationId = locationId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((locationId == null) ? 0 : locationId.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TypeKey other = (TypeKey) obj;
if (locationId == null) {
if (other.locationId != null)
return false;
} else if (!locationId.equals(other.locationId))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}
public abstract Vehicle getEmptyVehicle(TypeKey typeId);
public abstract Collection<TypeKey> getAvailableVehicleTypes();
public abstract void lock(Vehicle vehicle);
public abstract void unlock(Vehicle vehicle);
public abstract Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType);
public abstract boolean isLocked(Vehicle vehicle);
public abstract void unlockAll();
}

View file

@ -0,0 +1,247 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import basics.route.Vehicle;
import basics.route.VehicleImpl.NoVehicle;
class VehicleFleetManagerImpl implements VehicleFleetManager {
public VehicleFleetManagerImpl newInstance(Collection<Vehicle> vehicles){
return new VehicleFleetManagerImpl(vehicles);
}
public static VehicleFleetManager createDefaultFleetManager() {
return new DefaultFleetManager();
}
public static class DefaultFleetManager extends VehicleFleetManagerImpl {
public DefaultFleetManager() {
super(Collections.EMPTY_LIST);
}
}
static class TypeContainer {
private TypeKey type;
private ArrayList<Vehicle> vehicleList;
public TypeContainer(TypeKey type) {
super();
this.type = type;
vehicleList = new ArrayList<Vehicle>();
}
void add(Vehicle vehicle){
if(vehicleList.contains(vehicle)){
throw new IllegalStateException("cannot add vehicle twice " + vehicle.getId());
}
vehicleList.add(vehicle);
}
void remove(Vehicle vehicle){
vehicleList.remove(vehicle);
}
public Vehicle getVehicle() {
return vehicleList.get(0);
// return vehicleList.getFirst();
}
public boolean isEmpty() {
return vehicleList.isEmpty();
}
}
private static Logger logger = Logger.getLogger(VehicleFleetManagerImpl.class);
private Collection<Vehicle> vehicles;
private Set<Vehicle> lockedVehicles;
private Map<TypeKey,TypeContainer> typeMapOfAvailableVehicles;
public VehicleFleetManagerImpl(Collection<Vehicle> vehicles) {
super();
this.vehicles = vehicles;
this.lockedVehicles = new HashSet<Vehicle>();
makeMap();
logger.info("initialise " + this);
}
public VehicleFleetManagerImpl(Collection<Vehicle> vehicles, Collection<Vehicle> lockedVehicles) {
this.vehicles = vehicles;
makeMap();
this.lockedVehicles = new HashSet<Vehicle>();
for(Vehicle v : lockedVehicles){
lock(v);
}
logger.info("initialise " + this);
}
@Override
public String toString() {
return "[name=finiteVehicles]";
}
private void makeMap() {
typeMapOfAvailableVehicles = new HashMap<TypeKey, TypeContainer>();
for(Vehicle v : vehicles){
addVehicle(v);
}
}
private void addVehicle(Vehicle v) {
if(v.getType() == null){
throw new IllegalStateException("vehicle needs type");
}
// String typeId = v.getType().typeId;
TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
if(!typeMapOfAvailableVehicles.containsKey(typeKey)){
typeMapOfAvailableVehicles.put(typeKey, new TypeContainer(typeKey));
}
typeMapOfAvailableVehicles.get(typeKey).add(v);
}
private void removeVehicle(Vehicle v){
TypeKey key = new TypeKey(v.getType(),v.getLocationId());
if(typeMapOfAvailableVehicles.containsKey(key)){
typeMapOfAvailableVehicles.get(key).remove(v);
}
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getEmptyVehicle(java.lang.String)
*/
@Override
public Vehicle getEmptyVehicle(TypeKey typeId){
Vehicle v = null;
if(typeMapOfAvailableVehicles.containsKey(typeId)){
v = typeMapOfAvailableVehicles.get(typeId).getVehicle();
}
return v;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes()
*/
@Override
public Collection<TypeKey> getAvailableVehicleTypes(){
List<TypeKey> types = new ArrayList<TypeKey>();
for(TypeKey key : typeMapOfAvailableVehicles.keySet()){
if(!typeMapOfAvailableVehicles.get(key).isEmpty()){
types.add(key);
}
}
return types;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#lock(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public void lock(Vehicle vehicle){
if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
return;
}
boolean locked = lockedVehicles.add(vehicle);
removeVehicle(vehicle);
if(!locked){
throw new IllegalStateException("cannot lock vehicle twice " + vehicle.getId());
}
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlock(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public void unlock(Vehicle vehicle){
if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
return;
}
if(vehicle == null) return;
lockedVehicles.remove(vehicle);
addVehicle(vehicle);
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes(java.lang.String)
*/
@Override
public Collection<TypeKey> getAvailableVehicleTypes(TypeKey withoutThisType) {
List<TypeKey> types = new ArrayList<TypeKey>();
for(TypeKey typeKey : typeMapOfAvailableVehicles.keySet()){
if(typeKey.equals(withoutThisType)){
continue;
}
if(!typeMapOfAvailableVehicles.get(typeKey).isEmpty()){
types.add(typeKey);
}
}
return types;
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#isLocked(org.matsim.contrib.freight.vrp.basics.Vehicle)
*/
@Override
public boolean isLocked(Vehicle vehicle) {
return lockedVehicles.contains(vehicle);
}
/* (non-Javadoc)
* @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlockAll()
*/
@Override
public void unlockAll() {
Collection<Vehicle> locked = new ArrayList<Vehicle>(lockedVehicles);
for(Vehicle v : locked){
unlock(v);
}
if(!lockedVehicles.isEmpty()){
throw new IllegalStateException("no vehicle must be locked");
}
}
public int sizeOfLockedVehicles(){
return lockedVehicles.size();
}
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.VehicleRoute;
/**
* Updater that updates a vehicleRoute, e.g. the total costs or the time-windows.
*
* @author stefan schroeder
*
*/
interface VehicleRouteUpdater {
public boolean updateRoute(VehicleRoute vehicleRoute);
}

View file

@ -0,0 +1,870 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import util.RouteUtils;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractInsertionKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.AcceptorKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.RuinStrategyKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.SelectorKey;
import algorithms.VehicleRoutingAlgorithms.TypedMap.StrategyModuleKey;
import algorithms.acceptors.AcceptNewIfBetterThanWorst;
import algorithms.acceptors.AcceptNewRemoveFirst;
import algorithms.acceptors.SchrimpfAcceptance;
import algorithms.acceptors.SolutionAcceptor;
import algorithms.selectors.SelectBest;
import algorithms.selectors.SelectRandomly;
import algorithms.selectors.SolutionSelector;
import basics.Job;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetSize;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.InsertionListener;
import basics.algo.SearchStrategy;
import basics.algo.SearchStrategyManager;
import basics.algo.SearchStrategyModule;
import basics.algo.SearchStrategyModuleListener;
import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
import basics.route.VehicleRoute;
public class VehicleRoutingAlgorithms {
static class TypedMap {
static interface AbstractKey<K> {
Class<K> getType();
}
static class AcceptorKey implements AbstractKey<SolutionAcceptor>{
private ModKey modKey;
public AcceptorKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AcceptorKey))
return false;
AcceptorKey other = (AcceptorKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SolutionAcceptor> getType() {
return SolutionAcceptor.class;
}
}
static class SelectorKey implements AbstractKey<SolutionSelector>{
private ModKey modKey;
public SelectorKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SelectorKey other = (SelectorKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SolutionSelector> getType() {
return SolutionSelector.class;
}
}
static class StrategyModuleKey implements AbstractKey<SearchStrategyModule>{
private ModKey modKey;
public StrategyModuleKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StrategyModuleKey other = (StrategyModuleKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<SearchStrategyModule> getType() {
return SearchStrategyModule.class;
}
}
static class RuinStrategyKey implements AbstractKey<RuinStrategy>{
private ModKey modKey;
public RuinStrategyKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RuinStrategyKey other = (RuinStrategyKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<RuinStrategy> getType() {
return RuinStrategy.class;
}
}
static class AbstractInsertionKey implements AbstractKey<AbstractInsertionStrategy>{
private ModKey modKey;
public AbstractInsertionKey(ModKey modKey) {
super();
this.modKey = modKey;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((modKey == null) ? 0 : modKey.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractInsertionKey other = (AbstractInsertionKey) obj;
if (modKey == null) {
if (other.modKey != null)
return false;
} else if (!modKey.equals(other.modKey))
return false;
return true;
}
@Override
public Class<AbstractInsertionStrategy> getType() {
return AbstractInsertionStrategy.class;
}
}
private Map<AbstractKey<?>, Object> map = new HashMap<AbstractKey<?>, Object>();
public <T> T get(AbstractKey<T> key) {
if(map.get(key) == null) return null;
return key.getType().cast(map.get(key));
}
public <T> T put(AbstractKey<T> key, T value) {
return key.getType().cast(map.put(key, key.getType().cast(value)));
}
public Set<AbstractKey<?>> keySet(){
return map.keySet();
}
}
static class ModKey {
private String name;
private String id;
public ModKey(String name, String id) {
super();
this.name = name;
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ModKey other = (ModKey) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
private static Logger log = Logger.getLogger(VehicleRoutingAlgorithms.class);
private VehicleRoutingAlgorithms(){}
public static VehicleRoutingAlgorithm createAlgorithm(final VehicleRoutingProblem vrp, final AlgorithmConfig algorithmConfig){
return createAlgo(vrp,algorithmConfig.getXMLConfiguration());
}
@Deprecated
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final XMLConfiguration config){
return createAlgo(vrp,config);
}
public static VehicleRoutingAlgorithm readAndCreateAlgorithm(final VehicleRoutingProblem vrp, final String configFileName){
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
AlgorithmConfigXmlReader xmlReader = new AlgorithmConfigXmlReader(algorithmConfig);
xmlReader.read(configFileName);
return createAlgo(vrp,algorithmConfig.getXMLConfiguration());
}
private static VehicleRoutingAlgorithm createAlgo(final VehicleRoutingProblem vrp, XMLConfiguration config){
//fleetmanager
final VehicleFleetManager vehicleFleetManager;
if(vrp.getFleetSize().equals(FleetSize.INFINITE)){
vehicleFleetManager = new InfiniteVehicles(vrp.getVehicles());
}
else if(vrp.getFleetSize().equals(FleetSize.FINITE)){
vehicleFleetManager = new VehicleFleetManagerImpl(vrp.getVehicles());
}
else{
throw new IllegalStateException("fleet size can only be infinite or finite. " +
"makes sure your config file contains one of these options");
}
Set<PrioritizedVRAListener> algorithmListeners = new HashSet<PrioritizedVRAListener>();
List<InsertionListener> insertionListeners = new ArrayList<InsertionListener>();
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, new SolutionVerifier()));
RouteStates routeStates = new RouteStates();
routeStates.initialiseStateOfJobs(vrp.getJobs().values());
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, routeStates));
TypedMap definedClasses = new TypedMap();
/*
* initial solution - construction
*/
AlgorithmStartsListener createInitialSolution = createInitialSolution(config,vrp,vehicleFleetManager,routeStates,algorithmListeners,definedClasses);
if(createInitialSolution != null) algorithmListeners.add(new PrioritizedVRAListener(Priority.MEDIUM, createInitialSolution));
int solutionMemory = config.getInt("strategy.memory");
SearchStrategyManager searchStratManager = new SearchStrategyManager();
List<HierarchicalConfiguration> strategyConfigs = config.configurationsAt("strategy.searchStrategies.searchStrategy");
for(HierarchicalConfiguration strategyConfig : strategyConfigs){
String name = getName(strategyConfig);
SolutionAcceptor acceptor = getAcceptor(strategyConfig,vrp,algorithmListeners,definedClasses,solutionMemory);
SolutionSelector selector = getSelector(strategyConfig,vrp,algorithmListeners,definedClasses);
SearchStrategy strategy = new SearchStrategy(selector, acceptor);
strategy.setName(name);
List<HierarchicalConfiguration> modulesConfig = strategyConfig.configurationsAt("modules.module");
for(HierarchicalConfiguration moduleConfig : modulesConfig){
SearchStrategyModule module = buildModule(moduleConfig,vrp,vehicleFleetManager,routeStates,algorithmListeners,definedClasses);
strategy.addModule(module);
}
searchStratManager.addStrategy(strategy, strategyConfig.getDouble("probability"));
}
VehicleRoutingAlgorithm metaAlgorithm = new VehicleRoutingAlgorithm(vrp, searchStratManager);
if(config.containsKey("iterations")){
metaAlgorithm.setNuOfIterations(config.getInt("iterations"));
}
registerListeners(metaAlgorithm,algorithmListeners);
registerInsertionListeners(definedClasses,insertionListeners);
return metaAlgorithm;
}
private static void registerInsertionListeners(TypedMap definedClasses, List<InsertionListener> insertionListeners) {
for(AbstractKey<?> key : definedClasses.keySet()){
if(key instanceof AbstractInsertionKey){
AbstractInsertionKey insertionKey = (AbstractInsertionKey) key;
AbstractInsertionStrategy insertionStrategy = definedClasses.get(insertionKey);
for(InsertionListener l : insertionListeners){
// log.info("add insertionListener " + l + " to " + insertionStrategy);
insertionStrategy.addListener(l);
}
}
}
// log.warn("cannot register insertion listeners yet");
}
private static String getName(HierarchicalConfiguration strategyConfig) {
if(strategyConfig.containsKey("[@name]")){
return strategyConfig.getString("[@name]");
}
return "";
}
private static void registerListeners(VehicleRoutingAlgorithm metaAlgorithm, Set<PrioritizedVRAListener> algorithmListeners) {
metaAlgorithm.getAlgorithmListeners().addAll(algorithmListeners);
}
private static AlgorithmStartsListener createInitialSolution(XMLConfiguration config, final VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, RouteStates activityStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses) {
List<HierarchicalConfiguration> modConfigs = config.configurationsAt("construction.insertion");
if(modConfigs == null) return null;
if(modConfigs.isEmpty()) return null;
if(modConfigs.size() != 1) throw new IllegalStateException("#construction.modules != 1. 1 expected");
HierarchicalConfiguration modConfig = modConfigs.get(0);
String insertionName = modConfig.getString("[@name]");
if(insertionName == null) throw new IllegalStateException("insertion[@name] is missing.");
String insertionId = modConfig.getString("[@id]");
if(insertionId == null) insertionId = "noId";
ModKey modKey = makeKey(insertionName,insertionId);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(modKey);
AbstractInsertionStrategy insertionStrategy = definedClasses.get(insertionStrategyKey);
if(insertionStrategy == null){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertionStrategy = createInsertionStrategy(modConfig, vrp, vehicleFleetManager, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
definedClasses.put(insertionStrategyKey,insertionStrategy);
}
final AbstractInsertionStrategy finalInsertionStrategy = insertionStrategy;
return new AlgorithmStartsListener() {
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
CreateInitialSolution createInitialSolution = new CreateInitialSolution(finalInsertionStrategy);
createInitialSolution.setGenerateAsMuchAsRoutesAsVehiclesExist(false);
VehicleRoutingProblemSolution vrpSol = createInitialSolution.createInitialSolution(vrp);
solutions.add(vrpSol);
}
};
}
private static SolutionSelector getSelector(HierarchicalConfiguration strategyConfig, VehicleRoutingProblem vrp, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedSelectors) {
String selectorName = strategyConfig.getString("selector[@name]");
if(selectorName == null) throw new IllegalStateException("no solutionSelector defined. define either \"selectRandom\" or \"selectBest\"");
String selectorId = strategyConfig.getString("selector[@id]");
if(selectorId == null) selectorId="noId";
ModKey modKey = makeKey(selectorName,selectorId);
SelectorKey selectorKey = new SelectorKey(modKey);
SolutionSelector definedSelector = definedSelectors.get(selectorKey);
if(definedSelector != null) {
return definedSelector;
}
if(selectorName.equals("selectRandom")){
SelectRandomly selector = SelectRandomly.getInstance();
definedSelectors.put(selectorKey, selector);
return selector;
}
if(selectorName.equals("selectBest")){
SelectBest selector = SelectBest.getInstance();
definedSelectors.put(selectorKey, selector);
return selector;
}
throw new IllegalStateException("solutionSelector is not know. Currently, it only knows \"selectRandom\" and \"selectBest\"");
}
private static ModKey makeKey(String name, String id){
return new ModKey(name, id);
}
private static SolutionAcceptor getAcceptor(HierarchicalConfiguration strategyConfig, VehicleRoutingProblem vrp, Set<PrioritizedVRAListener> algorithmListeners, TypedMap typedMap, int solutionMemory) {
String acceptorName = strategyConfig.getString("acceptor[@name]");
if(acceptorName == null) throw new IllegalStateException("no solution acceptor is defined");
String acceptorId = strategyConfig.getString("acceptor[@id]");
if(acceptorId == null) acceptorId = "noId";
AcceptorKey acceptorKey = new AcceptorKey(makeKey(acceptorName,acceptorId));
SolutionAcceptor definedAcceptor = typedMap.get(acceptorKey);
if(definedAcceptor != null) return definedAcceptor;
if(acceptorName.equals("acceptNewRemoveWorst")){
AcceptNewIfBetterThanWorst acceptor = new AcceptNewIfBetterThanWorst(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("acceptNewRemoveFirst")){
AcceptNewRemoveFirst acceptor = new AcceptNewRemoveFirst(solutionMemory);
typedMap.put(acceptorKey, acceptor);
return acceptor;
}
if(acceptorName.equals("schrimpfAcceptance")){
int iterOfSchrimpf = strategyConfig.getInt("acceptor.warmup");
double alpha = strategyConfig.getDouble("acceptor.alpha");
SchrimpfAcceptance schrimpf = new SchrimpfAcceptance(solutionMemory, alpha, iterOfSchrimpf);
algorithmListeners.add(new PrioritizedVRAListener(Priority.LOW, schrimpf));
typedMap.put(acceptorKey, schrimpf);
return schrimpf;
}
else{
throw new IllegalStateException("solution acceptor " + acceptorName + " is not known");
}
}
private static SearchStrategyModule buildModule(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager,
RouteStates activityStates, Set<PrioritizedVRAListener> algorithmListeners, TypedMap definedClasses) {
String moduleName = moduleConfig.getString("[@name]");
if(moduleName == null) throw new IllegalStateException("module(-name) is missing.");
String moduleId = moduleConfig.getString("[@id]");
if(moduleId == null) moduleId = "noId";
ModKey modKey = makeKey(moduleName,moduleId);
StrategyModuleKey strategyModuleKey = new StrategyModuleKey(modKey);
SearchStrategyModule definedModule = definedClasses.get(strategyModuleKey);
if(definedModule != null) return definedModule;
if(moduleName.equals("ruin_and_recreate")){
String ruin_name = moduleConfig.getString("ruin[@name]");
if(ruin_name == null) throw new IllegalStateException("module.ruin[@name] is missing.");
String ruin_id = moduleConfig.getString("ruin[@id]");
if(ruin_id == null) ruin_id = "noId";
String shareToRuinString = moduleConfig.getString("ruin.share");
if(shareToRuinString == null) throw new IllegalStateException("module.ruin.share is missing.");
double shareToRuin = Double.valueOf(shareToRuinString);
final RuinStrategy ruin;
ModKey ruinKey = makeKey(ruin_name,ruin_id);
if(ruin_name.equals("randomRuin")){
ruin = getRandomRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin);
}
else if(ruin_name.equals("radialRuin")){
ruin = getRadialRuin(vrp, activityStates, definedClasses, ruinKey, shareToRuin);
}
else throw new IllegalStateException("ruin[@name] " + ruin_name + " is not known. Use either randomRuin or radialRuin.");
String insertionName = moduleConfig.getString("insertion[@name]");
if(insertionName == null) throw new IllegalStateException("module.insertion[@name] is missing. set it to \"regretInsertion\" or \"bestInsertion\"");
String insertionId = moduleConfig.getString("insertion[@id]");
if(insertionId == null) insertionId = "noId";
ModKey insertionKey = makeKey(insertionName,insertionId);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(insertionKey);
AbstractInsertionStrategy insertion = definedClasses.get(insertionStrategyKey);
if(insertion == null){
List<HierarchicalConfiguration> insertionConfigs = moduleConfig.configurationsAt("insertion");
if(insertionConfigs.size() != 1) throw new IllegalStateException("this should be 1");
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
}
final AbstractInsertionStrategy final_insertion = insertion;
SearchStrategyModule module = new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
Collection<Job> ruinedJobs = ruin.ruin(vrpSolution.getRoutes());
final_insertion.run(vrpSolution.getRoutes(), ruinedJobs, Double.MAX_VALUE);
double totalCost = RouteUtils.getTotalCost(vrpSolution.getRoutes());
vrpSolution.setCost(totalCost);
return vrpSolution;
}
@Override
public String toString() {
return getName();
}
@Override
public String getName() {
return "[name=ruin_and_recreate][ruin="+ruin+"][recreate="+final_insertion+"]";
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
if(moduleListener instanceof InsertionListener){
InsertionListener iListener = (InsertionListener) moduleListener;
if(!final_insertion.getListener().contains(iListener)){
logger.info("register moduleListener " + moduleListener);
final_insertion.addListener(iListener);
}
}
}
};
return module;
}
if(moduleName.equals("bestInsertion") || moduleName.equals("regretInsertion")){
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
AbstractInsertionStrategy insertion = getInsertionStrategy(moduleConfig, vrp, vehicleFleetManager, activityStates,
definedClasses, modKey, prioListeners);
SearchStrategyModule module = getModule(moduleName, insertion, vrp);
definedClasses.put(strategyModuleKey, module);
algorithmListeners.addAll(prioListeners);
return module;
}
// if(moduleName.equals("regretInsertion")){
// List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
// AbstractInsertionKey insertionKey = new AbstractInsertionKey(modKey);
// AbstractInsertionStrategy regretInsertion = definedClasses.get(insertionKey);
// if(regretInsertion == null){
// regretInsertion = createInsertionStrategy(moduleConfig,vrp,vehicleFleetManager,activityStates,prioListeners);
// }
// SearchStrategyModule module = getModule(moduleName, regretInsertion, vrp);
// definedClasses.put(strategyModuleKey, module);
// algorithmListeners.addAll(prioListeners);
// return module;
// }
if(moduleName.equals("randomRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRandomRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
if(moduleName.equals("radialRuin")){
double shareToRuin = moduleConfig.getDouble("share");
RuinStrategy ruin = getRadialRuin(vrp, activityStates,definedClasses, modKey, shareToRuin);
SearchStrategyModule module = getModule(moduleName, ruin);
definedClasses.put(strategyModuleKey, module);
return module;
}
if(moduleName.equals("gendreauPostOpt")){
int iterations = moduleConfig.getInt("iterations");
double share = moduleConfig.getDouble("share");
String ruinName = moduleConfig.getString("ruin[@name]");
if(ruinName == null) throw new IllegalStateException("gendreauPostOpt.ruin[@name] is missing. set it to \"radialRuin\" or \"randomRuin\"");
String ruinId = moduleConfig.getString("ruin[@id]");
if(ruinId == null) ruinId = "noId";
ModKey ruinKey = makeKey(ruinName,ruinId);
RuinStrategyKey stratKey = new RuinStrategyKey(ruinKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRadial.newInstance(vrp, 0.3, new JobDistanceAvgCosts(vrp.getTransportCosts()), new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
String insertionName = moduleConfig.getString("insertion[@name]");
if(insertionName == null) throw new IllegalStateException("gendreauPostOpt.insertion[@name] is missing. set it to \"regretInsertion\" or \"bestInsertion\"");
String insertionId = moduleConfig.getString("insertion[@id]");
if(insertionId == null) insertionId = "noId";
ModKey insertionKey = makeKey(insertionName,insertionId);
AbstractInsertionKey insertionStrategyKey = new AbstractInsertionKey(insertionKey);
AbstractInsertionStrategy insertion = definedClasses.get(insertionStrategyKey);
if(insertion == null){
List<HierarchicalConfiguration> insertionConfigs = moduleConfig.configurationsAt("insertion");
if(insertionConfigs.size() != 1) throw new IllegalStateException("this should be 1");
List<PrioritizedVRAListener> prioListeners = new ArrayList<PrioritizedVRAListener>();
insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, activityStates, prioListeners);
algorithmListeners.addAll(prioListeners);
}
GendreauPostOpt postOpt = new GendreauPostOpt(vrp, ruin, insertion);
postOpt.setShareOfJobsToRuin(share);
postOpt.setNuOfIterations(iterations);
postOpt.setFleetManager(vehicleFleetManager);
definedClasses.put(strategyModuleKey, postOpt);
return postOpt;
}
throw new NullPointerException("no module found with moduleName=" + moduleName +
"\n\tcheck config whether the correct names are used" +
"\n\tcurrently there are following modules available: " +
"\n\tbestInsertion" +
"\n\trandomRuin" +
"\n\tradialRuin" +
"\n\tgendreauPostOpt");
}
private static AbstractInsertionStrategy getInsertionStrategy(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp,VehicleFleetManager vehicleFleetManager,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, List<PrioritizedVRAListener> prioListeners) {
AbstractInsertionKey insertionKey = new AbstractInsertionKey(modKey);
AbstractInsertionStrategy bestInsertion = definedClasses.get(insertionKey);
if(bestInsertion == null){
bestInsertion = createInsertionStrategy(moduleConfig,vrp,vehicleFleetManager,activityStates,prioListeners);
}
return bestInsertion;
}
private static RuinStrategy getRadialRuin(VehicleRoutingProblem vrp,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, double shareToRuin) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRadial.newInstance(vrp, shareToRuin, new JobDistanceAvgCosts(vrp.getTransportCosts()), new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static RuinStrategy getRandomRuin(VehicleRoutingProblem vrp,
RouteStates activityStates, TypedMap definedClasses,
ModKey modKey, double shareToRuin) {
RuinStrategyKey stratKey = new RuinStrategyKey(modKey);
RuinStrategy ruin = definedClasses.get(stratKey);
if(ruin == null){
ruin = RuinRandom.newInstance(vrp, shareToRuin, new JobRemoverImpl(), new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts()));
definedClasses.put(stratKey, ruin);
}
return ruin;
}
private static AbstractInsertionStrategy createInsertionStrategy(HierarchicalConfiguration moduleConfig, VehicleRoutingProblem vrp,VehicleFleetManager vehicleFleetManager, RouteStates activityStates, List<PrioritizedVRAListener> algorithmListeners) {
AbstractInsertionStrategy insertion = InsertionFactory.createInsertion(vrp, moduleConfig, vehicleFleetManager, activityStates, algorithmListeners);
return insertion;
}
private static SearchStrategyModule getModule(final String moduleName, final AbstractInsertionStrategy insertion, final VehicleRoutingProblem vrp){
return new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
List<Job> unassignedJobs = getUnassignedJobs(vrpSolution,vrp);
// logger.info("unassigned: " + unassignedJobs.size());
insertion.run(vrpSolution.getRoutes(), unassignedJobs, Double.MAX_VALUE);
int jobsInSolution = countJobs(vrpSolution);
double penalty = 0.0;
if(jobsInSolution != vrp.getJobs().values().size()){
throw new IllegalStateException("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("solution not valid\n" + "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
//// throw new IllegalStateException("solution not valid\n" +
//// "#jobsInSolution=" + jobsInSolution + " #jobsInVrp=" + vrp.getJobs().values().size());
// logger.warn("a penalty of 1000 is added for each unassigned customer");
// penalty = (vrp.getJobs().values().size() - jobsInSolution)*1000.0;
// logger.warn("penalty = " + penalty);
}
double totalCost = RouteUtils.getTotalCost(vrpSolution.getRoutes());
vrpSolution.setCost(totalCost + penalty);
return vrpSolution;
}
@Override
public String toString() {
return "[name="+insertion+"]";
}
private int countJobs(VehicleRoutingProblemSolution vrpSolution) {
int counter = 0;
for(VehicleRoute route : vrpSolution.getRoutes()){
counter += route.getTourActivities().jobSize();
}
return counter;
}
private List<Job> getUnassignedJobs(VehicleRoutingProblemSolution vrpSolution,VehicleRoutingProblem vrp) {
List<Job> unassignedJobs = new ArrayList<Job>();
for(Job j : vrp.getJobs().values()){
boolean notAssigned = true;
for(VehicleRoute r : vrpSolution.getRoutes()){
if(r.getTourActivities().servesJob(j)){
notAssigned = false;
break;
}
}
if(notAssigned){
unassignedJobs.add(j);
}
}
return unassignedJobs;
}
@Override
public String getName() {
return moduleName;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
if(moduleListener instanceof InsertionListener){
InsertionListener iListener = (InsertionListener) moduleListener;
if(!insertion.getListener().contains(iListener)){
logger.info("register moduleListener " + moduleListener);
insertion.addListener(iListener);
}
}
}
};
}
private static SearchStrategyModule getModule(final String moduleName, final RuinStrategy ruin) {
return new SearchStrategyModule() {
private Logger logger = Logger.getLogger(SearchStrategyModule.class);
@Override
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
ruin.ruin(vrpSolution.getRoutes());
return vrpSolution;
}
@Override
public String toString() {
return "[name="+ruin+"]";
}
@Override
public String getName() {
return moduleName;
}
@Override
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
// TODO Auto-generated method stub
}
};
}
}

View file

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms;
import basics.route.Vehicle;
import algorithms.RouteAlgorithm.VehicleSwitchedListener;
class VehicleSwitched implements VehicleSwitchedListener{
private VehicleFleetManager fleetManager;
VehicleSwitched(VehicleFleetManager fleetManager){
this.fleetManager = fleetManager;
}
@Override
public void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle) {
fleetManager.unlock(oldVehicle);
fleetManager.lock(newVehicle);
}
}

View file

@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class AcceptNewIfBetterThanWorst implements SolutionAcceptor{
private final int solutionMemory;
public AcceptNewIfBetterThanWorst(int solutionMemory){
this.solutionMemory = solutionMemory;
}
/**
* Accepts every solution if solution memory allows. If memory occupied, than accepts new solution only if better than the worst in memory.
* Consequently, the worst solution is removed from solutions, and the new solution added.
*
* <p>Note that this modifies Collection<VehicleRoutingProblemSolution> solutions.
*/
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
boolean solutionAccepted = false;
if (solutions.size() < solutionMemory) {
solutions.add(newSolution);
solutionAccepted = true;
} else {
VehicleRoutingProblemSolution worstSolution = null;
for (VehicleRoutingProblemSolution s : solutions) {
if (worstSolution == null) worstSolution = s;
else if (s.getCost() > worstSolution.getCost()) worstSolution = s;
}
if(newSolution.getCost() < worstSolution.getCost()){
solutions.remove(worstSolution);
solutions.add(newSolution);
solutionAccepted = true;
}
}
return solutionAccepted;
}
@Override
public String toString() {
return "[name=acceptNewRemoveWorst]";
}
}

View file

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class AcceptNewRemoveFirst implements SolutionAcceptor{
private final int solutionMemory;
public AcceptNewRemoveFirst(int solutionMemory){
this.solutionMemory = solutionMemory;
}
/**
* Accepts every solution if solution memory allows. If memory occupied, than accepts new solution only if better than the worst in memory.
* Consequently, the worst solution is removed from solutions, and the new solution added.
*
* <p>Note that this modifies Collection<VehicleRoutingProblemSolution> solutions.
*/
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
if (solutions.size() >= solutionMemory) {
solutions.remove(solutions.iterator().next());
}
solutions.add(newSolution);
return true;
}
@Override
public String toString() {
return "[name=acceptNewRemoveFirst]";
}
}

View file

@ -0,0 +1,155 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.net.URL;
import java.util.Collection;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.log4j.Logger;
import util.Solutions;
import algorithms.VehicleRoutingAlgorithms;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.algo.AlgorithmStartsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.io.AlgorithmConfig;
import basics.io.AlgorithmConfigXmlReader;
public class SchrimpfAcceptance implements SolutionAcceptor, IterationStartsListener, AlgorithmStartsListener{
private static Logger logger = Logger.getLogger(SchrimpfAcceptance.class);
private final double alpha;
private int nOfTotalIterations = 1000;
private int currentIteration = 0;
private double initialThreshold = 0.0;
private final int nOfRandomWalks;
private final int solutionMemory;
public SchrimpfAcceptance(int solutionMemory, double alpha, int nOfWarmupIterations) {
super();
this.alpha = alpha;
this.nOfRandomWalks = nOfWarmupIterations;
this.solutionMemory = solutionMemory;
logger.info("initialise " + this);
}
@Override
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution) {
boolean solutionAccepted = false;
if (solutions.size() < solutionMemory) {
solutions.add(newSolution);
solutionAccepted = true;
} else {
VehicleRoutingProblemSolution worst = null;
double threshold = getThreshold(currentIteration);
for(VehicleRoutingProblemSolution solutionInMemory : solutions){
if(worst == null) worst = solutionInMemory;
else if(solutionInMemory.getCost() > worst.getCost()) worst = solutionInMemory;
}
if(newSolution.getCost() < worst.getCost() + threshold){
solutions.remove(worst);
solutions.add(newSolution);
solutionAccepted = true;
}
}
return solutionAccepted;
}
@Override
public String toString() {
return "[name=schrimpfAcceptanceFunction][alpha="+alpha+"][warmup=" + nOfRandomWalks + "]";
}
private double getThreshold(int iteration) {
double scheduleVariable = (double) iteration / (double) nOfTotalIterations;
// logger.debug("iter="+iteration+" totalIter="+nOfTotalIterations+" scheduling="+scheduleVariable);
double currentThreshold = initialThreshold * Math.exp(-Math.log(2) * scheduleVariable / alpha);
return currentThreshold;
}
@Override
public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions) {
reset();
logger.info("---------------------------------------------------------------------");
logger.info("prepare schrimpfAcceptanceFunction, i.e. determine initial threshold");
logger.info("start random-walk (see algorith-config at scr/main/resources/randomWalk.xml)");
double now = System.currentTimeMillis();
this.nOfTotalIterations = algorithm.getNuOfIterations();
/*
* randomWalk to determine standardDev
*/
final double[] results = new double[nOfRandomWalks];
URL resource = this.getClass().getClassLoader().getResource("randomWalk.xml");
AlgorithmConfig algorithmConfig = new AlgorithmConfig();
new AlgorithmConfigXmlReader(algorithmConfig).read(resource.getPath());
VehicleRoutingAlgorithm vra = VehicleRoutingAlgorithms.createAlgorithm(problem, algorithmConfig);
vra.setNuOfIterations(nOfRandomWalks);
vra.getAlgorithmListeners().addListener(new IterationEndsListener() {
@Override
public void informIterationEnds(int iteration, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
double result = Solutions.getBest(solutions).getCost();
// logger.info("result="+result);
results[iteration-1] = result;
}
});
vra.searchSolutions();
StandardDeviation dev = new StandardDeviation();
double standardDeviation = dev.evaluate(results);
initialThreshold = standardDeviation / 2;
logger.info("warmup done");
logger.info("total time: " + ((System.currentTimeMillis()-now)/1000.0) + "s");
logger.info("initial threshold: " + initialThreshold);
logger.info("---------------------------------------------------------------------");
}
private void reset() {
currentIteration = 0;
}
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
currentIteration = i;
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.acceptors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
/**
* Acceptor that decides whether the newSolution is accepted or not.
*
*
* @author stefan
*
*/
public interface SolutionAcceptor {
/**
* Accepts solution or not, and returns true if a new solution has been accepted.
*
* <p>If the solution is accepted, it is added to solutions, i.e. the solutions-collections is modified.
*
* @param solutions
* @param newSolution
* @return TODO
*/
public boolean acceptSolution(Collection<VehicleRoutingProblemSolution> solutions, VehicleRoutingProblemSolution newSolution);
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public class SelectBest implements SolutionSelector{
private static SelectBest selector = null;
public static SelectBest getInstance(){
if(selector == null){
selector = new SelectBest();
return selector;
}
return selector;
}
@Override
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions) {
double minCost = Double.MAX_VALUE;
VehicleRoutingProblemSolution bestSolution = null;
for(VehicleRoutingProblemSolution sol : solutions){
if(bestSolution == null){
bestSolution = sol;
minCost = sol.getCost();
}
else if(sol.getCost() < minCost){
bestSolution = sol;
minCost = sol.getCost();
}
}
return bestSolution;
}
@Override
public String toString() {
return "[name=selectBest]";
}
}

View file

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import basics.VehicleRoutingProblemSolution;
import util.RandomNumberGeneration;
public class SelectRandomly implements SolutionSelector{
private static SelectRandomly selector = null;
public static SelectRandomly getInstance(){
if(selector == null){
selector = new SelectRandomly();
return selector;
}
return selector;
}
private Random random = RandomNumberGeneration.getRandom();
@Override
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions) {
if(solutions.isEmpty()) return null;
List<VehicleRoutingProblemSolution> solList = new ArrayList<VehicleRoutingProblemSolution>(solutions);
int randomIndex = random.nextInt(solutions.size());
return solList.get(randomIndex);
}
public void setRandom(Random random) {
this.random = random;
}
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package algorithms.selectors;
import java.util.Collection;
import basics.VehicleRoutingProblemSolution;
public interface SolutionSelector {
public VehicleRoutingProblemSolution selectSolution(Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
public interface Job {
public String getId();
public int getCapacityDemand();
}

View file

@ -0,0 +1,183 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import basics.route.TimeWindow;
import util.Coordinate;
public class Service implements Job {
public static class Builder {
public static Builder newInstance(String id, int size){
return new Builder(id,size);
}
private String id;
private String locationId;
private String name = "service";
private Coordinate coord;
private double serviceTime;
private TimeWindow timeWindow = TimeWindow.newInstance(0.0, Double.MAX_VALUE);
private int demand;
private Builder(String id, int size) {
super();
this.id = id;
this.demand = size;
}
public Builder setName(String name){
this.name = name;
return this;
}
public Builder setLocationId(String locationId){
this.locationId = locationId;
return this;
}
public Builder setCoord(Coordinate coord){
this.coord = coord;
return this;
}
public Builder setServiceTime(double serviceTime){
this.serviceTime = serviceTime;
return this;
}
public Builder setTimeWindow(TimeWindow tw){
this.timeWindow = tw;
return this;
}
public Service build(){
if(locationId == null) {
if(coord == null) throw new IllegalStateException("either locationId or a coordinate must be given. But is not.");
locationId = coord.toString();
}
return new Service(this);
}
}
private final String id;
private final String locationId;
private final String name;
private final Coordinate coord;
private final double serviceTime;
private final TimeWindow timeWindow;
private final int demand;
private Service(Builder builder){
id = builder.id;
locationId = builder.locationId;
coord = builder.coord;
serviceTime = builder.serviceTime;
timeWindow = builder.timeWindow;
demand = builder.demand;
name = builder.name;
}
@Override
public String getId() {
return id;
}
public String getLocationId() {
return locationId;
}
public Coordinate getCoord(){
return coord;
}
public double getServiceDuration() {
return serviceTime;
}
public TimeWindow getTimeWindow(){
return timeWindow;
}
@Override
public int getCapacityDemand() {
return demand;
}
/**
* @return the name
*/
public String getType() {
return name;
}
@Override
public String toString() {
return "[id=" + id + "][locationId=" + locationId + "][coord="+coord+"][size=" + demand + "][serviceTime=" + serviceTime + "][timeWindow=" + timeWindow + "]";
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Service other = (Service) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View file

@ -0,0 +1,202 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.log4j.Logger;
import util.Counter;
import algorithms.acceptors.SolutionAcceptor;
import basics.algo.AlgorithmEndsListener;
import basics.algo.AlgorithmStartsListener;
import basics.algo.IterationEndsListener;
import basics.algo.IterationStartsListener;
import basics.algo.SearchStrategy;
import basics.algo.SearchStrategyManager;
import basics.algo.VehicleRoutingAlgorithmListener;
import basics.algo.VehicleRoutingAlgorithmListeners;
/**
* Algorithm that solves a {@link VehicleRoutingProblem}.
*
* @author stefan schroeder
*
*/
public class VehicleRoutingAlgorithm {
public static final int NOBREAK = Integer.MAX_VALUE;
private static Logger logger = Logger.getLogger(VehicleRoutingAlgorithm.class);
private VehicleRoutingProblem problem;
private int nOfIterations = 100;
private int prematureBreak = NOBREAK;
private Counter counter = new Counter("iterations ");
private SearchStrategyManager searchStrategyManager;
private VehicleRoutingAlgorithmListeners algoListeners = new VehicleRoutingAlgorithmListeners();
private Collection<VehicleRoutingProblemSolution> initialSolutions;
public VehicleRoutingAlgorithm(VehicleRoutingProblem problem, SearchStrategyManager searchStrategyManager) {
super();
this.problem = problem;
this.searchStrategyManager = searchStrategyManager;
initialSolutions = new ArrayList<VehicleRoutingProblemSolution>();
}
public VehicleRoutingAlgorithm(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> initialSolutions, SearchStrategyManager searchStrategyManager) {
super();
this.problem = problem;
this.searchStrategyManager = searchStrategyManager;
this.initialSolutions = initialSolutions;
}
/**
* Adds solution to the collection of initial solutions.
*
* @param solution
*/
public void addInitialSolution(VehicleRoutingProblemSolution solution){
initialSolutions.add(solution);
}
/**
* Sets premature break.
*
* <p>This breaks the algorithm prematurely after the assigned number of iterations without improvement (see input parameter).
* Improvement is what {@link SolutionAcceptor} understands about improvement. Or to put it in other words, the algo breaks prematurely after
* the assigned number of iterations without solution-acceptance.
*
*
* @param iterationsWithoutImprovement
*/
public void setPrematureBreak(int iterationsWithoutImprovement){
prematureBreak = iterationsWithoutImprovement;
}
/**
* Gets the {@link SearchStrategyManager}.
*
* @return SearchStrategyManager
*/
public SearchStrategyManager getSearchStrategyManager() {
return searchStrategyManager;
}
/**
* Runs the vehicle routing algorithm and returns a number of generated solutions.
*
* <p>The algorithm runs as long as it is specified in nuOfIterations and prematureBreak. In each iteration it selects a searchStrategy according
* to searchStrategyManager and runs the strategy to improve solutions.
* <p>Note that clients are allowed to observe/listen the algorithm. See {@link VehicleRoutingAlgorithmListener} and its according listeners.
*
* @return Collection<VehicleRoutingProblemSolution> the solutions
* @see {@link SearchStrategyManager}, {@link VehicleRoutingAlgorithmListener}, {@link AlgorithmStartsListener}, {@link AlgorithmEndsListener}, {@link IterationStartsListener}, {@link IterationEndsListener}
*/
public Collection<VehicleRoutingProblemSolution> searchSolutions(){
logger.info("------------------------------------------------");
logger.info("algorithm starts");
double now = System.currentTimeMillis();
verify();
counter.reset();
Collection<VehicleRoutingProblemSolution> solutions = new ArrayList<VehicleRoutingProblemSolution>(initialSolutions);
algorithmStarts(problem,solutions);
int iterWithoutImprovement = 0;
logger.info("iterations start");
for(int i=0;i<nOfIterations;i++){
iterationStarts(i+1,problem,solutions);
counter.incCounter();
SearchStrategy strat = searchStrategyManager.getRandomStrategy();
boolean newSolutionFoundAndAccepted = strat.run(problem, solutions);
selectedStrategy(strat.getName(),problem, solutions);
if(newSolutionFoundAndAccepted) iterWithoutImprovement = 0;
else iterWithoutImprovement++;
if(iterWithoutImprovement > prematureBreak){
logger.info("premature break at iteration "+ (i+1));
break;
}
iterationEnds(i+1,problem,solutions);
}
logger.info("iterations end at " + nOfIterations + " iterations");
algorithmEnds(problem,solutions);
logger.info("total time: " + ((System.currentTimeMillis()-now)/1000.0) + "s");
logger.info("done");
logger.info("------------------------------------------------");
return solutions;
}
private void selectedStrategy(String name, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.selectedStrategy(name,problem, solutions);
}
/**
* Returns the number of iterations.
*
* @return iterations
*/
public int getNuOfIterations(){
return nOfIterations;
}
/**
* Asserts that the sum of probabilities of the searchStrategies is equal to 1.0.
*/
private void verify() {
double sum = 0.0;
for(Double prob : searchStrategyManager.getProbabilities()){
sum += prob;
}
if(sum < 1.0*0.99 || sum > 1.0*1.01) throw new IllegalStateException("sum of probabilities is not 1.0, but is "+ sum + ". make sure that the sum of the probability of each searchStrategy is 1.0");
}
private void algorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.algorithmEnds(problem, solutions);
}
public VehicleRoutingAlgorithmListeners getAlgorithmListeners() {
return algoListeners;
}
private void iterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.iterationEnds(i,problem, solutions);
}
private void iterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.iterationStarts(i, problem, solutions);
}
private void algorithmStarts(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
algoListeners.algorithmStarts(problem, this, solutions);
}
public void setNuOfIterations(int nOfIterations) {
this.nOfIterations = nOfIterations;
}
}

View file

@ -0,0 +1,473 @@
/*******************************************************************************
* Copyright (c) 2011 Stefan Schroeder.
* eMail: stefan.schroeder@kit.edu
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import util.Coordinate;
import util.CrowFlyCosts;
import util.Locations;
import util.Neighborhood;
import util.NeighborhoodImpl;
import basics.costs.DefaultVehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingActivityCosts;
import basics.costs.VehicleRoutingTransportCosts;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.VehicleType;
/**
* Contains and describes the vehicle routing problem.
*
* <p>A routing problem is defined as jobs, vehicles and costs.
*
* <p> To construct the problem, use VehicleRoutingProblem.Builder (VehicleRoutingProblem.Builder.newInstance()).
*
* <p>By default, fleetSize is INFINITE and fleetComposition is HOMOGENEOUS, transport-costs are calculated as euclidean-distance (CrowFlyCosts),
* and activity-costs are set to DefaultVehicleRoutingActivityCosts which represent hard time-windows (missed time-windows are penalyzed with Double.MAX_VALUE).
*
*
*
* @author stefan schroeder
*
*/
public class VehicleRoutingProblem {
/**
* Builder to build the routing-problem.
*
* @author stefan schroeder
*
*/
public static class Builder {
/**
* Returns a new instance of this builder.
*
* @return builder
*/
public static Builder newInstance(){ return new Builder(); }
private VehicleRoutingTransportCosts transportCosts;
private VehicleRoutingActivityCosts activityCosts = new DefaultVehicleRoutingActivityCosts();
private Map<String,Job> jobs;
private Collection<Vehicle> vehicles;
private Map<String, Coordinate> coordinates;
private FleetSize fleetSize = FleetSize.INFINITE;
private FleetComposition fleetComposition = FleetComposition.HOMOGENEOUS;
private Collection<VehicleType> vehicleTypes;
public Builder() {
jobs = new HashMap<String, Job>();
vehicles = new ArrayList<Vehicle>();
coordinates = new HashMap<String, Coordinate>();
vehicleTypes = new ArrayList<VehicleImpl.VehicleType>();
}
/**
* Create a location (i.e. coordinate) and returns the key of the location which is Coordinate.toString().
*
* @param x
* @param y
* @return locationId
* @see Coordinate
*/
public String createLocation(double x, double y){
Coordinate coord = new Coordinate(x, y);
String id = coord.toString();
if(!coordinates.containsKey(id)){
coordinates.put(id, coord);
}
return id;
}
/**
* Returns the unmodifiable map of locations (mapped by their id).
*
* @return
*/
public Map<String,Coordinate> getLocationMap(){
return Collections.unmodifiableMap(coordinates);
}
/**
* Returns the locations collected by this builder.
*
* <p>Locations are cached when adding a shipment, service, depot, vehicle.
*
* @return locations
*
*/
public Locations getLocations(){
return new Locations() {
@Override
public Coordinate getCoord(String id) {
return coordinates.get(id);
}
};
}
/**
* Sets routing costs.
*
* @param costs
* @return builder
* @see VehicleRoutingTransportCosts
*/
public Builder setRoutingCost(VehicleRoutingTransportCosts costs){
this.transportCosts = costs;
return this;
}
/**
* Sets the type of fleetSize.
*
* <p>FleetSize is either FleetSize.INFINITE or FleetSize.FINITE
*
* @param fleetSize
* @return
*/
public Builder setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
// if(fleetSize.equals(FleetSize.INFINITE)){
// fleetSizeIsInfinite=true;
// }
return this;
}
/**
* Sets the fleetComposition.
*
* <p>FleetComposition is either FleetComposition.HETEROGENEOUS or FleetComposition.HOMOGENEOUS
*
* @param fleetComposition
* @return
*/
public Builder setFleetComposition(FleetComposition fleetComposition){
this.fleetComposition = fleetComposition;
return this;
}
/**
* Adds a service to jobList.
*
* <p>If jobList already contains service, a warning message is printed, and the existing job will be overwritten.
*
* @param service
* @return
*/
public Builder addService(Service service){
coordinates.put(service.getLocationId(), service.getCoord());
if(jobs.containsKey(service.getId())){ logger.warn("service " + service + " already in job list. overrides existing job."); }
jobs.put(service.getId(),service);
return this;
}
/**
* Adds a job which is either a service or a shipment.
*
* @param job
* @return
* @throws IllegalStateException if job is neither a shipment or a service.
*/
public Builder addJob(Job job) {
if(job instanceof Service) {
addService((Service) job);
}
// else if(job instanceof Shipment){
// addShipment((Shipment)job);
// }
else throw new IllegalStateException("job can only be a shipment or a service, but is instance of " + job.getClass());
return this;
}
// private void addShipment(Shipment job) {
// coordinates.put(job.getFromId(),job.getFromCoord());
// coordinates.put(job.getToId(), job.getToCoord());
// if(jobs.containsKey(job.getId())){ logger.warn("service " + job + " already in job list. overrides existing job."); }
// jobs.put(job.getId(),job);
//
// }
/**
* Adds a vehicle.
*
*
* @param vehicle
* @return
*/
public Builder addVehicle(Vehicle vehicle) {
// fleetSizeIsFinite = true;
vehicles.add(vehicle);
if(!vehicleTypes.contains(vehicle.getType())){
vehicleTypes.add(vehicle.getType());
}
coordinates.put(vehicle.getLocationId(), vehicle.getCoord());
return this;
}
/**
* Adds a vehicleType.
*
* @param type
* @return builder
*/
public Builder addVehicleType(VehicleType type){
vehicleTypes.add(type);
return this;
}
/**
* Sets the activityCostFunction that considers also activities on a vehicle-route.
*
* <p>Here you can consider missed time-windows for example. By default, this is set to a DefaultVehicleActivityCostFunction.
*
* @param activityCosts
* @return
* @see VehicleRoutingTransportCosts, DefaultVehicleRouteCostFunction
*/
public Builder setActivityCosts(VehicleRoutingActivityCosts activityCosts){
this.activityCosts = activityCosts;
return this;
}
public VehicleRoutingProblem build() {
log.info("build problem ...");
if(transportCosts == null){
logger.warn("set routing costs crowFlyDistance.");
transportCosts = new CrowFlyCosts(getLocations());
}
return new VehicleRoutingProblem(this);
}
public Builder addLocation(String id, Coordinate coord) {
coordinates.put(id, coord);
return this;
}
public Builder addAllJobs(Collection<Job> jobs) {
for(Job j : jobs){
addJob(j);
}
return this;
}
public Builder addAllVehicles(Collection<Vehicle> vehicles) {
for(Vehicle v : vehicles){
addVehicle(v);
}
return this;
}
}
/**
* Enum that characterizes the fleet-size.
*
* @author sschroeder
*
*/
public static enum FleetSize {
FINITE, INFINITE;
}
/**
* Enum that characterizes fleet-compostion.
*
* @author sschroeder
*
*/
public static enum FleetComposition {
HETEROGENEOUS, HOMOGENEOUS;
}
public static Builder newBuilderInstance(){
return new Builder();
}
public static Logger log = Logger.getLogger(VehicleRoutingProblem.class);
private static Logger logger = Logger.getLogger(VehicleRoutingProblem.class);
private VehicleRoutingTransportCosts transportCosts;
private VehicleRoutingActivityCosts activityCosts;
private Neighborhood neighborhood;
/**
* @return the neighborhood
*/
public Neighborhood getNeighborhood() {
return neighborhood;
}
/**
* @param neighborhood the neighborhood to set
*/
public void setNeighborhood(Neighborhood neighborhood) {
this.neighborhood = neighborhood;
}
private final Map<String, Job> jobs;
/**
* Collection that contains available vehicles.
*/
private final Collection<Vehicle> vehicles;
/**
* Collection that contains all available types.
*/
private Collection<VehicleType> vehicleTypes;
/**
* An enum that indicates type of fleetSize. By default, it is INFINTE
*/
private FleetSize fleetSize = FleetSize.INFINITE;
/**
* An enum that indicates fleetSizeComposition. By default, it is HOMOGENOUS.
*/
private FleetComposition fleetComposition;
private VehicleRoutingProblem(Builder builder) {
this.jobs = builder.jobs;
this.fleetComposition = builder.fleetComposition;
this.fleetSize = builder.fleetSize;
this.vehicles=builder.vehicles;
this.vehicleTypes = builder.vehicleTypes;
this.transportCosts = builder.transportCosts;
this.activityCosts = builder.activityCosts;
this.neighborhood = new NeighborhoodImpl(this);
log.info("initialise " + this);
}
@Override
public String toString() {
return "[fleetSize="+fleetSize+"][fleetComposition="+fleetComposition+"][#jobs="+jobs.size()+"][#vehicles="+vehicles.size()+"][#vehicleTypes="+vehicleTypes.size()+"]["+
"transportCost="+transportCosts+"][activityCosts="+activityCosts+"]";
}
/**
* Returns fleet-composition.
*
* @return fleetComposition which is either FleetComposition.HETEROGENEOUS or FleetComposition.HOMOGENEOUS
*/
public FleetComposition getFleetComposition() {
return fleetComposition;
}
public void setFleetComposition(FleetComposition fleetComposition){
this.fleetComposition = fleetComposition;
}
/**
* Returns type of fleetSize, either INFINITE or FINITE.
*
* <p>By default, it is INFINITE.
*
* @return either FleetSize.INFINITE or FleetSize.FINITE
*/
public FleetSize getFleetSize() {
return fleetSize;
}
public void setFleetSize(FleetSize fleetSize){
this.fleetSize = fleetSize;
}
/**
* Returns the unmodifiable job map.
*
* @return unmodifiable jobMap
*/
public Map<String, Job> getJobs() {
return Collections.unmodifiableMap(jobs);
}
/**
* Returns the entire, unmodifiable collection of types.
*
* @return unmodifiable collection of types
* @see VehicleType
*/
public Collection<VehicleType> getTypes(){
return Collections.unmodifiableCollection(vehicleTypes);
}
/**
* Returns the entire, unmodifiable collection of vehicles.
*
* @return unmodifiable collection of vehicles
* @see Vehicle
*/
public Collection<Vehicle> getVehicles() {
return Collections.unmodifiableCollection(vehicles);
}
/**
* Returns routing costs.
*
* @return routingCosts
* @see VehicleRoutingTransportCosts
*/
public VehicleRoutingTransportCosts getTransportCosts() {
return transportCosts;
}
/**
* Sets routing costs.
*
* @param costs
* @see VehicleRoutingTransportCosts
*/
public void setTransportCosts(VehicleRoutingTransportCosts costs) {
this.transportCosts = costs;
logger.info("transport costs set to " + costs.getClass());
}
/**
* Returns activityCosts.
*/
public VehicleRoutingActivityCosts getActivityCosts(){
return activityCosts;
}
/**
* Sets activityCosts.
*/
public void setActivityCosts(VehicleRoutingActivityCosts activityCosts){
this.activityCosts = activityCosts;
logger.info("activtiy costs set to " + activityCosts.getClass());
}
}

View file

@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics;
import java.util.ArrayList;
import java.util.Collection;
import basics.route.VehicleRoute;
/**
* Contains the solution of a vehicle routing problem and its corresponding costs.
*
* @author stefan schröder
*
*/
public class VehicleRoutingProblemSolution {
public static double NO_COST_YET = -9999.0;
/**
* Makes a deep copy of the solution to be copied.
*
* @param solution2copy
* @return
*/
public static VehicleRoutingProblemSolution copyOf(VehicleRoutingProblemSolution solution2copy){
return new VehicleRoutingProblemSolution(solution2copy);
}
private final Collection<VehicleRoute> routes;
private double cost;
private VehicleRoutingProblemSolution(VehicleRoutingProblemSolution solution){
routes = new ArrayList<VehicleRoute>();
for(VehicleRoute r : solution.getRoutes()){
VehicleRoute route = VehicleRoute.copyOf(r);
routes.add(route);
}
this.cost = solution.getCost();
}
/**
* Constructs a solution with a number of {@link VehicleRoute}s and their corresponding aggregate cost value.
*
* @param routes
* @param cost
*/
public VehicleRoutingProblemSolution(Collection<VehicleRoute> routes, double cost) {
super();
this.routes = routes;
this.cost = cost;
}
public Collection<VehicleRoute> getRoutes() {
return routes;
}
public double getCost() {
return cost;
}
public void setCost(double cost){
this.cost = cost;
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface AlgorithmEndsListener extends VehicleRoutingAlgorithmListener{
void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingAlgorithm;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface AlgorithmStartsListener extends VehicleRoutingAlgorithmListener{
void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.route.VehicleRoute;
public interface InsertionEndsListener extends InsertionListener {
public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes);
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public interface InsertionListener extends SearchStrategyModuleListener{
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.route.VehicleRoute;
public interface InsertionStartsListener extends InsertionListener {
public void informInsertionStarts(Collection<VehicleRoute> vehicleRoutes, int nOfJobs2Recreate);
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface IterationEndsListener extends VehicleRoutingAlgorithmListener{
public void informIterationEnds(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface IterationStartsListener extends VehicleRoutingAlgorithmListener{
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import basics.Job;
import basics.route.VehicleRoute;
public interface JobInsertedListener extends InsertionListener{
public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn);
}

View file

@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.apache.log4j.Logger;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import algorithms.acceptors.SolutionAcceptor;
import algorithms.selectors.SolutionSelector;
public class SearchStrategy {
private static Logger logger = Logger.getLogger(SearchStrategy.class);
private Collection<SearchStrategyModule> searchStrategyModules = new ArrayList<SearchStrategyModule>();
private SolutionSelector solutionSelector;
private SolutionAcceptor solutionAcceptor;
private String name;
public SearchStrategy(SolutionSelector solutionSelector, SolutionAcceptor solutionAcceptor) {
super();
this.solutionSelector = solutionSelector;
this.solutionAcceptor = solutionAcceptor;
logger.info("initialise " + this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<SearchStrategyModule> getSearchStrategyModules() {
return Collections.unmodifiableCollection(searchStrategyModules);
}
public SolutionSelector getSolutionSelector() {
return solutionSelector;
}
public SolutionAcceptor getSolutionAcceptor() {
return solutionAcceptor;
}
@Override
public String toString() {
return "searchStrategy [#modules="+searchStrategyModules.size()+"][selector="+solutionSelector+"][acceptor="+solutionAcceptor+"]";
}
/**
* Runs the search-strategy and its according modules, and returns true if a new solution has been accepted.
*
* <p>This involves three basic steps: 1) Selecting a solution from solutions (input parameter) according to {@link SolutionSelector}, 2) running the modules
* ({@link SearchStrategyModule}) on the selectedSolution and 3) accepting the new solution according to {@link SolutionAcceptor}.
* <p> Note that after 1) the selected solution is copied, thus the original solution is not modified.
* <p> Note also that 3) modifies the input parameter solutions by adding, removing, replacing existing solutions.
*
* @param vrp
* @param solutions which will be modified
* @return boolean true if solution has been accepted, false otherwise
* @see SolutionSelector, SearchStrategyModule, SolutionAcceptor
*/
public boolean run(VehicleRoutingProblem vrp, Collection<VehicleRoutingProblemSolution> solutions){
VehicleRoutingProblemSolution solution = solutionSelector.selectSolution(solutions);
if(solution == null) throw new IllegalStateException("solution is null. check solutionSelector to return an appropiate solution.");
VehicleRoutingProblemSolution lastSolution = VehicleRoutingProblemSolution.copyOf(solution);
for(SearchStrategyModule module : searchStrategyModules){
VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(lastSolution);
lastSolution = newSolution;
}
boolean solutionAccepted = solutionAcceptor.acceptSolution(solutions, lastSolution);
return solutionAccepted;
}
public void addModule(SearchStrategyModule module){
if(module == null) throw new IllegalStateException("module to be added is null.");
searchStrategyModules.add(module);
logger.info("module added [module="+module+"][#modules="+searchStrategyModules.size()+"]");
}
public void addModuleListener(SearchStrategyModuleListener moduleListener) {
for(SearchStrategyModule module : searchStrategyModules){
module.addModuleListener(moduleListener);
}
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public class SearchStrategyListener {
}

View file

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import util.RandomNumberGeneration;
public class SearchStrategyManager {
private List<SearchStrategyListener> searchStrategyListeners = new ArrayList<SearchStrategyListener>();
private List<SearchStrategy> strategies = new ArrayList<SearchStrategy>();
private List<Double> probabilities = new ArrayList<Double>();
private Random random = RandomNumberGeneration.getRandom();
private double sumOfProbabilities = 0;
public void setRandom(Random random) {
this.random = random;
}
public List<SearchStrategy> getStrategies() {
return Collections.unmodifiableList(strategies);
}
public List<Double> getProbabilities() {
return Collections.unmodifiableList(probabilities);
}
/**
* adds a new search strategy. the probability must be within [0,1].
* @param strategy
* @param probability
*/
public void addStrategy(SearchStrategy strategy, double probability){
if(strategy == null){
throw new IllegalStateException("strategy is null. make sure adding a valid strategy.");
}
if(probability > 1.0){
throw new IllegalStateException("probability is higher than one, but it must be within [0,1].");
}
if(probability < 0.0){
throw new IllegalStateException("probability is lower than zero, but it must be within [0,1].");
}
strategies.add(strategy);
probabilities.add(probability);
sumOfProbabilities += probability;
if(sumOfProbabilities > 1.0){
throw new IllegalStateException("total probability of all strategies is higher than one, but it must be within [0,1].");
}
}
public SearchStrategy getRandomStrategy() {
if(random == null) throw new IllegalStateException("randomizer is null. make sure you set random object correctly");
double randomFig = random.nextDouble();
double sumWeight = 0.0;
for (int i = 0; i < probabilities.size(); i++) {
sumWeight += probabilities.get(i);
if (randomFig < sumWeight) {
return strategies.get(i);
}
}
throw new IllegalStateException("no seaarch-strategy found");
}
public void addSearchStrategyListener(SearchStrategyListener strategyListener){
searchStrategyListeners.add(strategyListener);
}
public void addSearchStrategyModuleListener(SearchStrategyModuleListener moduleListener){
for(SearchStrategy s : strategies){
s.addModuleListener(moduleListener);
}
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import basics.VehicleRoutingProblemSolution;
public interface SearchStrategyModule {
public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution);
public String getName();
public void addModuleListener(SearchStrategyModuleListener moduleListener);
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
public interface SearchStrategyModuleListener {
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (C) 2013 Stefan Schroeder
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributors:
* Stefan Schroeder - initial API and implementation
******************************************************************************/
package basics.algo;
import java.util.Collection;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
public interface StrategySelectedListener extends VehicleRoutingAlgorithmListener{
void informSelectedStrategy(String strategyName, VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions);
}

Some files were not shown because too many files have changed in this diff Show more