/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator 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 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator 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.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.util.ai.aco;
import archimulator.util.event.BlockingEvent;
import archimulator.util.event.BlockingEventDispatcher;
import archimulator.util.event.CycleAccurateEventQueue;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.math3.ml.distance.EuclideanDistance;
import org.jgrapht.graph.ListenableUndirectedWeightedGraph;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Ant Colony Optimization (ACO) helper.
*
* @author Min Cai
*/
public class ACOHelper {
private BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher;
private CycleAccurateEventQueue cycleAccurateEventQueue;
private ListenableUndirectedWeightedGraph<Node, Edge> graph;
private double p;
private double delta;
private double maxPheromone;
private double alpha;
private double beta;
private List<Node> nodes;
private List<Edge> edges;
private List<Ant> ants;
private List<Node> shortestPath;
private double shortestPathCost;
/**
* Create an Ant Colony Optimization (ACO) helper.
*
* @param blockingEventDispatcher the blocking event dispatcher
* @param cycleAccurateEventQueue the cycle accurate event queue
* @param p the p value
* @param delta the delta value
* @param maxPheromone the max pheromone value
* @param alpha the alpha value
* @param beta the beta value
*/
public ACOHelper(BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher, CycleAccurateEventQueue cycleAccurateEventQueue, double p, double delta, double maxPheromone, double alpha, double beta) {
this.blockingEventDispatcher = blockingEventDispatcher;
this.cycleAccurateEventQueue = cycleAccurateEventQueue;
this.graph = new ListenableUndirectedWeightedGraph<>(Edge.class);
this.p = p;
this.delta = delta;
this.maxPheromone = maxPheromone;
this.alpha = alpha;
this.beta = beta;
this.nodes = new ArrayList<>();
this.edges = new ArrayList<>();
this.ants = new ArrayList<>();
this.shortestPath = null;
this.shortestPathCost = Double.MAX_VALUE;
this.blockingEventDispatcher.addListener(PathGeneratedEvent.class, event -> {
if (event.getPathCost() < shortestPathCost) {
shortestPath = event.getPath();
shortestPathCost = event.getPathCost();
blockingEventDispatcher.dispatch(new ShortestPathGeneratedEvent(event.getAnt(), event.getPath(), event.getPathEdges(), event.getPathCost()));
}
});
}
/**
* Create an ACO helper from the specified file.
*
* @param blockingEventDispatcher the blocking event dispatcher
* @param cycleAccurateEventQueue the cycle accurate event queue
* @param fileName the file name
* @param p the p value
* @param delta the delta value
* @param maxPheromone the max pheromone value
* @param alpha the alpha value
* @param beta the beta value
* @return the newly created ACO helper
*/
public static ACOHelper read(BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher, CycleAccurateEventQueue cycleAccurateEventQueue, String fileName, double p, double delta, double maxPheromone, double alpha, double beta) {
try {
ACOHelper acoHelper = new ACOHelper(blockingEventDispatcher, cycleAccurateEventQueue, p, delta, maxPheromone, alpha, beta);
List<String> lines = IOUtils.readLines(new FileReader(fileName));
for (String line : lines) {
String[] parts = line.split(" ");
if (parts.length == 3 && NumberUtils.isNumber(parts[0].trim()) && NumberUtils.isNumber(parts[1].trim()) && NumberUtils.isNumber(parts[2].trim())) {
int i = Integer.parseInt(parts[0].trim());
double x = Double.parseDouble(parts[1].trim());
double y = Double.parseDouble(parts[2].trim());
Node node = new Node(acoHelper, "" + i, x, y);
acoHelper.getGraph().addVertex(node);
acoHelper.getNodes().add(node);
}
}
for (Node nodeFrom : acoHelper.getNodes()) {
acoHelper.getNodes().stream().filter(nodeTo -> nodeFrom != nodeTo && acoHelper.getEdge(nodeFrom, nodeTo) == null).forEach(nodeTo -> {
Edge edge = new Edge(acoHelper, nodeFrom, nodeTo, 1, euclideanDistance.compute(new double[]{nodeFrom.getX(), nodeFrom.getY()}, new double[]{nodeTo.getX(), nodeTo.getY()}));
acoHelper.getGraph().addEdge(nodeFrom, nodeTo, edge);
acoHelper.getEdges().add(edge);
});
}
return acoHelper;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Get the edge for the specified source and destination nodes.
*
* @param nodeFrom the source node
* @param nodeTo the destination node
* @return the edge for the specified source and destination nodes
*/
public Edge getEdge(Node nodeFrom, Node nodeTo) {
return this.getGraph().getEdge(nodeFrom, nodeTo);
}
/**
* Get the blocking event dispatcher.
*
* @return the blocking event dispatcher
*/
public BlockingEventDispatcher<BlockingEvent> getBlockingEventDispatcher() {
return blockingEventDispatcher;
}
/**
* Get the cycle accurate event queue.
*
* @return the cycle accurate event queue
*/
public CycleAccurateEventQueue getCycleAccurateEventQueue() {
return cycleAccurateEventQueue;
}
/**
* Get the p value.
*
* @return the p value
*/
public double getP() {
return p;
}
/**
* Get the delta value.
*
* @return the delta value
*/
public double getDelta() {
return delta;
}
/**
* Get the max pheromone value.
*
* @return the max pheromone value
*/
public double getMaxPheromone() {
return maxPheromone;
}
/**
* Get the alpha value.
*
* @return the alpha value
*/
public double getAlpha() {
return alpha;
}
/**
* Get the beta value.
*
* @return the beta value
*/
public double getBeta() {
return beta;
}
/**
* Get the graph.
*
* @return the graph
*/
public ListenableUndirectedWeightedGraph<Node, Edge> getGraph() {
return graph;
}
/**
* Get the list of nodes.
*
* @return the list of nodes
*/
public List<Node> getNodes() {
return nodes;
}
/**
* Get the list of edges.
*
* @return the list of edges
*/
public List<Edge> getEdges() {
return edges;
}
/**
* Get the list of ants.
*
* @return the list of ants
*/
public List<Ant> getAnts() {
return ants;
}
/**
* Get the list of edges composing the shortest path.
*
* @return the list of edges composing the shortest path
*/
public List<Node> getShortestPath() {
return shortestPath;
}
/**
* Get the cost of the shortest path.
*
* @return the cost of the shortest path
*/
public double getShortestPathCost() {
return shortestPathCost;
}
@Override
public String toString() {
return String.format("ACOHelper{p=%s, delta=%s, maxPheromone=%s, alpha=%s, beta=%s}", p, delta, maxPheromone, alpha, beta);
}
private static EuclideanDistance euclideanDistance = new EuclideanDistance();
/**
* Entry point.
*
* @param args the arguments
*/
public static void main(String[] args) {
BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher = new BlockingEventDispatcher<>();
CycleAccurateEventQueue cycleAccurateEventQueue = new CycleAccurateEventQueue();
ACOHelper acoHelper = read(blockingEventDispatcher, cycleAccurateEventQueue, "src/main/java/archimulator/util/ai/aco/berlin52.tsp", 0.1, 0.5, 1, 0.5, 0.5);
cycleAccurateEventQueue.getPerCycleEvents().add(() -> acoHelper.getEdges().forEach(Edge::evaporate));
for (int i = 0; i < acoHelper.getNodes().size(); i++) {
acoHelper.getAnts().add(new Ant(acoHelper, "" + i, acoHelper.getNodes().get(i)));
}
GraphStreamViewer viewer = new GraphStreamViewer(acoHelper);
acoHelper.getBlockingEventDispatcher().addListener(ShortestPathGeneratedEvent.class, event -> {
viewer.getGraph().getEdgeSet().forEach(e -> e.removeAttribute("ui.class"));
event.getPathEdges().stream()
.map(edge -> (org.graphstream.graph.Edge) viewer.getGraph().getEdge(edge.getNodeFrom().getName() + "-" + edge.getNodeTo().getName()))
.forEach(e -> e.addAttribute("ui.class", "shortestPath"));
System.out.println("New shortest path found: " + event.getPath().stream().map(Node::getName).collect(Collectors.toList()));
System.out.println("New shortest path of edges found: " + event.getPathEdges().stream().map(Edge::getName).collect(Collectors.toList()));
System.out.println("New shortest path cost found: " + event.getPathCost());
});
for (; ; ) {
cycleAccurateEventQueue.advanceOneCycle();
}
}
}