Archived
0

change graph implementation

This commit is contained in:
iMoHax
2015-05-16 17:25:40 +03:00
parent 42a958ecb2
commit 0fd99b0ac2
12 changed files with 1275 additions and 2 deletions

View File

@@ -0,0 +1,23 @@
package ru.trader.analysis;
public class AnalysisCallBack {
private volatile boolean cancel = false;
public String getMessage(String key){return "";}
public void startStage(String id){}
public void setMax(long count){}
public void inc(){}
public void print(String message){}
public void endStage(String id){}
public final boolean isCancel() {
return cancel;
}
public final void cancel(){
this.cancel = true;
}
}

View File

@@ -0,0 +1,111 @@
package ru.trader.analysis.graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.trader.analysis.AnalysisCallBack;
import ru.trader.core.Profile;
import ru.trader.graph.Connectable;
import java.util.Collection;
import java.util.function.Predicate;
public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class);
private final Profile profile;
public ConnectibleGraph(Profile profile) {
super();
this.profile = profile;
}
public ConnectibleGraph(Profile profile, AnalysisCallBack callback) {
super(callback);
this.profile = profile;
}
@Override
protected GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
return new ConnectibleGraphBuilder(vertex, set, deep, limit);
}
public void build(T start, Collection<T> set){
super.build(start, set, profile.getJumps(), profile.getShip().getTank());
}
private class DistanceFilter implements Predicate<Double> {
private final double limit;
private final T source;
private DistanceFilter(double limit, T source) {
this.limit = limit;
this.source = source;
}
@Override
public boolean test(Double distance) {
return distance <= profile.getShip().getJumpRange(limit) || (profile.withRefill() && distance <= profile.getShip().getJumpRange() && source.canRefill());
}
}
private class ConnectibleGraphBuilder extends GraphBuilder {
private final DistanceFilter distanceFilter;
protected boolean refill;
private ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
super(vertex, set, deep, limit);
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
}
@Override
protected double onConnect(T entry) {
double distance = vertex.getEntry().getDistance(entry);
if (!distanceFilter.test(distance)){
LOG.trace("Vertex {} is far away, {}", entry, distance);
return -1;
}
double costFuel = profile.getShip().getFuelCost(limit, distance);
double nextLimit = profile.withRefill() ? limit - costFuel : profile.getShip().getTank();
if (nextLimit < 0) {
LOG.trace("Refill");
refill = true;
nextLimit = profile.getShip().getTank() - profile.getShip().getFuelCost(distance);
} else {
refill = false;
}
return nextLimit;
}
@Override
protected ConnectibleEdge createEdge(Vertex<T> target) {
return new ConnectibleEdge(vertex, target, refill);
}
}
protected class ConnectibleEdge extends Edge<T> {
private final boolean refill;
protected ConnectibleEdge(Vertex<T> source, Vertex<T> target, boolean refill) {
super(source, target);
this.refill = refill;
}
public boolean isRefill() {
return refill;
}
@Override
protected double computeWeight() {
T s = source.getEntry();
T t = target.getEntry();
return s.getDistance(t);
}
@Override
public String toString() {
return source.getEntry().toString() + " - "+ weight
+ (refill ? "R" : "")
+" -> " + target.getEntry().toString();
}
}
}

View File

