Passed
Push — master ( 1f3b93...63f895 )
by Alessandro
09:41
created

computeDistanceMatrix()   C

Complexity

Conditions 10

Size

Total Lines 57
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 21
dl 0
loc 57
rs 5.9999
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like it.cnr.istc.pst.platinum.ai.framework.time.solver.apsp.APSPTemporalSolver.computeDistanceMatrix() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package it.cnr.istc.pst.platinum.ai.framework.time.solver.apsp;
2
3
import java.util.HashMap;
4
import java.util.Iterator;
5
import java.util.Map;
6
7
import it.cnr.istc.pst.platinum.ai.framework.time.solver.TemporalSolver;
8
import it.cnr.istc.pst.platinum.ai.framework.time.tn.TemporalNetwork;
9
import it.cnr.istc.pst.platinum.ai.framework.time.tn.TimePoint;
10
import it.cnr.istc.pst.platinum.ai.framework.time.tn.TimePointDistanceConstraint;
11
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.event.AddRelationTemporalNetworkNotification;
12
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.event.DelRelationTemporalNetworkNotification;
13
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.event.TemporalNetworkNotification;
14
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.query.TimePointDistanceQuery;
15
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.query.TimePointDistanceToHorizonQuery;
16
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.query.TimePointQuery;
17
import it.cnr.istc.pst.platinum.ai.framework.time.tn.lang.query.TimePointScheduleQuery;
18
19
/**
20
 * 
21
 * @author alessandro
22
 *
23
 */
