don't use frok join on build graph
fix multi thread search
This commit is contained in:
@@ -6,6 +6,9 @@ import ru.trader.analysis.graph.Traversal;
|
||||
public interface RouteSpecification<T> {
|
||||
|
||||
public boolean specified(Edge<T> edge, Traversal<T> entry);
|
||||
public default boolean updateSpecified(Edge<T> edge, Traversal<T> entry){
|
||||
return specified(edge, entry);
|
||||
}
|
||||
|
||||
default RouteSpecification<T> and(final RouteSpecification<T> other){
|
||||
return (edge, entry) -> RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry);
|
||||
|
||||
@@ -7,7 +7,6 @@ import ru.trader.analysis.graph.*;
|
||||
import ru.trader.core.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -62,7 +61,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
private void runDeferredTasks(){
|
||||
deferredTasks.sort((b1,b2) -> Integer.compare(b2.getDeep(), b1.getDeep()));
|
||||
for (VendorsGraphBuilder task : deferredTasks) {
|
||||
ForkJoinTask.invokeAll(task);
|
||||
task.compute();
|
||||
}
|
||||
deferredTasks.clear();
|
||||
}
|
||||
@@ -104,54 +103,45 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void build() {
|
||||
protected void compute() {
|
||||
if (isAdding){
|
||||
if (!vertex.locker().tryLock()){
|
||||
throw new ConcurrentModificationException("Adding must do in single thread");
|
||||
} else {
|
||||
try {
|
||||
addAlreadyCheckedEdges();
|
||||
} finally {
|
||||
vertex.locker().unlock();
|
||||
}
|
||||
}
|
||||
addAlreadyCheckedEdges();
|
||||
} else {
|
||||
super.build();
|
||||
super.compute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double checkConnect(Vendor buyer) {
|
||||
double nextlimit = super.checkConnect(buyer);
|
||||
Vendor seller = vertex.getEntry();
|
||||
if (nextlimit > 0){
|
||||
protected BuildHelper<Vendor> createHelper(Vendor buyer) {
|
||||
BuildHelper<Vendor> helper = super.createHelper(buyer);
|
||||
if (helper.isConnected()){
|
||||
Vendor seller = vertex.getEntry();
|
||||
if (buyer instanceof TransitVendor && (deep == 0 || seller.getPlace().equals(buyer.getPlace()))){
|
||||
LOG.trace("Buyer is transit of seller or is end, skipping");
|
||||
nextlimit = -1;
|
||||
return new BuildHelper<>(buyer, -1);
|
||||
}
|
||||
if (seller instanceof TransitVendor && seller.getPlace().equals(buyer.getPlace())){
|
||||
LOG.trace("Seller is transit of buyer, skipping");
|
||||
nextlimit = -1;
|
||||
return new BuildHelper<>(buyer, -1);
|
||||
}
|
||||
|
||||
}
|
||||
return nextlimit;
|
||||
return helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(Vertex<Vendor> next, double nextLimit) {
|
||||
BuildEdge e;
|
||||
protected void connect(Edge<Vendor> edge) {
|
||||
if (edge instanceof VendorsBuildEdge){
|
||||
super.connect(edge);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildEdge createEdge(BuildHelper<Vendor> helper, Vertex<Vendor> next) {
|
||||
BuildEdge cEdge = super.createEdge(helper, next);
|
||||
if (next.getEntry() instanceof TransitVendor){
|
||||
e = super.createEdge(next);
|
||||
} else {
|
||||
e = createEdge(next);
|
||||
vertex.connect(e);
|
||||
return cEdge;
|
||||
}
|
||||
addSubTask(e, nextLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VendorsBuildEdge createEdge(Vertex<Vendor> target) {
|
||||
BuildEdge cEdge = super.createEdge(target);
|
||||
if (vertex.getEntry() instanceof TransitVendor){
|
||||
addEdgesToHead(cEdge);
|
||||
}
|
||||
@@ -193,8 +183,8 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
|
||||
private void addAlreadyCheckedEdges(){
|
||||
LOG.trace("Adding already checked vertex");
|
||||
vertex.getEdges().parallelStream().forEach(aEdge -> {
|
||||
VendorsBuildEdge e = (VendorsBuildEdge) aEdge;
|
||||
vertex.getEdges().parallelStream().forEach(edge -> {
|
||||
VendorsBuildEdge e = (VendorsBuildEdge) edge;
|
||||
if (callback.isCancel()) return;
|
||||
Vendor entry = e.getTarget().getEntry();
|
||||
LOG.trace("Check {}", entry);
|
||||
@@ -247,25 +237,22 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
}
|
||||
}
|
||||
|
||||
private void addSubTask(BuildEdge e, double nextLimit){
|
||||
Vertex<Vendor> next = e.getTarget();
|
||||
// If level > deep when vertex already added on upper deep
|
||||
if (next.getLevel() < deep || next.getEntry() instanceof TransitVendor) {
|
||||
boolean adding = next.getLevel() >= deep;
|
||||
if (deep > 0 || adding) {
|
||||
//Recursive build
|
||||
VendorsGraphBuilder task = new VendorsGraphBuilder(this, e, set, deep - 1, nextLimit);
|
||||
task.isAdding = adding;
|
||||
if (adding){
|
||||
holdTask(task);
|
||||
} else {
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
@Override
|
||||
protected GraphBuilder createSubTask(Edge<Vendor> edge, Collection<Vendor> set, int deep, double limit) {
|
||||
return new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSubTask(Edge<Vendor> edge, double nextLimit) {
|
||||
Vertex<Vendor> next = edge.getTarget();
|
||||
if (next.getLevel() >= deep && next.getEntry() instanceof TransitVendor) {
|
||||
if (deep > 0){
|
||||
VendorsGraphBuilder task = new VendorsGraphBuilder(this, (BuildEdge) edge, set, deep - 1, nextLimit);
|
||||
task.isAdding = true;
|
||||
holdTask(task);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Vertex {} already check", next);
|
||||
}
|
||||
super.addSubTask(edge, nextLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,7 +520,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
protected boolean check(Edge<Vendor> e){
|
||||
VendorsBuildEdge edge = (VendorsBuildEdge) e;
|
||||
return fuel <= edge.getMaxFuel() && (fuel >= edge.getMinFuel() || edge.getSource().getEntry().canRefill())
|
||||
&& (edge.getProfit() > 0 || isFound(edge, this));
|
||||
&& (edge.getProfit() > 0 || VendorsCrawler.this.isFound(edge, this));
|
||||
}
|
||||
|
||||
protected VendorsEdge wrap(Edge<Vendor> e) {
|
||||
|
||||
@@ -5,21 +5,17 @@ import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
//TODO: make it worked in multi thread
|
||||
private final static int THRESHOLD = 1;
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class);
|
||||
|
||||
protected Vertex<T> root;
|
||||
protected final List<Vertex<T>> vertexes;
|
||||
protected final GraphCallBack callback;
|
||||
protected int minJumps;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
protected AbstractGraph() {
|
||||
this(new AnalysisCallBack());
|
||||
@@ -27,16 +23,17 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
|
||||
protected AbstractGraph(AnalysisCallBack callback) {
|
||||
this.callback = new GraphCallBack(callback);
|
||||
vertexes = new CopyOnWriteArrayList<>();
|
||||
vertexes = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected abstract RecursiveAction createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
|
||||
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);
|
||||
minJumps = 1;
|
||||
root = getInstance(start, maxDeep, maxDeep);
|
||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||
GraphBuilder builder = createGraphBuilder(root, set, maxDeep - 1, limit);
|
||||
builder.compute();
|
||||
onEnd();
|
||||
callback.endBuild();
|
||||
}
|
||||
@@ -49,21 +46,22 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
return new Vertex<>(entry, index);
|
||||
}
|
||||
|
||||
protected Vertex<T> getInstance(T entry, int level, int deep){
|
||||
Vertex<T> vertex = getVertex(entry).orElse(null);
|
||||
if (vertex == null) {
|
||||
synchronized (vertexes){
|
||||
vertex = getVertex(entry).orElse(null);
|
||||
if (vertex == null){
|
||||
LOG.trace("Is new vertex");
|
||||
vertex = newInstance(entry, vertexes.size());
|
||||
vertex.setLevel(level);
|
||||
vertexes.add(vertex);
|
||||
int jumps = root != null ? root.getLevel() - deep : 0;
|
||||
if (jumps > minJumps)
|
||||
minJumps = jumps;
|
||||
}
|
||||
private Vertex<T> getInstance(T entry, int level, int deep){
|
||||
Vertex<T> vertex = null;
|
||||
lock.lock();
|
||||
try {
|
||||
vertex = getVertex(entry).orElse(null);
|
||||
if (vertex == null){
|
||||
LOG.trace("Is new vertex");
|
||||
vertex = newInstance(entry, vertexes.size());
|
||||
vertexes.add(vertex);
|
||||
vertex.setLevel(level);
|
||||
int jumps = root != null ? root.getLevel() - deep : 0;
|
||||
if (jumps > minJumps)
|
||||
minJumps = jumps;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
@@ -103,8 +101,7 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
return vertexes.size();
|
||||
}
|
||||
|
||||
protected abstract class GraphBuilder extends RecursiveAction {
|
||||
protected final List<RecursiveAction> subTasks = new ArrayList<>(THRESHOLD);
|
||||
protected abstract class GraphBuilder {
|
||||
protected final Vertex<T> vertex;
|
||||
protected final Collection<T> set;
|
||||
protected final int deep;
|
||||
@@ -117,70 +114,89 @@ public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
protected abstract double checkConnect(T entry);
|
||||
protected abstract Edge<T> createEdge(Vertex<T> target);
|
||||
protected RecursiveAction createSubTask(Vertex<T> vertex, Collection<T> set, int deep, double limit){
|
||||
return createGraphBuilder(vertex, set, deep, limit);
|
||||
protected BuildHelper<T> createHelper(final T entry){
|
||||
return new BuildHelper<>(entry, limit);
|
||||
}
|
||||
protected abstract Edge<T> createEdge(BuildHelper<T> helper, Vertex<T> target);
|
||||
protected void connect(Edge<T> edge){
|
||||
vertex.connect(edge);
|
||||
}
|
||||
protected GraphBuilder createSubTask(Edge<T> edge, Collection<T> set, int deep, double limit){
|
||||
return createGraphBuilder(edge.getTarget(), set, deep, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void compute() {
|
||||
vertex.locker().lock();
|
||||
try {
|
||||
if (vertex.getLevel() <= deep){
|
||||
vertex.setLevel(deep+1);
|
||||
protected void compute() {
|
||||
List<BuildHelper<T>> helpers;
|
||||
if (vertex.getLevel() <= deep){
|
||||
vertex.setLevel(deep+1);
|
||||
} else {
|
||||
if (vertex.getLevel() > deep+1){
|
||||
LOG.trace("Already build");
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
vertex.locker().unlock();
|
||||
}
|
||||
build();
|
||||
helpers = build();
|
||||
runSubTasks(helpers);
|
||||
}
|
||||
|
||||
protected void build(){
|
||||
private List<BuildHelper<T>> build(){
|
||||
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
for (T entry : set) {
|
||||
if (callback.isCancel()) break;
|
||||
if (entry == vertex.getEntry()) continue;
|
||||
double nextLimit = checkConnect(entry);
|
||||
if (nextLimit >= 0) {
|
||||
LOG.trace("Connect {} to {}", vertex, entry);
|
||||
Vertex<T> next = getInstance(entry, 0, deep);
|
||||
connect(next, nextLimit);
|
||||
} else {
|
||||
LOG.trace("Vertex {} is far away", entry);
|
||||
}
|
||||
if (subTasks.size() >= THRESHOLD) {
|
||||
joinSubTasks();
|
||||
}
|
||||
}
|
||||
if (!subTasks.isEmpty()){
|
||||
joinSubTasks();
|
||||
}
|
||||
List<BuildHelper<T>> helpers = set.parallelStream()
|
||||
.filter(entry -> entry != vertex.getEntry())
|
||||
.map(this::createHelper)
|
||||
.filter(BuildHelper::isConnected)
|
||||
.collect(Collectors.toList());
|
||||
helpers.parallelStream().forEach(this::connect);
|
||||
LOG.trace("End build graph from {} on deep {}", vertex, deep);
|
||||
return helpers;
|
||||
}
|
||||
|
||||
protected void connect(Vertex<T> next, double nextLimit){
|
||||
vertex.connect(createEdge(next));
|
||||
private void connect(final BuildHelper<T> helper){
|
||||
LOG.trace("Connect {} to {}", vertex, helper.entry);
|
||||
Vertex<T> next = getInstance(helper.entry, 0, deep);
|
||||
Edge<T> edge = createEdge(helper, next);
|
||||
helper.setEdge(edge);
|
||||
connect(edge);
|
||||
}
|
||||
|
||||
private void runSubTasks(final List<BuildHelper<T>> helpers){
|
||||
LOG.trace("Build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
for (BuildHelper<T> helper : helpers) {
|
||||
if (callback.isCancel()) break;
|
||||
addSubTask(helper.edge, helper.nextLimit);
|
||||
}
|
||||
LOG.trace("End build sub graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
}
|
||||
|
||||
protected void addSubTask(Edge<T> edge, double nextLimit){
|
||||
Vertex<T> next = edge.getTarget();
|
||||
if (next.getLevel() < deep) {
|
||||
if (deep > 0) {
|
||||
//Recursive build
|
||||
RecursiveAction task = createSubTask(next, set, deep - 1, nextLimit);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
GraphBuilder task = createSubTask(edge, set, deep - 1, nextLimit);
|
||||
task.compute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void joinSubTasks(){
|
||||
for (RecursiveAction subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
protected class BuildHelper<T> {
|
||||
private final T entry;
|
||||
private final double nextLimit;
|
||||
private Edge<T> edge;
|
||||
|
||||
public BuildHelper(T entry, double nextLimit) {
|
||||
this.entry = entry;
|
||||
this.nextLimit = nextLimit;
|
||||
this.edge = null;
|
||||
}
|
||||
|
||||
private void setEdge(Edge<T> edge) {
|
||||
this.edge = edge;
|
||||
}
|
||||
|
||||
public boolean isConnected(){
|
||||
return nextLimit >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,33 +42,32 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
|
||||
}
|
||||
|
||||
protected class ConnectibleGraphBuilder extends GraphBuilder {
|
||||
protected double minFuel;
|
||||
protected double maxFuel;
|
||||
protected double distance;
|
||||
|
||||
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
super(vertex, set, deep, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double checkConnect(T entry) {
|
||||
distance = vertex.getEntry().getDistance(entry);
|
||||
protected BuildHelper<T> createHelper(T entry) {
|
||||
double distance = vertex.getEntry().getDistance(entry);
|
||||
if (distance > getShip().getMaxJumpRange()){
|
||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||
return -1;
|
||||
return new BuildHelper<>(entry,-1);
|
||||
}
|
||||
maxFuel = getShip().getMaxFuel(distance);
|
||||
minFuel = getShip().getMinFuel(distance);
|
||||
double maxFuel = getShip().getMaxFuel(distance);
|
||||
double minFuel = getShip().getMinFuel(distance);
|
||||
double fuel = getProfile().withRefill() ? vertex.getEntry().canRefill() ? getShip().getRoundMaxFuel(distance) : limit : getShip().getTank();
|
||||
double fuelCost = getShip().getFuelCost(fuel, distance);
|
||||
return fuel - fuelCost;
|
||||
double nextLimit = getProfile().withRefill() ? fuel - fuelCost : fuel;
|
||||
return new CBuildHelper<>(entry, nextLimit, minFuel, maxFuel, distance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BuildEdge createEdge(Vertex<T> target) {
|
||||
protected BuildEdge createEdge(BuildHelper<T> helper, Vertex<T> target) {
|
||||
CBuildHelper h = (CBuildHelper) helper;
|
||||
BuildEdge res = new BuildEdge(vertex, target);
|
||||
res.setFuel(minFuel, maxFuel);
|
||||
res.setDistance(distance);
|
||||
res.setFuel(h.minFuel, h.maxFuel);
|
||||
res.setDistance(h.distance);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -132,4 +131,19 @@ public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CBuildHelper<T> extends BuildHelper<T> {
|
||||
private final double minFuel;
|
||||
private final double maxFuel;
|
||||
private final double distance;
|
||||
|
||||
private CBuildHelper(T entry, double nextLimit, double minFuel, double maxFuel, double distance) {
|
||||
super(entry, nextLimit);
|
||||
this.minFuel = minFuel;
|
||||
this.maxFuel = maxFuel;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,11 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
protected boolean isFound(Edge<T> edge, Traversal<T> head){
|
||||
return specification.specified(edge, head);
|
||||
return isFound(edge, head, false);
|
||||
}
|
||||
|
||||
private boolean isFound(Edge<T> edge, Traversal<T> head, boolean updateStates){
|
||||
return updateStates ? specification.updateSpecified(edge, head) : specification.specified(edge, head);
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
@@ -100,7 +104,7 @@ public class Crawler<T> {
|
||||
if (maxDeep < 0) maxDeep = 0;
|
||||
found = bfs(start(s), maxDeep, count);
|
||||
} else {
|
||||
found = dfs(start(s), Math.min(t.get().getLevel() + 1, s.getLevel()), count);
|
||||
found = dfs(start(s), t.get().getLevel() + 1, count);
|
||||
}
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
@@ -158,7 +162,7 @@ public class Crawler<T> {
|
||||
boolean stop = false;
|
||||
if (deep == source.getLevel()){
|
||||
for (Edge<T> next : entry.getEdges()) {
|
||||
if (isFound(next, entry)){
|
||||
if (isFound(next, entry, true)){
|
||||
List<Edge<T>> res = getCopyList(entry, next);
|
||||
LOG.debug("Last edge found, path {}", res);
|
||||
found++;
|
||||
@@ -170,9 +174,13 @@ public class Crawler<T> {
|
||||
}
|
||||
}
|
||||
if (!stop && found < count){
|
||||
if (deep <= source.getLevel() && entry.size() < maxSize-1) {
|
||||
if (deep < source.getLevel() && entry.size() < maxSize-1) {
|
||||
LOG.trace("Search around");
|
||||
for (Edge<T> edge : entry.getEdges()) {
|
||||
if (entry.isSkipped()){
|
||||
LOG.trace("Is skipped");
|
||||
break;
|
||||
}
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
found += dfs(travers(entry, edge), deep, count-found);
|
||||
if (found >= count) break;
|
||||
@@ -190,6 +198,10 @@ public class Crawler<T> {
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
if (entry.isSkipped()){
|
||||
LOG.trace("Is skipped");
|
||||
continue;
|
||||
}
|
||||
Vertex<T> source = entry.vertex;
|
||||
if (entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
@@ -199,7 +211,7 @@ public class Crawler<T> {
|
||||
Iterator<Edge<T>> iterator = entry.iterator();
|
||||
while (iterator.hasNext()){
|
||||
Edge<T> edge = iterator.next();
|
||||
if (isFound(edge, entry)){
|
||||
if (isFound(edge, entry, true)){
|
||||
List<Edge<T>> res = getCopyList(entry, edge);
|
||||
LOG.debug("Last edge found, path {}", res);
|
||||
found++;
|
||||
@@ -207,6 +219,10 @@ public class Crawler<T> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entry.isSkipped()){
|
||||
LOG.trace("Is skipped");
|
||||
break;
|
||||
}
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
@@ -226,10 +242,14 @@ public class Crawler<T> {
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
if (entry.isSkipped()){
|
||||
LOG.trace("Is skipped");
|
||||
continue;
|
||||
}
|
||||
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
|
||||
Edge<T> edge = entry.getEdge();
|
||||
if (edge != null) {
|
||||
if (isFound(edge, entry)) {
|
||||
if (isFound(edge, entry, true)) {
|
||||
List<Edge<T>> res = entry.toEdges();
|
||||
LOG.debug("Path found {}", res);
|
||||
found++;
|
||||
@@ -238,6 +258,10 @@ public class Crawler<T> {
|
||||
}
|
||||
if (found >= count) break;
|
||||
}
|
||||
if (entry.isSkipped()){
|
||||
LOG.trace("Is skipped");
|
||||
continue;
|
||||
}
|
||||
if (edge.getTarget().isSingle()){
|
||||
continue;
|
||||
}
|
||||
@@ -288,6 +312,10 @@ public class Crawler<T> {
|
||||
if (found >= count) break;
|
||||
CTEntrySupport next = targetsQueue.peek();
|
||||
limit = next != null ? next.entry.getWeight() : Double.NaN;
|
||||
if (deep > entry.getTarget().getLevel() || entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (alreadyFound + found < count){
|
||||
LOG.trace("Continue search, limit {}", limit);
|
||||
@@ -295,10 +323,6 @@ public class Crawler<T> {
|
||||
LOG.trace("Already {} found, extracting", alreadyFound);
|
||||
continue;
|
||||
}
|
||||
if (deep >= entry.getTarget().getLevel() || entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
DFS task = new DFS(curr, deep, count - found, limit);
|
||||
POOL.invoke(task);
|
||||
targetsQueue.addAll(task.getTargets());
|
||||
@@ -371,7 +395,8 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
private class DFS extends RecursiveAction {
|
||||
private final CTEntrySupport root;
|
||||
private CTEntrySupport root;
|
||||
private CTEntrySupport curr;
|
||||
private final int count;
|
||||
private final int deep;
|
||||
private final Collection<CTEntrySupport> queue;
|
||||
@@ -411,14 +436,52 @@ public class Crawler<T> {
|
||||
return queue;
|
||||
}
|
||||
|
||||
private boolean cancel(){
|
||||
if (isCancelled()) return true;
|
||||
if (root.entry.isSkipped()){
|
||||
LOG.trace("Root skipped");
|
||||
if (isSubTask){
|
||||
LOG.trace("Stop sub task");
|
||||
return true;
|
||||
} else {
|
||||
curr = root;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean skip(){
|
||||
if (curr.entry.isSkipped()){
|
||||
while (curr.entry.isSkipped()){
|
||||
LOG.trace("Is skipped, return to prev level");
|
||||
if (!levelUp()) break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean levelUp(){
|
||||
if (isRoot(curr)) return false;
|
||||
assert curr.parent != null;
|
||||
LOG.trace("Return to prev level");
|
||||
if (curr == root){
|
||||
root = root.parent;
|
||||
}
|
||||
curr = curr.parent;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void search(){
|
||||
CTEntrySupport curr = root;
|
||||
curr = root;
|
||||
LOG.trace("Start {}", root);
|
||||
while (curr.hasNext()){
|
||||
if (cancel()) break;
|
||||
Edge<T> edge = curr.next();
|
||||
CostTraversalEntry entry = curr.entry;
|
||||
if (skip()) continue;
|
||||
LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight);
|
||||
boolean isTarget = isFound(edge, entry);
|
||||
boolean isTarget = isFound(edge, entry, true);
|
||||
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1;
|
||||
if (canDeep || isTarget){
|
||||
CostTraversalEntry nextEntry = travers(entry, edge);
|
||||
@@ -430,8 +493,9 @@ public class Crawler<T> {
|
||||
LOG.trace("Found, add entry {} to queue", nextEntry);
|
||||
targets.add(curr);
|
||||
limit = Double.isNaN(limit) ? nextEntry.getWeight() : Math.min(limit, nextEntry.getWeight());
|
||||
curr = curr.parent;
|
||||
levelUp();
|
||||
} else {
|
||||
if (skip()) continue;
|
||||
if (!Double.isNaN(limit) && nextEntry.getWeight() >= limit){
|
||||
if (targets.size() < count){
|
||||
LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry);
|
||||
@@ -439,24 +503,22 @@ public class Crawler<T> {
|
||||
} else {
|
||||
LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry);
|
||||
}
|
||||
if (!isRoot(curr.parent)){
|
||||
curr = curr.parent.parent;
|
||||
} else {
|
||||
levelUp();
|
||||
if (!levelUp()){
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){
|
||||
if (addSubTask(curr))
|
||||
curr = curr.parent;
|
||||
levelUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Is limit deep");
|
||||
}
|
||||
while (!curr.hasNext() && !isRoot(curr)){
|
||||
LOG.trace("Level complete, return to prev level");
|
||||
curr = curr.parent;
|
||||
while (!curr.hasNext() && levelUp()){
|
||||
LOG.trace("Level complete");
|
||||
}
|
||||
}
|
||||
LOG.trace("Done {}", root);
|
||||
@@ -503,6 +565,7 @@ public class Crawler<T> {
|
||||
private final Edge<T> edge;
|
||||
private List<Edge<T>> edges;
|
||||
private Integer size;
|
||||
private transient boolean skipped;
|
||||
|
||||
protected TraversalEntry(Vertex<T> vertex) {
|
||||
this.vertex = vertex;
|
||||
@@ -515,6 +578,7 @@ public class Crawler<T> {
|
||||
this.head = head;
|
||||
this.vertex = edge.getTarget();
|
||||
this.edge = edge;
|
||||
this.skipped = head.isSkipped();
|
||||
edges = null;
|
||||
}
|
||||
|
||||
@@ -550,6 +614,16 @@ public class Crawler<T> {
|
||||
getEdges().sort(Comparator.<Edge<T>>naturalOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkipped(boolean skipped) {
|
||||
this.skipped = skipped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipped() {
|
||||
return skipped;
|
||||
}
|
||||
|
||||
protected List<Edge<T>> collect(Collection<Edge<T>> src){
|
||||
return new ArrayList<>(src);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ public interface Traversal<T> {
|
||||
List<Edge<T>> getEdges();
|
||||
Iterator<Edge<T>> iterator();
|
||||
void sort();
|
||||
void setSkipped(boolean skipped);
|
||||
boolean isSkipped();
|
||||
|
||||
default boolean isConnect(T target){
|
||||
Edge<T> edge = getEdge();
|
||||
|
||||
@@ -10,7 +10,7 @@ public class Vertex<T> {
|
||||
private final T entry;
|
||||
private final int index;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private volatile int level = -1;
|
||||
private int level = -1;
|
||||
|
||||
public Vertex(T entry, int index) {
|
||||
this.entry = entry;
|
||||
@@ -37,16 +37,15 @@ public class Vertex<T> {
|
||||
return level;
|
||||
}
|
||||
|
||||
public ReentrantLock locker(){
|
||||
return lock;
|
||||
}
|
||||
|
||||
public void connect(Edge<T> edge){
|
||||
assert this == edge.getSource();
|
||||
synchronized (edges){
|
||||
lock.lock();
|
||||
try {
|
||||
if (!edges.contains(edge)){
|
||||
edges.add(edge);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,13 +248,14 @@ public class Ship {
|
||||
private double ladenJumpRange = Double.NaN;
|
||||
private void fillFuelTable(){
|
||||
double fuel = getEngine().getMaxFuel();
|
||||
fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)];
|
||||
FuelHelper[] fuelTable = new FuelHelper[(int) (fuel/FUEL_TABLE_STEP)];
|
||||
maxJumpRange = Double.NaN; ladenJumpRange = Double.NaN;
|
||||
for (int i = fuelTable.length - 1; i >= 0; i--) {
|
||||
double distance = getJumpRange(fuel);
|
||||
fuelTable[i] = new FuelHelper(distance, fuel);
|
||||
fuel = fuel - FUEL_TABLE_STEP;
|
||||
}
|
||||
this.fuelTable = fuelTable;
|
||||
}
|
||||
|
||||
public double getMaxFuel(double distance){
|
||||
|
||||
@@ -118,7 +118,7 @@ public class CrawlerTest extends Assert {
|
||||
paths.clear();
|
||||
|
||||
crawler.findMin(x4, 20);
|
||||
TestUtil.assertPaths(true, paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
|
||||
TestUtil.assertPaths(paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
|
||||
PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4),
|
||||
PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4),
|
||||
PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4),
|
||||
@@ -240,7 +240,8 @@ public class CrawlerTest extends Assert {
|
||||
paths.clear();
|
||||
|
||||
crawler.findFast(x2);
|
||||
TestUtil.assertPaths(paths.get(), PPath.of(x5, x3, x2));
|
||||
assertNotNull(paths.get());
|
||||
assertEquals(2, paths.get().get(0).size());
|
||||
paths.clear();
|
||||
|
||||
}
|
||||
@@ -277,9 +278,6 @@ public class CrawlerTest extends Assert {
|
||||
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
|
||||
paths.clear();
|
||||
|
||||
crawler.findFast(x6, x2);
|
||||
TestUtil.assertPaths(paths.get(), PPath.of(x6, x4, x2));
|
||||
paths.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
Reference in New Issue
Block a user