@@ -0,0 +1,291 @@
package ru.trader.analysis.graph;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;
public class Crawler<T> {
private final static ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = 4;
private final static Logger LOG = LoggerFactory.getLogger(Crawler.class);
private final Graph<T> graph;
private final Consumer<List<Edge<T>>> onFoundFunc;
public Crawler(Graph<T> graph, Consumer<List<Edge<T>>> onFoundFunc) {
this.graph = graph;
this.onFoundFunc = onFoundFunc;
}
private List<Edge<T>> getCopyList(List<Edge<T>> head, Edge<T> tail){
List<Edge<T>> res = new ArrayList<>(20);
res.addAll(head);
res.add(tail);
return res;
}
public void findFast(T target){
findFast(target, 1);
}
public void findFast(T target, int count){
Vertex<T> t = graph.getVertex(target);
int found = 0;
if (t != null) {
if (count > 1) {
found = bfs(new ArrayList<>(), graph.root, target, 0, count);
} else {
found = dfs(new ArrayList<>(), graph.root, target, t.getLevel() + 1, count);
}
}
LOG.debug("Found {} paths", found);
}
public void findMin(T target){
findMin(target, 1);
}
public void findMin(T target, int count){
Vertex<T> t = graph.getVertex(target);
int found = 0;
if (t != null) {
found = ucs(new ArrayList<>(), graph.root, target, 0, count);
}
LOG.debug("Found {} paths", found);
}
private int dfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head);
int found = 0;
if (deep == source.getLevel()){
Optional<Edge<T>> last = source.getEdges().parallelStream()
.filter(next -> next.isConnect(target))
.findFirst();
if (last.isPresent()){
List<Edge<T>> res = getCopyList(head, last.get());
LOG.debug("Last edge find, path {}", res);
onFoundFunc.accept(res);
found++;
}
}
if (found < count){
if (deep < source.getLevel()) {
LOG.trace("Search around");
for (Edge<T> edge : source.getEdges()) {
if (edge.getTarget().isSingle()) continue;
found += dfs(getCopyList(head, edge), edge.getTarget(), target, deep, count-found);
if (found >= count) break;
}
}
}
return found;
}
private int bfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
LOG.trace("BFS from {} to {}, deep {}, count {}", source, target, deep, count);
int found = 0;
LinkedList<TraversalEntry> queue = new LinkedList<>();
queue.add(new TraversalEntry(head, source));
while (!queue.isEmpty() && count > found){
TraversalEntry entry = queue.poll();
head = entry.head;
source = entry.vertex;
LOG.trace("Search from {} to {}, head {}", source, target, head);
source.sortEdges();
for (Edge<T> edge : source.getEdges()) {
if (edge.isConnect(target)){
List<Edge<T>> res = getCopyList(head, edge);
LOG.debug("Last edge find, path {}", res);
onFoundFunc.accept(res);
found++;
}
if (found >= count) break;
if (edge.getTarget().isSingle()) continue;
if (deep < source.getLevel()) {
queue.add(new TraversalEntry(getCopyList(head, edge), edge.getTarget()));
}
}
}
return found;
}
private int ucs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
LOG.trace("UCS from {} to {}, deep {}, count {}", source, target, deep, count);
int found = 0;
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
queue.add(new CostTraversalEntry(head, source));
while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.poll();
LOG.trace("Check path head {}, edge {}, cost {}", entry.head, entry.edge, entry.cost);
head = entry.head;
Edge<T> edge = entry.edge;
if (edge != null) {
source = edge.getSource();
if (edge.isConnect(target)) {
List<Edge<T>> res = getCopyList(head, edge);
LOG.debug("Path found {}", res);
onFoundFunc.accept(res);
found++;
if (found >= count) break;
}
if (edge.getTarget().isSingle() || deep >= source.getLevel()){
continue;
}
head = getCopyList(entry.head, edge);
}
Iterator<Edge<T>> iterator = entry.iterator;
//put only 2 entry for iterate
while (iterator.hasNext()){
edge = iterator.next();
if (deep < source.getLevel() && !edge.getTarget().isSingle() || edge.isConnect(target)) {
LOG.trace("Add edge {} to queue", edge);
queue.add(new CostTraversalEntry(head, edge, entry.cost));
}
}
}
return found;
}
//last edge don't compare
private int ucs2(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
LOG.trace("UCS2 from {} to {}, deep {}, count {}", source, target, deep, count);
int found = 0;
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
source.sortEdges();
queue.add(new CostTraversalEntry(head, source));
while (!queue.isEmpty() && count > found){
CostTraversalEntry entry = queue.peek();
head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head;
Iterator<Edge<T>> iterator = entry.iterator;
LOG.trace("Check path head {}, cost {}", head, entry.cost);
int i = 0;
//put only 2 entry for iterate
while (iterator.hasNext() && i < 2){
Edge<T> edge = iterator.next();
LOG.trace("Check edge {}", edge);
if (edge.isConnect(target)) {
List<Edge<T>> res = getCopyList(head, edge);
LOG.debug("Last edge find, path {}", res);
onFoundFunc.accept(res);
found++;
}
if (found >= count) break;
if (edge.getTarget().isSingle()) continue;
if (deep < source.getLevel()) {
edge.getTarget().sortEdges();
queue.add(new CostTraversalEntry(head, edge, entry.cost));
i++;
}
}
if (!iterator.hasNext()) queue.poll();
}
return found;
}
private class TraversalEntry {
private final List<Edge<T>> head;
private final Vertex<T> vertex;
private TraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
this.head = head;
this.vertex = vertex;
}
}
private class CostTraversalEntry implements Comparable<CostTraversalEntry>{
private final List<Edge<T>> head;
private final Edge<T> edge;
private final Iterator<Edge<T>> iterator;
private final double cost;
private CostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
this.head = head;
this.iterator = vertex.getEdges().iterator();
this.edge = null;
this.cost = 0;
}
private CostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost) {
this.head = head;
this.edge = edge;
this.iterator = edge.getTarget().getEdges().iterator();
this.cost = cost + edge.getWeight();
}
@Override
public int compareTo(@NotNull CostTraversalEntry other) {
int cmp = Double.compare(cost, other.cost);
if (cmp != 0) return cmp;
return Integer.compare(head.size(), other.head.size());
}
}
/*
private class PathFinder extends RecursiveAction {
private final TopList<Path<T>> paths;
private final Path<T> head;
private final Vertex<T> target;
private PathFinder(TopList<Path<T>> paths, Path<T> head, Vertex<T> target) {
this.paths = paths;
this.head = head;
this.target = target;
}
@Override
protected void compute() {
if (target == null || isCancelled()) return;
Vertex<T> source = head.getTarget();
LOG.trace("Find path to deep from {} to {}, head {}", source, target, head);
Edge<T> edge = source.getEdge(target);
if (edge != null){
Path<T> path = head.connectTo(edge.getTarget(), limit < edge.getLength());
path.finish();
LOG.trace("Last edge find, add path {}", path);
synchronized (paths){
if (!paths.add(path)) complete(null);
}
callback.onFound();
}
if (!source.isSingle()){
LOG.trace("Search around");
ArrayList<PathFinder> subTasks = new ArrayList<>(source.getEdges().size());
Iterator<Edge<T>> iterator = source.getEdges().iterator();
while (iterator.hasNext()) {
Edge<T> next = iterator.next();
if (isDone() || callback.isCancel()) break;
// target already added if source consist edge
if (next.isConnect(target)) continue;
Path<T> path = head.connectTo(next.getTarget(), limit < next.getLength());
//Recursive search
PathFinder task = new PathFinder(paths, path, target);
task.fork();
subTasks.add(task);
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
for (PathFinder subTask : subTasks) {
if (isDone() || callback.isCancel()) {
subTask.cancel(callback.isCancel());
} else {
subTask.join();
}
}
subTasks.clear();
}
}
if (!subTasks.isEmpty()){
for (PathFinder subTask : subTasks) {
if (isDone() || callback.isCancel()) {
subTask.cancel(callback.isCancel());
} else {
subTask.join();
}
}
subTasks.clear();
}
}
}
}*/
}