24
public final class APSPTemporalSolver extends TemporalSolver<TimePointQuery>
25
{
26
	private DistanceGraph dg;									// distance graph
27
	private Map<TimePoint, Map<TimePoint, Long>> distance;		// the distance matrix containing the minimum distances between points;
28
	private boolean toCompute;								// lazy approach - propagate constraint only when needed
29
	private int propagationCounter;		
30
	
31
	/**
32
	 * Create an All-Pair-Shortest-Path Solver instance.
33
	 * 
34
	 */
35
	public APSPTemporalSolver(TemporalNetwork tn) {
36
		super(tn);
37
		
38
		// initialize APSP data structure
39
		this.distance = null;
40
		// attribute for testing purposes
41
		this.propagationCounter = 0;
42
		// initialize
43
		this.dg();
44
		// set flag
45
		this.toCompute = true;
46
	}
47
	
48
	/**
49
	 * 
50
	 */
51
	private void dg() {
52
53
		// create the distance graph
54
		this.dg = new DistanceGraph();
55
		// set temporal horizon
56
		this.dg.setInfity(this.tn.getHorizon());
57
		
58
		// add all points to the distance graph
59
		for (TimePoint point : this.tn.getTimePoints()) {
60
			// add node to the distance graph
61
			this.dg.add(point);
62
		}
63
		
64
		// add edges between points 
65
		for (TimePoint reference : this.tn.getTimePoints()) {
66
			for (TimePoint target : this.tn.getTimePoints()) {
67
				
68
				// check if different
69
				if (!reference.equals(target)) {
70
					
71
					// get constraint bounds
72
					long[] bounds = this.tn.getConstraintBounds(reference, target);
73
					// check if a bound exists
74
					if (bounds != null) {
75
						
76
						// set distance graph's edges according to the computed bounds
77
						this.dg.add(reference, target, bounds[1]);
78
						this.dg.add(target, reference, -bounds[0]);
79
					}
80
				} 
81
			}
82
		}
83
	}
84
	
85
	/**
86
	 * 
87
	 * @return
88
	 */
89
	@Override
90
	public boolean isValid() 
91
	{
92
		// check information status
93
		if (this.toCompute) {
94
			// clean distance matrix's data
95
			this.computeDistanceMatrix();
96
		}
97
		
98
		// check consistency
99
		boolean consistent = true;
100
		Iterator<TimePoint> it = this.dg.getPoints().iterator();
101
		while (it.hasNext() && consistent) {
102
			
103
			// next time point
104
			TimePoint tp = it.next();
105
			// get distance
106
			long distance = this.distance.get(tp).get(tp); 
0 ignored issues
show
Comprehensibility introduced by
The variable distanceshadows a field with the same name declared in line 27. Consider renaming it.
Loading history...
107
			// check cycle distance
108
			consistent = distance == 0;
109
		}
110
		
111
		// get consistency check result
112
		return consistent;
113
	}
114
	
115
	/**
116
	 * 
117
	 */
118
	@Override
119
	public void notify(TemporalNetworkNotification info)
120
	{
121
		// check notification type
122
		switch (info.getType()) {
123
			
124
			// time point deleted
125
			case INITIALIZATION :
126
			case ADD_TP : 
127
			case DEL_TP : {
128
				
129
				// update the distance graph 
130
				this.dg();	
131
				// set to propagate flag
132
				this.toCompute = true;
133
			}
134
			break;
135
			
136
			case DEL_REL : {
137
				
138
				// get data
139
				DelRelationTemporalNetworkNotification notify = (DelRelationTemporalNetworkNotification) info;
140
				
141
				// check constraints and updated distance graph
142
				for (TimePointDistanceConstraint constraint : notify.getRels()) {
143
					// check bound intersection between the two points
144
					long[] bounds = this.tn.getConstraintBounds(constraint.getReference(), constraint.getTarget());
145
					// check if a constraint still exists
146
					if (bounds != null) {
147
						
148
						// update distance bounds
149
						this.dg.add(constraint.getReference(), constraint.getTarget(), bounds[1]);
150
						this.dg.add(constraint.getTarget(), constraint.getReference(), -bounds[0]);
151
						
152
					} else {
153
						
154
						// no constraint between the two time points
155
						this.dg.delete(constraint.getReference(), constraint.getTarget());
156
						this.dg.delete(constraint.getTarget(), constraint.getReference());
157
					}
158
				}
159
				
160
				// set to propagate flag
161
				this.toCompute = true;
162
			}
163
			break;
164
			
165
			case ADD_REL : {
166
				
167
				// get data
168
				AddRelationTemporalNetworkNotification notify = (AddRelationTemporalNetworkNotification) info;
169
				
170
				// check constraints and update distance graph
171
				for (TimePointDistanceConstraint constraint : notify.getRels()) {
172
					// check bound intersection between the two time points
173
					long[] bounds = this.tn.getConstraintBounds(constraint.getReference(), constraint.getTarget());
174
					
175
					// update distance bounds
176
					this.dg.add(constraint.getReference(), constraint.getTarget(), bounds[1]);
177
					this.dg.add(constraint.getTarget(), constraint.getReference(), -bounds[0]);
178
				}
179
				
180
				// set to propagate flag
181
				this.toCompute = true;
182
			}
183
			break; 
184
		}
185
	}
186
	
187
	/**
188
	 * 
189
	 * @return
190
	 */
191
	public DistanceGraph getDistanceGraph() {
192
		// get the distance graph
193
		return this.dg;
194
	}
195
	
196
	/**
197
	 * 
198
	 */
199
	@Override
200
	public String toString() {
201
		
202
		if (this.toCompute) {
203
			this.computeDistanceMatrix();
204
		}
205
		
206
		// print distance matrix 
207
		String matrix = "Distance matrix (Computed using Floyd-Warshall Algorithm)\n";
208
		for (TimePoint i : this.dg.getPoints()) {
209
			for (TimePoint j : this.dg.getPoints()) {
210
				matrix += "\t" + this.distance.get(i).get(j);
0 ignored issues
show
Performance introduced by
String concatenation with + is inefficient. Doing so in a loop may incur a significant performance penalty. Consider using a StringBuilder instead
Loading history...
211
			}
212
			matrix += "\n";
0 ignored issues
show
Performance introduced by
String concatenation with + is inefficient. Doing so in a loop may incur a significant performance penalty. Consider using a StringBuilder instead
Loading history...
213
		}
214
	
215
		// get description of the distance matrix
216
		return matrix;
217
	}
218
	
219
	/**
220
	 * Returns the number of temporal propagation actually done
221
	 * Only for testing purposes
222
	 * 
223
	 * @return
224
	 */
225
	public int getPropagationCounter() {
226
		return propagationCounter;
227
	}
228
	
229
	/**
230
	 * 
231
	 */
232
	@Override
233
	public void process(TimePointQuery query) {
234
		
235
		// check query type 
236
		switch (query.getType()) {
237
		
238
			// handle time point bound query
239
			case TP_SCHEDULE : {
240
				
241
				// get query
242
				TimePointScheduleQuery tpBoundQuery = (TimePointScheduleQuery) query;
243
				// get time point
244
				TimePoint point = tpBoundQuery.getTimePoint();
245
				// get distance between the origin and the time point
246
				long[] distance = this.getDistance(this.tn.getOriginTimePoint(), point); 
0 ignored issues
show
Comprehensibility introduced by
The variable distanceshadows a field with the same name declared in line 27. Consider renaming it.
Loading history...
247
				// set information 
248
				point.setLowerBound(distance[0]);
249
				point.setUpperBound(distance[1]);
250
			}
251
			break;
252
			
253
			// handle time point distance query
254
			case TP_DISTANCE : {
255
				
256
				// get query
257
				TimePointDistanceQuery tpDistanceQuery = (TimePointDistanceQuery) query;
258
				// get source point 
259
				TimePoint source = tpDistanceQuery.getSource();
260
				// get target point 
261
				TimePoint target = tpDistanceQuery.getTarget();
262
				// get distance between points
263
				long[] distance = this.getDistance(source, target);
0 ignored issues
show
Comprehensibility introduced by
The variable distanceshadows a field with the same name declared in line 27. Consider renaming it.
Loading history...
264
				// set information
265
				tpDistanceQuery.setDistanceLowerBound(distance[0]);
266
				tpDistanceQuery.setDistanceUpperBound(distance[1]);
267
			}
268
			break;
269
			
270
			// handle time point distance to horizon query
271
			case TP_DISTANCE_TO_HORIZON : {
272
				
273
				// get query 
274
				TimePointDistanceToHorizonQuery tpDistanceQuery = (TimePointDistanceToHorizonQuery) query;
275
				// get time point
276
				TimePoint tp = tpDistanceQuery.getTimePoint();
277
				// get distance to horizon
278
				long[] distance = this.getDistance(tp, this.tn.getHorizonTimePoint());
0 ignored issues
show
Comprehensibility introduced by
The variable distanceshadows a field with the same name declared in line 27. Consider renaming it.
Loading history...
279
				// set information
280
				tpDistanceQuery.setDistance(distance);
281
			}
282
			break;
283
			
284
			default : {
285
				
286
				// not a time point query
287
				throw new RuntimeException("Impossible to process this type of temporal query " + query.getType());
0 ignored issues
show
Best Practice introduced by
Dedicated exceptions should be preferred over throwing the generic Exception.
Loading history...
288
			}
289
		}
290
	}
291
	
292
	/**
293
	 * Returns distance lower and upper bounds of Time Point 
294
	 * tp1 to Time Point tp2 
295
	 * 
296
	 * @param tp1
297
	 * @param tp2
298
	 * @return
299
	 */
300
	protected long[] getDistance(TimePoint tp1, TimePoint tp2) {
301
		
302
		// compute minimal minimal network if needed
303
		if (this.toCompute) {
304
			// clean distance matrix's data
305
			this.computeDistanceMatrix();
306
		}
307
		
308
		// default distance bounds - total uncertainty
309
		long[] bounds = new long[] {
310
				-this.tn.getHorizon(), 
311
				this.tn.getHorizon()
312
		};
313
		
314
		// check if both time points exists
315
		if (this.distance.containsKey(tp1) && this.distance.get(tp1).containsKey(tp2)) {
316
			// set bounds
317
			bounds = new long[] {
318
					-this.distance.get(tp2).get(tp1),		// lower bound
319
					this.distance.get(tp1).get(tp2)			// upper bound
320
				};
321
		}
322
		
323
		// get bounds
324
		return bounds;
325
	}
326
	
327
	/**
328
	 * 
329
	 */
330
	private void computeDistanceMatrix() {
331
		
332
		// check size of the distance graph
333
		this.distance = new HashMap<TimePoint, Map<TimePoint, Long>>();
334
		// initialize distances to infinity
335
		for (TimePoint i : this.dg.getPoints()) {
336
			
337
			// create data structure
338
			this.distance.put(i, new HashMap<TimePoint, Long>());
339
			for (TimePoint j : this.dg.getPoints()) {
340
				
341
				// check pairs of time points
342
				if (i.equals(j)) {
343
					// set distance to 0
344
					this.distance.get(i).put(j, 0l);
345
					
346
				} else {
347
					
348
					// set distance to infinity
349
					this.distance.get(i).put(j, this.dg.getInfity());
350
				}
351
			}
352
		}
353
		
354
		// initialize distances using computed intersections from distance constraints
355
		for (TimePoint point : this.dg.getPoints()) {
356
			// get adjacent points
357
			for (TimePoint adj : this.dg.getAdjacents(point)) {
358
				
359
				// get distance
360
				long distance = this.dg.getDistance(point, adj);
0 ignored issues
show
Comprehensibility introduced by
The variable distanceshadows a field with the same name declared in line 27. Consider renaming it.
Loading history...
361
				// set distance
362
				this.distance.get(point).put(adj, distance);
363
			}
364
		}
365
		
366
		// compute minimum distance between two nodes passing through any k intermediate node
367
		for (TimePoint k : this.dg.getPoints()) {
368
			// compute shortest paths using intermediate points
369
			for (TimePoint i : this.dg.getPoints()) {
370
				for (TimePoint j : this.dg.getPoints()) {
371
					
372
					// compute the path from i to j through k
373
					long path = this.distance.get(i).get(k) + this.distance.get(k).get(j);
374
					// compare computed distance with the direct path
375
					if (this.distance.get(i).get(j) > path) {
376
						// update distance
377
						this.distance.get(i).put(j, path);
378
					}
379
				}
380
			}
381
		}
382
		
383
		// update propagation counter
384
		this.propagationCounter++;
385
		// set propagation flag
386
		this.toCompute = false;
387
	}
388
	
389
	/**
390
	 * 
391
	 */
392
	@Override
393
	public void printDiagnosticData() {
394
		// compute if temporal data
395
		if (this.toCompute) {
396
			this.computeDistanceMatrix();
397
		}
398
		
399
		System.out.println("Distance Graph:\n"
400
				+ "" + this.dg + "\n\n"
401
				+ "Distance matrix:\n"
402
				+ "" + this + "\n");
403
	}
404
}
405