Skip to content

Commit ba22ec8

Browse files
author
Peter
committed
bug fix for inPlace deleting in memorygraphsafe
1 parent 9907896 commit ba22ec8

File tree

14 files changed

+275
-161
lines changed

14 files changed

+275
-161
lines changed

core/src/main/java/de/jetsli/graph/routing/ContractionHierarchies.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import de.jetsli.graph.coll.MyOpenBitSet;
2020
import de.jetsli.graph.storage.DistEntry;
2121
import de.jetsli.graph.storage.Graph;
22-
import de.jetsli.graph.util.MyIteratorable;
22+
import de.jetsli.graph.util.GraphUtility;
2323
import java.util.Date;
2424
import java.util.PriorityQueue;
2525

@@ -50,7 +50,7 @@ public Graph contract(Graph g) {
5050

5151
// TODO calculate edge difference => yet another dikstra necessary!?
5252
for (int i = 0; i < locations; i++) {
53-
heap.add(new DistEntry(i, MyIteratorable.count(g.getOutgoing(i))));
53+
heap.add(new DistEntry(i, GraphUtility.count(g.getOutgoing(i))));
5454
}
5555
DistEntry curr;
5656
MyBitSet alreadyContracted = new MyOpenBitSet(locations);

core/src/main/java/de/jetsli/graph/storage/MMapGraph.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import java.nio.channels.FileChannel;
3030
import org.slf4j.Logger;
3131
import org.slf4j.LoggerFactory;
32-
import static de.jetsli.graph.util.MyIteratorable.*;
32+
import static de.jetsli.graph.util.GraphUtility.*;
3333
import gnu.trove.map.hash.TIntFloatHashMap;
3434
import java.io.*;
3535

core/src/main/java/de/jetsli/graph/storage/MemoryGraph.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import de.jetsli.graph.coll.MyBitSet;
1919
import de.jetsli.graph.coll.MyOpenBitSet;
2020
import de.jetsli.graph.util.EdgeIdIterator;
21-
import de.jetsli.graph.util.MyIteratorable;
21+
import de.jetsli.graph.util.GraphUtility;
2222
import java.lang.reflect.Field;
2323
import java.util.ArrayList;
2424
import java.util.Arrays;

core/src/main/java/de/jetsli/graph/storage/MemoryGraphSafe.java

Lines changed: 80 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package de.jetsli.graph.storage;
1717

1818
import de.jetsli.graph.coll.MyBitSet;
19+
import de.jetsli.graph.coll.MyBitSetImpl;
1920
import de.jetsli.graph.coll.MyOpenBitSet;
21+
import de.jetsli.graph.coll.MyTBitSet;
2022
import de.jetsli.graph.util.EdgeIdIterator;
2123
import de.jetsli.graph.util.Helper;
2224
import gnu.trove.list.array.TIntArrayList;
@@ -25,6 +27,7 @@
2527
import java.io.IOException;
2628
import java.util.Arrays;
2729
import java.util.Date;
30+
import javax.management.RuntimeErrorException;
2831
import org.slf4j.Logger;
2932
import org.slf4j.LoggerFactory;
3033

@@ -375,88 +378,104 @@ public void optimize() {
375378
return;
376379

377380

378-
if (deleted < size / 5)
379-
optimizeIfFewDeletes(deleted);
380-
else
381-
optimizeIfLotsOfDeletes(deleted);
381+
// if (deleted < size / 5) {
382+
inPlaceDelete(deleted);
383+
// } else
384+
// optimizeIfLotsOfDeletes(deleted);
382385
}
383386

384387
/**
385388
* This methods moves the last nodes into the deleted nodes, which is much more memory friendly
386-
* but probably slower than optimizeIfLotsOfDeletes for many deletes.
389+
* for only a few deletes but probably not for many deletes.
387390
*/
388-
void optimizeIfFewDeletes(int deleted) {
391+
void inPlaceDelete(int deleted) {
389392
// Alternative to this method: use smaller segments for nodes and not one big fat java array?
390393
//
391-
// Prepare edge-update of nodes which are connected to deleted nodes and
392-
// create old- to new-index map.
393-
int index = getNodes();
394-
int counter = 0;
395-
int newIndices[] = new int[deleted];
396-
int oldIndices[] = new int[deleted];
397-
TIntIntHashMap markedMap = new TIntIntHashMap(deleted, 1.5f, -1, -1);
398-
for (int del = deletedNodes.next(0); del >= 0; del = deletedNodes.next(del + 1)) {
399-
EdgeIdIterator delEdgesIter = getEdges(del);
394+
// Prepare edge-update of nodes which are connected to deleted nodes
395+
int toMoveNode = getNodes();
396+
int itemsToMove = 0;
397+
int maxMoves = Math.min(deleted, Math.max(0, toMoveNode - deleted));
398+
int newIndices[] = new int[maxMoves];
399+
int oldIndices[] = new int[maxMoves];
400+
401+
TIntIntHashMap oldToNewIndexMap = new TIntIntHashMap(deleted, 1.5f, -1, -1);
402+
MyBitSetImpl toUpdate = new MyBitSetImpl(deleted * 3);
403+
for (int delNode = deletedNodes.next(0); delNode >= 0; delNode = deletedNodes.next(delNode + 1)) {
404+
EdgeIdIterator delEdgesIter = getEdges(delNode);
400405
while (delEdgesIter.next()) {
401406
int currNode = delEdgesIter.nodeId();
402407
if (deletedNodes.contains(currNode))
403408
continue;
404409

405-
// remove all edges to the deleted nodes
406-
EdgeIdIterator nodesConnectedToDelIter = getEdges(currNode);
407-
boolean firstNext = nodesConnectedToDelIter.next();
408-
if (firstNext) {
409-
// this forces new edges to be created => TODO only update the edges. do not create new entries
410-
refToEdges[currNode] = -1;
411-
do {
412-
if (!deletedNodes.contains(nodesConnectedToDelIter.nodeId()))
413-
internalAdd(currNode, nodesConnectedToDelIter.nodeId(),
414-
nodesConnectedToDelIter.distance(), nodesConnectedToDelIter.flags());
415-
} while (nodesConnectedToDelIter.next());
416-
}
410+
toUpdate.add(currNode);
417411
}
418412

419-
index--;
420-
for (; index >= 0; index--) {
421-
if (!deletedNodes.contains(index))
413+
toMoveNode--;
414+
for (; toMoveNode >= 0; toMoveNode--) {
415+
if (!deletedNodes.contains(toMoveNode))
422416
break;
423417
}
424418

425-
newIndices[counter] = del;
426-
oldIndices[counter] = index;
427-
markedMap.put(index, del);
428-
counter++;
419+
if (toMoveNode < delNode)
420+
break;
421+
422+
// create sorted old- to new-index map
423+
newIndices[itemsToMove] = delNode;
424+
oldIndices[itemsToMove] = toMoveNode;
425+
oldToNewIndexMap.put(toMoveNode, delNode);
426+
itemsToMove++;
429427
}
430428

431-
// now move marked nodes into deleted nodes
432-
for (int i = 0; i < oldIndices.length; i++) {
429+
// all deleted nodes could be connected to existing. remove the connections
430+
for (int toUpdateNode = toUpdate.next(0); toUpdateNode >= 0; toUpdateNode = toUpdate.next(toUpdateNode + 1)) {
431+
// remove all edges to the deleted nodes
432+
EdgeIdIterator nodesConnectedToDelIter = getEdges(toUpdateNode);
433+
// hack to remove edges ref afterwards
434+
boolean firstNext = nodesConnectedToDelIter.next();
435+
if (firstNext) {
436+
// this forces new edges to be created => TODO only update the edges. do not create new entries
437+
refToEdges[toUpdateNode] = EMPTY_LINK;
438+
do {
439+
if (!deletedNodes.contains(nodesConnectedToDelIter.nodeId()))
440+
internalAdd(toUpdateNode, nodesConnectedToDelIter.nodeId(),
441+
nodesConnectedToDelIter.distance(), nodesConnectedToDelIter.flags());
442+
} while (nodesConnectedToDelIter.next());
443+
}
444+
}
445+
toUpdate.clear();
446+
447+
// marks connected nodes to rewrite the edges
448+
for (int i = 0; i < itemsToMove; i++) {
433449
int oldI = oldIndices[i];
434-
int newI = newIndices[i];
450+
EdgeIdIterator movedEdgeIter = getEdges(oldI);
451+
while (movedEdgeIter.next()) {
452+
if (deletedNodes.contains(movedEdgeIter.nodeId()))
453+
throw new IllegalStateException("shouldn't happen the edge to the node " + movedEdgeIter.nodeId() + " should be already deleted. " + oldI);
435454

436-
EdgeIdIterator toMoveEdgeIter = getEdges(oldI);
437-
while (toMoveEdgeIter.next()) {
438-
int movedNewIndex = markedMap.get(toMoveEdgeIter.nodeId());
439-
// if node not moved at all (<0) or node is not yet moved use the old index
440-
if (movedNewIndex < 0 || movedNewIndex >= newI) {
441-
movedNewIndex = toMoveEdgeIter.nodeId();
442-
}
443-
444-
// update edges of r to use newI instead of oldI
445-
EdgeIdIterator updateIter = getEdges(movedNewIndex);
446-
boolean firstNext = updateIter.next();
447-
if (firstNext) {
448-
// this forces new edges to be created => TODO only update the edges. do not create new entries
449-
refToEdges[movedNewIndex] = -1;
450-
do {
451-
int connNode;
452-
if (updateIter.nodeId() == oldI)
453-
connNode = newI;
454-
else
455-
connNode = updateIter.nodeId();
456-
internalAdd(movedNewIndex, connNode, updateIter.distance(), updateIter.flags());
457-
} while (updateIter.next());
458-
}
455+
toUpdate.add(movedEdgeIter.nodeId());
459456
}
457+
}
458+
459+
// rewrite the edges of nodes connected to moved nodes
460+
for (int toUpdateNode = toUpdate.next(0); toUpdateNode >= 0; toUpdateNode = toUpdate.next(toUpdateNode + 1)) {
461+
EdgeIdIterator connectedToMovedIter = getEdges(toUpdateNode);
462+
boolean firstNext = connectedToMovedIter.next();
463+
if (firstNext) {
464+
refToEdges[toUpdateNode] = EMPTY_LINK;
465+
do {
466+
int currNode = connectedToMovedIter.nodeId();
467+
int other = oldToNewIndexMap.get(currNode);
468+
if (other < 0)
469+
other = currNode;
470+
internalAdd(toUpdateNode, other, connectedToMovedIter.distance(), connectedToMovedIter.flags());
471+
} while (connectedToMovedIter.next());
472+
}
473+
}
474+
475+
// move nodes into deleted nodes
476+
for (int i = 0; i < itemsToMove; i++) {
477+
int oldI = oldIndices[i];
478+
int newI = newIndices[i];
460479
refToEdges[newI] = refToEdges[oldI];
461480
lats[newI] = lats[oldI];
462481
lons[newI] = lons[oldI];
@@ -469,7 +488,7 @@ void optimizeIfFewDeletes(int deleted) {
469488
/**
470489
* This methods creates a new in-memory graph without the specified deleted nodes.
471490
*/
472-
void optimizeIfLotsOfDeletes(int deleted) {
491+
void replacingDelete(int deleted) {
473492
MemoryGraphSafe inMemGraph = new MemoryGraphSafe(null, getNodes() - deleted, getMaxEdges());
474493

475494
// see MMapGraph for a near duplicate
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2012 Peter Karich [email protected]
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.jetsli.graph.util;
17+
18+
import de.jetsli.graph.storage.DistEntry;
19+
import de.jetsli.graph.storage.Graph;
20+
import gnu.trove.set.hash.TIntHashSet;
21+
import java.util.ArrayList;
22+
import java.util.Iterator;
23+
import java.util.List;
24+
25+
/**
26+
*
27+
* @author Peter Karich, [email protected]
28+
*/
29+
public class GraphUtility {
30+
31+
/**
32+
* @throws could throw exception if uncatched problems like index out of bounds etc
33+
*/
34+
public static List<String> getProblems(Graph g) {
35+
List<String> problems = new ArrayList<String>();
36+
int nodes = g.getNodes();
37+
for (int i = 0; i < nodes; i++) {
38+
double lat = g.getLatitude(i);
39+
if (lat > 90 || lat < -90)
40+
problems.add("latitude is not within its bounds " + lat);
41+
double lon = g.getLongitude(i);
42+
if (lon > 180 || lon < -180)
43+
problems.add("longitude is not within its bounds " + lon);
44+
int incom = count(g.getIncoming(i));
45+
int out = count(g.getOutgoing(i));
46+
int e = count(g.getEdges(i));
47+
if (Math.max(out, incom) > e)
48+
problems.add("count incoming or outgoing edges should be maximum "
49+
+ e + " but were:" + incom + "(in), " + out + "(out)");
50+
51+
EdgeIdIterator iter = g.getEdges(i);
52+
while (iter.next()) {
53+
if (iter.nodeId() >= nodes)
54+
problems.add("edge of " + i + " has a node " + iter.nodeId() + " greater or equal to getNodes");
55+
}
56+
}
57+
58+
for (int i = 0; i < nodes; i++) {
59+
new XFirstSearch().start(g, i, false);
60+
}
61+
62+
return problems;
63+
}
64+
65+
/**
66+
* note/todo: counts edges twice if both directions available
67+
*/
68+
public static int countEdges(Graph g) {
69+
int counter = 0;
70+
int nodes = g.getNodes();
71+
for (int i = 0; i < nodes; i++) {
72+
EdgeIdIterator iter = g.getEdges(i);
73+
while (iter.next()) {
74+
counter++;
75+
}
76+
}
77+
return counter;
78+
}
79+
80+
public static int count(EdgeIdIterator iter) {
81+
int counter = 0;
82+
while (iter.next()) {
83+
++counter;
84+
}
85+
return counter;
86+
}
87+
88+
public static int count(Iterable<?> iter) {
89+
int counter = 0;
90+
for (Object o : iter) {
91+
++counter;
92+
}
93+
return counter;
94+
}
95+
96+
public static boolean contains(EdgeIdIterator iter, int... locs) {
97+
TIntHashSet set = new TIntHashSet();
98+
99+
while (iter.next()) {
100+
set.add(iter.nodeId());
101+
}
102+
for (int l : locs) {
103+
if (!set.contains(l))
104+
return false;
105+
}
106+
return true;
107+
}
108+
109+
public static boolean contains(Iterable<? extends DistEntry> iter, int... locs) {
110+
Iterator<? extends DistEntry> i = iter.iterator();
111+
TIntHashSet set = new TIntHashSet();
112+
while (i.hasNext()) {
113+
set.add(i.next().node);
114+
}
115+
for (int l : locs) {
116+
if (!set.contains(l))
117+
return false;
118+
}
119+
return true;
120+
}
121+
}

0 commit comments

Comments
 (0)