View File

@@ -0,0 +1,60 @@
package ru.trader.analysis.graph;
import org.jetbrains.annotations.NotNull;
public abstract class Edge<T> implements Comparable<Edge>{
protected Double weight;
protected final Vertex<T> target;
protected final Vertex<T> source;
protected Edge(Vertex<T> source, Vertex<T> target) {
this.target = target;
this.source = source;
}
protected abstract double computeWeight();
public Vertex<T> getTarget(){
return target;
}
public Vertex<T> getSource() {
return source;
}
public double getWeight(){
if (weight == null){
weight = computeWeight();
}
return weight;
}
public boolean isConnect(T other){
return target.getEntry().equals(other);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Edge edge = (Edge) o;
return source.equals(edge.source) && target.equals(edge.target);
}
@Override
public int hashCode() {
int result = target.hashCode();
result = 31 * result + source.hashCode();
return result;
}
@Override
public int compareTo(@NotNull Edge other) {
return Double.compare(getWeight(), other.getWeight());
}
@Override
public String toString() {
return source.getEntry().toString() + " - "+ weight +" -> " + target.getEntry().toString();
}
}

View File

@@ -0,0 +1,144 @@
package ru.trader.analysis.graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.trader.analysis.AnalysisCallBack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
public abstract class Graph<T> {
private final static ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = 4;
private final static Logger LOG = LoggerFactory.getLogger(Graph.class);
protected Vertex<T> root;
protected final Map<T, Vertex<T>> vertexes;
private final GraphCallBack callback;
protected int minJumps;
protected Graph() {
this(new AnalysisCallBack());
}
protected Graph(AnalysisCallBack callback) {
this.callback = new GraphCallBack(callback);
vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD);
}
protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
public void build(T start, Collection<T> set, int maxDeep, double limit) {
callback.startBuild(start);
root = getInstance(start, maxDeep);
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
if (set.size() > vertexes.size()){
minJumps = maxDeep;
} else {
minJumps = 1;
for (Vertex<T> vertex : vertexes.values()) {
int jumps = maxDeep - vertex.getLevel();
if (jumps > minJumps) minJumps = jumps;
}
}
callback.endBuild();
}
private Vertex<T> getInstance(T entry, int deep){
Vertex<T> vertex = new Vertex<>(entry);
vertex.setLevel(deep);
Vertex<T> old = vertexes.get(entry);
if (old == null || old.getLevel() < deep) {
LOG.trace("Is top vertex");
vertexes.put(entry, vertex);
}
return vertex;
}
public boolean isAccessible(T entry){
return vertexes.containsKey(entry);
}
public Vertex<T> getVertex(T entry){
return vertexes.get(entry);
}
public T getRoot() {
return root.getEntry();
}
public int getMinJumps() {
return minJumps;
}
protected abstract class GraphBuilder extends RecursiveAction {
protected final Vertex<T> vertex;
protected final Collection<T> set;
protected final int deep;
protected final double limit;
protected GraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
this.vertex = vertex;
this.set = set;
this.deep = deep;
this.limit = limit;
}
protected abstract double onConnect(T entry);
protected abstract Edge<T> createEdge(Vertex<T> target);
@Override
protected void compute() {
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
ArrayList<GraphBuilder> subTasks = new ArrayList<>(THRESHOLD);
Iterator<T> iterator = set.iterator();
while (iterator.hasNext()) {
if (callback.isCancel()) break;
T entry = iterator.next();
if (entry == vertex.getEntry()) continue;
double nextLimit = onConnect(entry);
if (nextLimit >= 0) {
LOG.trace("Connect {} to {}", vertex, entry);
Vertex<T> next = getInstance(entry, vertex.getLevel() - 1);
vertex.connect(createEdge(next));
if (deep > 0) {
//Recursive build
GraphBuilder task = createGraphBuilder(next, set, deep - 1, nextLimit);
task.fork();
subTasks.add(task);
}
} else {
LOG.trace("Vertex {} is far away", entry);
}
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
for (GraphBuilder subTask : subTasks) {
if (callback.isCancel()){
subTask.cancel(true);
} else {
subTask.join();
}
}
subTasks.clear();
}
}
if (!subTasks.isEmpty()){
for (GraphBuilder subTask : subTasks) {
if (callback.isCancel()){
subTask.cancel(true);
} else {
subTask.join();
}
}
subTasks.clear();
}
LOG.trace("End build graph from {} on deep {}", vertex, deep);
}
}
}

