tmf: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / linuxtools / tmf / core / synchronization / SyncAlgorithmFullyIncremental.java
1 /*******************************************************************************
2 * Copyright (c) 2013 École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial implementation and API
11 * Francis Giraldeau - Transform computation using synchronization graph
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.tmf.core.synchronization;
15
16 import java.io.IOException;
17 import java.io.ObjectOutputStream;
18 import java.io.Serializable;
19 import java.math.BigDecimal;
20 import java.math.MathContext;
21 import java.util.Collection;
22 import java.util.LinkedHashMap;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.eclipse.linuxtools.internal.tmf.core.synchronization.graph.SyncSpanningTree;
28 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
29 import org.eclipse.linuxtools.tmf.core.event.matching.TmfEventDependency;
30 import org.eclipse.linuxtools.tmf.core.synchronization.ITmfTimestampTransform;
31 import org.eclipse.linuxtools.tmf.core.synchronization.Messages;
32 import org.eclipse.linuxtools.tmf.core.synchronization.SynchronizationAlgorithm;
33 import org.eclipse.linuxtools.tmf.core.synchronization.TimestampTransformFactory;
34 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
35 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
36
37 /**
38 * Class implementing fully incremental trace synchronization approach as
39 * described in
40 *
41 * Masoume Jabbarifar, Michel Dagenais and Alireza Shameli-Sendi,
42 * "Streaming Mode Incremental Clock Synchronization"
43 *
44 * Since the algorithm itself applies to two traces, it is implemented in a
45 * private class, while this public class manages the synchronization between
46 * all traces.
47 *
48 * @author Geneviève Bastien
49 * @since 3.0
50 * @deprecated This class has been moved to internal. Use one of
51 * {@link SynchronizationAlgorithmFactory#getFullyIncrementalAlgorithm()}
52 * method to get this algorithm.
53 */
54 @Deprecated
55 public class SyncAlgorithmFullyIncremental extends SynchronizationAlgorithm {
56
57 /**
58 * Auto-generated serial UID
59 */
60 private static final long serialVersionUID = -1782788842774838830L;
61
62 private static final MathContext fMc = MathContext.DECIMAL128;
63
64 /** @serial */
65 private final List<ConvexHull> fSyncs;
66
67 private SyncSpanningTree fTree = null;
68
69 /**
70 * Initialization of the attributes
71 */
72 public SyncAlgorithmFullyIncremental() {
73 fSyncs = new LinkedList<>();
74 }
75
76 /**
77 * Function called after all matching has been done, to do any post-match
78 * treatment. For this class, it calculates stats, while the data is
79 * available
80 */
81 @Override
82 public void matchingEnded() {
83 getStats();
84 }
85
86 @Override
87 public void init(Collection<ITmfTrace> traces) {
88 ITmfTrace[] traceArr = traces.toArray(new ITmfTrace[traces.size()]);
89 fSyncs.clear();
90 /* Create a convex hull for all trace pairs */
91 // FIXME: is it necessary to make ConvexHull for every pairs up-front?
92 // The ConvexHull seems to be created on the fly in processMatch().
93 for (int i = 0; i < traceArr.length; i++) {
94 for (int j = i + 1; j < traceArr.length; j++) {
95 ConvexHull algo = new ConvexHull(traceArr[i].getHostId(), traceArr[j].getHostId());
96 fSyncs.add(algo);
97 }
98 }
99 }
100
101 @Override
102 protected void processMatch(TmfEventDependency match) {
103 String host1 = match.getSourceEvent().getTrace().getHostId();
104 String host2 = match.getDestinationEvent().getTrace().getHostId();
105
106 /* Process only if source and destination are different */
107 if (host1.equals(host2)) {
108 return;
109 }
110
111 /* Check if a convex hull algorithm already exists for these 2 hosts */
112 ConvexHull algo = null;
113 for (ConvexHull traceSync : fSyncs) {
114 if (traceSync.isForHosts(host1, host2)) {
115 algo = traceSync;
116 }
117 }
118 if (algo == null) {
119 algo = new ConvexHull(host1, host2);
120 fSyncs.add(algo);
121 }
122 algo.processMatch(match);
123 invalidateSyncGraph();
124 }
125
126 private void invalidateSyncGraph() {
127 fTree = null;
128 }
129
130 @Override
131 public ITmfTimestampTransform getTimestampTransform(ITmfTrace trace) {
132 return getTimestampTransform(trace.getHostId());
133 }
134
135 @Override
136 public ITmfTimestampTransform getTimestampTransform(String hostId) {
137 SyncSpanningTree tree = getSyncTree();
138 return tree.getTimestampTransform(hostId);
139 }
140
141 /**
142 * Each convex hull computes the synchronization between 2 given hosts. A
143 * synchronization can be done on multiple hosts that may not all
144 * communicate with each other. We must use another algorithm to determine
145 * which host will be the reference node and what synchronization formula
146 * will be used between each host and this reference node.
147 *
148 * For example, take traces a, b and c where a and c talk to b but do not
149 * know each other ({@literal a <-> b <-> c}). The convex hulls will contain
150 * the formulae between their 2 traces, but if a is the reference node, then
151 * the resulting formula of c would be the composition of {@literal a <-> b}
152 * and {@literal b <-> c}
153 *
154 * @return The synchronization spanning tree for this synchronization
155 */
156 private SyncSpanningTree getSyncTree() {
157 if (fTree == null) {
158 fTree = new SyncSpanningTree();
159 for (ConvexHull traceSync : fSyncs) {
160 SyncQuality q = traceSync.getQuality();
161 if (q == SyncQuality.ACCURATE || q == SyncQuality.APPROXIMATE) {
162 String from = traceSync.getReferenceHost();
163 String to = traceSync.getOtherHost();
164 fTree.addSynchronization(from, to, traceSync.getTimestampTransform(to), traceSync.getAccuracy());
165 }
166 }
167 }
168 return fTree;
169 }
170
171 @Override
172 public SyncQuality getSynchronizationQuality(ITmfTrace trace1, ITmfTrace trace2) {
173 for (ConvexHull traceSync : fSyncs) {
174 if (traceSync.isForHosts(trace1.getHostId(), trace2.getHostId())) {
175 return traceSync.getQuality();
176 }
177 }
178 return SyncQuality.ABSENT;
179 }
180
181 @Override
182 public boolean isTraceSynced(String hostId) {
183 ITmfTimestampTransform t = getTimestampTransform(hostId);
184 return !t.equals(TimestampTransformFactory.getDefaultTransform());
185 }
186
187 @Override
188 public Map<String, Map<String, Object>> getStats() {
189 /*
190 * TODO: Stats, while still accurate, may be misleading now that the
191 * sync tree changes synchronization formula. The stats should use the
192 * tree instead
193 */
194 Map<String, Map<String, Object>> statmap = new LinkedHashMap<>();
195 for (ConvexHull traceSync : fSyncs) {
196 statmap.put(traceSync.getReferenceHost() + " <==> " + traceSync.getOtherHost(), traceSync.getStats()); //$NON-NLS-1$
197 }
198 return statmap;
199 }
200
201 @Override
202 public String toString() {
203 StringBuilder b = new StringBuilder();
204 b.append(getClass().getSimpleName() + " "); //$NON-NLS-1$
205 b.append(fSyncs);
206 return b.toString();
207 }
208
209 /**
210 * This is the actual synchronization algorithm between two traces using
211 * convex hull
212 */
213 private class ConvexHull implements Serializable {
214
215 private static final long serialVersionUID = 8309351175030935291L;
216
217 /**
218 * The list of meaningful points on the upper hull (received by the
219 * reference trace, below in a graph)
220 */
221 private final LinkedList<SyncPoint> fUpperBoundList = new LinkedList<>();
222
223 /**
224 * The list of meaninful points on the lower hull (sent by the reference
225 * trace, above in a graph)
226 */
227 private final LinkedList<SyncPoint> fLowerBoundList = new LinkedList<>();
228
229 /** Points forming the line with maximum slope */
230 private final SyncPoint[] fLmax;
231
232 /** Points forming the line with minimum slope */
233 private final SyncPoint[] fLmin;
234
235 /**
236 * Slopes and ordinate at origin of respectively fLmin, fLmax and the
237 * bisector
238 */
239 private BigDecimal fAlphamin, fBetamax, fAlphamax, fBetamin, fAlpha, fBeta;
240
241 private int fNbMatches, fNbAccurateMatches;
242 private String fReferenceHost = "", fOtherHost = ""; //$NON-NLS-1$//$NON-NLS-2$
243 private SyncQuality fQuality;
244
245 private Map<String, Object> fStats = new LinkedHashMap<>();
246
247 /**
248 * Initialization of the attributes
249 *
250 * @param host1
251 * ID of the first host
252 * @param host2
253 * ID of the second host
254 */
255 public ConvexHull(String host1, String host2) {
256 if (host1.compareTo(host2) > 0) {
257 fReferenceHost = host2;
258 fOtherHost = host1;
259 } else {
260 fReferenceHost = host1;
261 fOtherHost = host2;
262 }
263 fLmax = new SyncPoint[2];
264 fLmin = new SyncPoint[2];
265 fAlpha = BigDecimal.ONE;
266 fAlphamax = BigDecimal.ONE;
267 fAlphamin = BigDecimal.ONE;
268 fBeta = BigDecimal.ZERO;
269 fBetamax = BigDecimal.ZERO;
270 fBetamin = BigDecimal.ZERO;
271 fNbMatches = 0;
272 fNbAccurateMatches = 0;
273 fQuality = SyncQuality.ABSENT; // default quality
274 }
275
276 protected void processMatch(TmfEventDependency match) {
277
278 LinkedList<SyncPoint> boundList, otherBoundList;
279
280 SyncPoint[] line, otherLine;
281 SyncPoint p;
282 int inversionFactor = 1;
283 boolean qualify = false;
284 fNbMatches++;
285
286 /* Initialize data depending on the which hull the match is part of */
287 if (match.getSourceEvent().getTrace().getHostId().compareTo(match.getDestinationEvent().getTrace().getHostId()) > 0) {
288 boundList = fUpperBoundList;
289 otherBoundList = fLowerBoundList;
290 line = fLmin;
291 otherLine = fLmax;
292 p = new SyncPoint(match.getDestinationEvent(), match.getSourceEvent());
293 inversionFactor = 1;
294 } else {
295 boundList = fLowerBoundList;
296 otherBoundList = fUpperBoundList;
297 line = fLmax;
298 otherLine = fLmin;
299 p = new SyncPoint(match.getSourceEvent(), match.getDestinationEvent());
300 inversionFactor = -1;
301 }
302
303 /*
304 * Does the message qualify for the hull, or is in on the wrong side
305 * of the reference line
306 */
307 if ((line[0] == null) || (line[1] == null) || (p.crossProduct(line[0], line[1]) * inversionFactor > 0)) {
308 /*
309 * If message qualifies, verify if points need to be removed
310 * from the hull and add the new point as the maximum reference
311 * point for the line. Also clear the stats that are not good
312 * anymore
313 */
314 fNbAccurateMatches++;
315 qualify = true;
316 removeUselessPoints(p, boundList, inversionFactor);
317 line[1] = p;
318 fStats.clear();
319 }
320
321 /*
322 * Adjust the boundary of the reference line and if one of the
323 * reference point of the other line was removed from the hull, also
324 * adjust the other line
325 */
326 adjustBound(line, otherBoundList, inversionFactor);
327 if ((otherLine[1] != null) && !boundList.contains(otherLine[0])) {
328 adjustBound(otherLine, boundList, inversionFactor * -1);
329 }
330
331 if (qualify) {
332 approximateSync();
333 }
334
335 }
336
337 /**
338 * Calculates slopes and ordinate at origin of fLmax and fLmin to obtain
339 * and approximation of the synchronization at this time
340 */
341 private void approximateSync() {
342
343 /**
344 * Line slopes functions
345 *
346 * Lmax = alpha_max T + beta_min
347 *
348 * Lmin = alpha_min T + beta_max
349 */
350 if ((fLmax[0] != null) || (fLmin[0] != null)) {
351 /**
352 * Do not recalculate synchronization after it is failed. We
353 * keep the last not failed result.
354 */
355 if (getQuality() != SyncQuality.FAIL) {
356 BigDecimal alphamax = fLmax[1].getAlpha(fLmax[0]);
357 BigDecimal alphamin = fLmin[1].getAlpha(fLmin[0]);
358 SyncQuality quality = null;
359
360 if ((fLmax[0] == null) || (fLmin[0] == null)) {
361 quality = SyncQuality.APPROXIMATE;
362 }
363 else if (alphamax.compareTo(alphamin) > 0) {
364 quality = SyncQuality.ACCURATE;
365 } else {
366 /* Lines intersect, not good */
367 quality = SyncQuality.FAIL;
368 }
369 /*
370 * Only calculate sync if this match does not cause failure
371 * of synchronization
372 */
373 if (quality != SyncQuality.FAIL) {
374 fAlphamax = alphamax;
375 fBetamin = fLmax[1].getBeta(fAlphamax);
376 fAlphamin = alphamin;
377 fBetamax = fLmin[1].getBeta(fAlphamin);
378 fAlpha = fAlphamax.add(fAlphamin).divide(BigDecimal.valueOf(2), fMc);
379 fBeta = fBetamin.add(fBetamax).divide(BigDecimal.valueOf(2), fMc);
380 }
381 setQuality(quality);
382 }
383 } else if (((fLmax[0] == null) && (fLmin[1] == null))
384 || ((fLmax[1] == null) && (fLmin[0] == null))) {
385 /* Either there is no upper hull point or no lower hull */
386 setQuality(SyncQuality.INCOMPLETE);
387 }
388 }
389
390 /*
391 * Verify if the line should be adjusted to be more accurate give the
392 * hull
393 */
394 private void adjustBound(SyncPoint[] line, LinkedList<SyncPoint> otherBoundList, int inversionFactor) {
395 SyncPoint minPoint = null, nextPoint;
396 boolean finishedSearch = false;
397
398 /*
399 * Find in the other bound, the origin point of the line, start from
400 * the beginning if the point was lost
401 */
402 int i = Math.max(0, otherBoundList.indexOf(line[0]));
403
404 while ((i < otherBoundList.size() - 1) && !finishedSearch) {
405 minPoint = otherBoundList.get(i);
406 nextPoint = otherBoundList.get(i + 1);
407
408 /*
409 * If the rotation (cross-product) is not optimal, move to next
410 * point as reference for the line (if available)
411 *
412 * Otherwise, the current minPoint is the minPoint of the line
413 */
414 if (minPoint.crossProduct(nextPoint, line[1]) * inversionFactor > 0) {
415 if (nextPoint.getTimeX() < line[1].getTimeX()) {
416 i++;
417 } else {
418 line[0] = null;
419 finishedSearch = true;
420 }
421 } else {
422 line[0] = minPoint;
423 finishedSearch = true;
424 }
425 }
426
427 if (line[0] == null) {
428 line[0] = minPoint;
429 }
430
431 /* Make sure point 0 is before point 1 */
432 if ((line[0] != null) && (line[0].getTimeX() > line[1].getTimeX())) {
433 line[0] = null;
434 }
435 }
436
437 /*
438 * When a point qualifies to be in a hull, we verify if any of the
439 * existing points need to be removed from the hull
440 */
441 private void removeUselessPoints(final SyncPoint p, final LinkedList<SyncPoint> boundList, final int inversionFactor) {
442
443 boolean checkRemove = true;
444
445 while (checkRemove && boundList.size() >= 2) {
446 if (p.crossProduct(boundList.get(boundList.size() - 2), boundList.getLast()) * inversionFactor > 0) {
447 boundList.removeLast();
448 } else {
449 checkRemove = false;
450 }
451 }
452 boundList.addLast(p);
453 }
454
455 public ITmfTimestampTransform getTimestampTransform(String hostId) {
456 if (hostId.equals(fOtherHost) && (getQuality() == SyncQuality.ACCURATE || getQuality() == SyncQuality.APPROXIMATE || getQuality() == SyncQuality.FAIL)) {
457 /* alpha: beta => 1 / fAlpha, -1 * fBeta / fAlpha); */
458 return TimestampTransformFactory.createLinear(BigDecimal.ONE.divide(fAlpha, fMc), BigDecimal.valueOf(-1).multiply(fBeta).divide(fAlpha, fMc));
459 }
460 return TimestampTransformFactory.getDefaultTransform();
461 }
462
463 public SyncQuality getQuality() {
464 return fQuality;
465 }
466
467 public BigDecimal getAccuracy() {
468 return fAlphamax.subtract(fAlphamin);
469 }
470
471 public Map<String, Object> getStats() {
472 if (fStats.size() == 0) {
473 String syncQuality;
474 switch (getQuality()) {
475 case ABSENT:
476 syncQuality = Messages.SyncAlgorithmFullyIncremental_absent;
477 break;
478 case ACCURATE:
479 syncQuality = Messages.SyncAlgorithmFullyIncremental_accurate;
480 break;
481 case APPROXIMATE:
482 syncQuality = Messages.SyncAlgorithmFullyIncremental_approx;
483 break;
484 case INCOMPLETE:
485 syncQuality = Messages.SyncAlgorithmFullyIncremental_incomplete;
486 break;
487 case FAIL:
488 default:
489 syncQuality = Messages.SyncAlgorithmFullyIncremental_fail;
490 break;
491 }
492
493 fStats.put(Messages.SyncAlgorithmFullyIncremental_refhost, fReferenceHost);
494 fStats.put(Messages.SyncAlgorithmFullyIncremental_otherhost, fOtherHost);
495 fStats.put(Messages.SyncAlgorithmFullyIncremental_quality, syncQuality);
496 fStats.put(Messages.SyncAlgorithmFullyIncremental_alpha, fAlpha);
497 fStats.put(Messages.SyncAlgorithmFullyIncremental_beta, fBeta);
498 fStats.put(Messages.SyncAlgorithmFullyIncremental_ub, (fUpperBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fUpperBoundList.size());
499 fStats.put(Messages.SyncAlgorithmFullyIncremental_lb, (fLowerBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fLowerBoundList.size());
500 fStats.put(Messages.SyncAlgorithmFullyIncremental_accuracy, getAccuracy().doubleValue());
501 fStats.put(Messages.SyncAlgorithmFullyIncremental_nbmatch, (fNbMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbMatches);
502 fStats.put(Messages.SyncAlgorithmFullyIncremental_nbacc, (fNbAccurateMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbAccurateMatches);
503 fStats.put(Messages.SyncAlgorithmFullyIncremental_refformula, Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost);
504 fStats.put(Messages.SyncAlgorithmFullyIncremental_otherformula, fAlpha + Messages.SyncAlgorithmFullyIncremental_mult + Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost + Messages.SyncAlgorithmFullyIncremental_add + fBeta);
505 }
506 return fStats;
507
508 }
509
510 public String getReferenceHost() {
511 return fReferenceHost;
512 }
513
514 public String getOtherHost() {
515 return fOtherHost;
516 }
517
518 public boolean isForHosts(String hostId1, String hostId2) {
519 return ((fReferenceHost.equals(hostId1) && fOtherHost.equals(hostId2)) || (fReferenceHost.equals(hostId2) && fOtherHost.equals(hostId1)));
520 }
521
522 private void writeObject(ObjectOutputStream s)
523 throws IOException {
524 /*
525 * Remove calculation data because most of it is not serializable.
526 * We have the statistics anyway
527 */
528 fUpperBoundList.clear();
529 fLowerBoundList.clear();
530 fLmin[0] = null;
531 fLmin[1] = null;
532 fLmax[0] = null;
533 fLmax[1] = null;
534 s.defaultWriteObject();
535 }
536
537 @SuppressWarnings("nls")
538 @Override
539 public String toString() {
540 StringBuilder b = new StringBuilder();
541 b.append("Between " + fReferenceHost + " and " + fOtherHost + " [");
542 b.append(" alpha " + fAlpha + " beta " + fBeta + " ]");
543 return b.toString();
544 }
545
546 private void setQuality(SyncQuality fQuality) {
547 this.fQuality = fQuality;
548 }
549
550 }
551
552 /**
553 * Private class representing a point to synchronize on a graph. The x axis
554 * is the timestamp of the event from the reference trace while the y axis
555 * is the timestamp of the event on the other trace
556 */
557 private class SyncPoint {
558 private final ITmfTimestamp x, y;
559
560 public SyncPoint(ITmfEvent ex, ITmfEvent ey) {
561 x = ex.getTimestamp();
562 y = ey.getTimestamp();
563 }
564
565 public long getTimeX() {
566 return x.getValue();
567 }
568
569 /**
570 * Calculate a cross product of 3 points:
571 *
572 * If the cross-product < 0, then p, pa, pb are clockwise
573 *
574 * If the cross-product > 0, then p, pa, pb are counter-clockwise
575 *
576 * If cross-product == 0, then they are in a line
577 *
578 * @param pa
579 * First point
580 * @param pb
581 * Second point
582 * @return The cross product
583 */
584 public long crossProduct(SyncPoint pa, SyncPoint pb) {
585 long cp = ((pa.x.getValue() - x.getValue()) * (pb.y.getValue() - y.getValue()) - (pa.y.getValue() - y.getValue()) * (pb.x.getValue() - x.getValue()));
586 return cp;
587 }
588
589 /*
590 * Gets the alpha (slope) between two points
591 */
592 public BigDecimal getAlpha(SyncPoint p1) {
593 if (p1 == null) {
594 return BigDecimal.ONE;
595 }
596 BigDecimal deltay = BigDecimal.valueOf(y.getValue() - p1.y.getValue());
597 BigDecimal deltax = BigDecimal.valueOf(x.getValue() - p1.x.getValue());
598 if (deltax.equals(BigDecimal.ZERO)) {
599 return BigDecimal.ONE;
600 }
601 return deltay.divide(deltax, fMc);
602 }
603
604 /*
605 * Get the beta value (when x = 0) of the line given alpha
606 */
607 public BigDecimal getBeta(BigDecimal alpha) {
608 return BigDecimal.valueOf(y.getValue()).subtract(alpha.multiply(BigDecimal.valueOf(x.getValue()), fMc));
609 }
610
611 @Override
612 public String toString() {
613 return String.format("%s (%s, %s)", this.getClass().getCanonicalName(), x, y); //$NON-NLS-1$
614 }
615 }
616
617 private void writeObject(ObjectOutputStream s)
618 throws IOException {
619 /*
620 * Remove the tree because it is not serializable
621 */
622 fTree = null;
623 s.defaultWriteObject();
624 }
625
626 }
This page took 0.045968 seconds and 5 git commands to generate.