Skip to content

Commit f605635

Browse files
committed
Neighborlist is now a synchronized queue, to avoid multithreading errors
1 parent f354fdd commit f605635

6 files changed

Lines changed: 229 additions & 9 deletions

File tree

src/main/java/info/debatty/java/graphs/Graph.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,19 +1143,20 @@ public int hashCode() {
11431143
}
11441144

11451145
@Override
1146-
public boolean equals(Object obj) {
1146+
public boolean equals(final Object obj) {
11471147
if (obj == null) {
11481148
return false;
11491149
}
11501150
if (getClass() != obj.getClass()) {
11511151
return false;
11521152
}
11531153
final Graph<?> other = (Graph<?>) obj;
1154+
System.out.println("Delegating to map...");
11541155
if (this.map != other.map && (this.map == null || !this.map.equals(other.map))) {
11551156
return false;
11561157
}
11571158
return true;
11581159
}
11591160

1160-
1161+
11611162
}

src/main/java/info/debatty/java/graphs/NeighborList.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package info.debatty.java.graphs;
22

3-
import info.debatty.java.util.BoundedPriorityQueue;
3+
import info.debatty.java.util.SynchronizedBoundedPriorityQueue;
44
import java.io.Serializable;
55
import java.util.ArrayList;
66
import java.util.HashMap;
@@ -12,7 +12,7 @@
1212
*
1313
* @author Thibault Debatty
1414
*/
15-
public class NeighborList extends BoundedPriorityQueue<Neighbor>
15+
public class NeighborList extends SynchronizedBoundedPriorityQueue<Neighbor>
1616
implements Serializable {
1717

1818
public static <T> ArrayList<Edge>

src/main/java/info/debatty/java/util/BoundedPriorityQueue.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package info.debatty.java.util;
22

33
import java.io.Serializable;
4-
import java.util.concurrent.PriorityBlockingQueue;
4+
import java.util.PriorityQueue;
55

66
/**
77
* This class implements a bounded priority queue A structure that always keeps
@@ -10,7 +10,8 @@
1010
* @author Thibault Debatty
1111
* @param <E>
1212
*/
13-
public class BoundedPriorityQueue<E> extends PriorityBlockingQueue<E> implements Serializable {
13+
public class BoundedPriorityQueue<E> extends PriorityQueue<E>
14+
implements Serializable {
1415

1516
private final int capacity;
1617

@@ -84,6 +85,7 @@ public final int getCapacity() {
8485
* @param other
8586
* @return
8687
*/
88+
@Override
8789
public boolean equals(final Object other) {
8890
if (other == null) {
8991
return false;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright 2017 Thibault Debatty.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package info.debatty.java.util;
26+
27+
import java.io.Serializable;
28+
import java.util.Collection;
29+
import java.util.Iterator;
30+
import java.util.Queue;
31+
32+
/**
33+
*
34+
* @author Thibault Debatty
35+
* @param <E>
36+
*/
37+
public class SynchronizedBoundedPriorityQueue<E>
38+
implements Serializable, Iterable<E>, Collection<E>, Queue<E> {
39+
40+
private final BoundedPriorityQueue<E> queue;
41+
42+
public SynchronizedBoundedPriorityQueue(final int capacity) {
43+
queue = new BoundedPriorityQueue<E>(capacity);
44+
}
45+
46+
@Override
47+
public Iterator<E> iterator() {
48+
return queue.iterator();
49+
}
50+
51+
@Override
52+
public int size() {
53+
synchronized (this) {
54+
return queue.size();
55+
}
56+
}
57+
58+
public boolean offer(E e) {
59+
synchronized (this) {
60+
return queue.offer(e);
61+
}
62+
}
63+
64+
public E poll() {
65+
synchronized (this) {
66+
return queue.poll();
67+
}
68+
}
69+
70+
public E peek() {
71+
synchronized (this) {
72+
return queue.peek();
73+
}
74+
}
75+
76+
public boolean isEmpty() {
77+
synchronized (this) {
78+
return queue.isEmpty();
79+
}
80+
}
81+
82+
public boolean contains(Object o) {
83+
synchronized (this) {
84+
return queue.contains(o);
85+
}
86+
}
87+
88+
public Object[] toArray() {
89+
synchronized (this) {
90+
return queue.toArray();
91+
}
92+
}
93+
94+
public <T> T[] toArray(T[] a) {
95+
synchronized (this) {
96+
return queue.toArray(a);
97+
}
98+
}
99+
100+
public boolean add(E e) {
101+
synchronized (this) {
102+
return queue.add(e);
103+
}
104+
}
105+
106+
public boolean remove(Object o) {
107+
synchronized (this) {
108+
return queue.remove(o);
109+
}
110+
}
111+
112+
public boolean containsAll(Collection<?> c) {
113+
synchronized (this) {
114+
return queue.containsAll(c);
115+
}
116+
}
117+
118+
public boolean addAll(Collection<? extends E> c) {
119+
synchronized (this) {
120+
return queue.addAll(c);
121+
}
122+
}
123+
124+
public boolean removeAll(Collection<?> c) {
125+
synchronized (this) {
126+
return queue.removeAll(c);
127+
}
128+
}
129+
130+
public boolean retainAll(Collection<?> c) {
131+
synchronized (this) {
132+
return queue.retainAll(c);
133+
}
134+
}
135+
136+
public void clear() {
137+
synchronized (this) {
138+
queue.clear();
139+
}
140+
}
141+
142+
public E remove() {
143+
synchronized (this) {
144+
return queue.remove();
145+
}
146+
}
147+
148+
public E element() {
149+
synchronized (this) {
150+
return queue.element();
151+
}
152+
}
153+
154+
public boolean equals(Object other) {
155+
if (other == null) {
156+
return false;
157+
}
158+
159+
if (!other.getClass().isInstance(this)) {
160+
return false;
161+
}
162+
163+
SynchronizedBoundedPriorityQueue<E> other_synced =
164+
(SynchronizedBoundedPriorityQueue<E>) other;
165+
166+
synchronized (this) {
167+
return queue.equals(other_synced.queue);
168+
}
169+
}
170+
171+
public String toString() {
172+
synchronized (this) {
173+
return queue.toString();
174+
}
175+
}
176+
}

src/test/java/info/debatty/java/graphs/GraphTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ public void testStronglyConnectedComponents() {
115115
* Test of stronglyConnectedComponents method, of class Graph.
116116
*/
117117
public void testStronglyConnectedComponents2() {
118-
System.out.println("Strongly connected components");
119-
System.out.println("=============================");
118+
System.out.println("Strongly connected components 2");
119+
System.out.println("===============================");
120120

121121
// Generate two series of nodes
122122
ArrayList<Integer> nodes = new ArrayList<Integer>();

src/test/java/info/debatty/java/graphs/build/ThreadedNNDescentTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public double similarity(Integer value1, Integer value2) {
7575

7676
Integer first_node = graph.first();
7777
System.out.println(threaded_graph.getNeighbors(first_node));
78-
assertEquals(graph.getNeighbors(first_node).countCommons(threaded_graph.getNeighbors(first_node)), k);
78+
assertEquals(k, graph.getNeighbors(first_node).countCommons(threaded_graph.getNeighbors(first_node)));
7979

8080
int correct_edges = threaded_graph.compare(graph);
8181
double correct_ratio = 1.0 * correct_edges / (count * k);
@@ -87,4 +87,45 @@ public double similarity(Integer value1, Integer value2) {
8787
assertTrue("Too many wrong edges!", correct_ratio >= MIN_CORRECT_RATIO);
8888
}
8989

90+
/**
91+
* Test that the threaded nndescent algorithms produces neighborlists of
92+
* the correct size (might fail because of missing synchronization).
93+
*/
94+
public void testK() {
95+
System.out.println("test K");
96+
97+
int count = 10;
98+
int k = 5;
99+
int trials = 10;
100+
101+
// Generate some nodes
102+
Random r = new Random();
103+
LinkedList<Integer> nodes = new LinkedList<Integer>();
104+
for (int i = 0; i < count; i++) {
105+
nodes.add(r.nextInt());
106+
}
107+
108+
// Define the similarity
109+
SimilarityInterface<Integer> similarity =
110+
new SimilarityInterface<Integer>() {
111+
112+
public double similarity(final Integer value1, final Integer value2) {
113+
return 1.0 / (1.0 + Math.abs(value1 - value2));
114+
}
115+
};
116+
117+
for (int i = 0; i < trials; i++) {
118+
System.out.println("NNdescent threaded graph builder...");
119+
ThreadedNNDescent<Integer> threaded_builder =
120+
new ThreadedNNDescent<Integer>();
121+
threaded_builder.setK(k);
122+
threaded_builder.setSimilarity(similarity);
123+
Graph<Integer> threaded_graph = threaded_builder.computeGraph(nodes);
124+
125+
for (Integer value : threaded_graph.getNodes()) {
126+
assertEquals(k, threaded_graph.getNeighbors(value).size());
127+
}
128+
}
129+
}
130+
90131
}

0 commit comments

Comments
 (0)