View File

@@ -0,0 +1,25 @@
package ru.trader.analysis.graph;
import ru.trader.analysis.AnalysisCallBack;
public class GraphCallBack {
private final AnalysisCallBack parent;
public final String BUILD_STAGE = "graph.stage.build";
public GraphCallBack(AnalysisCallBack parent) {
this.parent = parent;
}
public void startBuild(Object entry){
parent.startStage(BUILD_STAGE);
parent.print(String.format(parent.getMessage(BUILD_STAGE), entry.toString()));
}
public void endBuild(){
parent.endStage(BUILD_STAGE);
}
public boolean isCancel(){
return parent.isCancel();
}
}

View File

@@ -0,0 +1,82 @@
package ru.trader.analysis.graph;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Vertex<T> {
private final ArrayList<Edge<T>> edges = new ArrayList<>();
private final T entry;
private volatile int level = -1;
public Vertex(T entry) {
this.entry = entry;
}
public T getEntry() {
return entry;
}
public boolean isEntry(T entry){
return this.entry.equals(entry);
}
void setLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
public void connect(Edge<T> edge){
assert this == edge.getSource();
synchronized (edges){
edges.add(edge);
}
}
public List<Edge<T>> getEdges() {
return edges;
}
public Optional<Edge<T>> getEdge(Vertex<T> target) {
return getEdge(target.entry);
}
public Optional<Edge<T>> getEdge(T target) {
return edges.stream().filter((e) -> e.isConnect(target)).findFirst();
}
public void sortEdges(){
edges.sort(Comparator.<Edge<T>>naturalOrder());
}
public boolean isConnected(T other){
return getEdge(other).isPresent();
}
public boolean isSingle(){
return edges.size() == 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vertex vertex = (Vertex) o;
return entry.equals(vertex.entry);
}
@Override
public int hashCode() {
return entry.hashCode();
}
@Override
public String toString() {
return "Vertex{" + entry + ", lvl=" + level + '}';
}
}