| /******************************************************************************* |
| * Copyright (c) 2019 École Polytechnique de Montréal |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License v1.0 which |
| * accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.incubator.analysis.core.weighted.tree; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.diff.DifferentialWeightedTree; |
| import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.diff.DifferentialWeightedTreeProvider; |
| import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.diff.DifferentialWeightedTreeSet; |
| import org.eclipse.tracecompass.tmf.core.util.Pair; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| /** |
| * Utility methods to operate on {@link WeightedTree} objects |
| * |
| * @author Geneviève Bastien |
| */ |
| public final class WeightedTreeUtils { |
| |
| private WeightedTreeUtils() { |
| // Nothing to do |
| } |
| |
| /** |
| * Does the differential between 2 weighted trees, ie what happened in tree2 |
| * differently than in tree1. The base weight come from the second tree and |
| * the differential value will show the difference with the first tree. |
| * |
| * @param <T> |
| * The type of element in the tree |
| * @param first |
| * The tree that will be differentiated. |
| * @param second |
| * The tree to use as the base |
| * @return The differential weighted tree |
| */ |
| public static <@NonNull T> Collection<DifferentialWeightedTree<T>> diffTrees(Collection<WeightedTree<T>> first, Collection<WeightedTree<T>> second) { |
| List<DifferentialWeightedTree<T>> diffTrees = new ArrayList<>(); |
| for (WeightedTree<T> base : second) { |
| T object = base.getObject(); |
| // Find the equivalent tree in the first collection |
| WeightedTree<T> other = findObject(first, object); |
| double diffWeight = other == null ? Double.NaN : (double) (base.getWeight() - other.getWeight()) / other.getWeight(); |
| DifferentialWeightedTree<@NonNull T> diffTree = new DifferentialWeightedTree<>(base, object, base.getWeight(), diffWeight); |
| diffTrees.add(diffTree); |
| |
| // Make the differential of the children |
| for (DifferentialWeightedTree<T> childTree : diffTrees(other == null ? Collections.emptyList() : other.getChildren(), base.getChildren())) { |
| diffTree.addChild(childTree); |
| } |
| } |
| return diffTrees; |
| } |
| |
| /** |
| * Does the differential between 2 weighted tree sets, ie for each |
| * comparable elements, what happened in tree set 2 differently than in tree |
| * set 1. The base weight come from the second tree set and the differential |
| * value will show the difference with the first tree. |
| * <p> |
| * Calling this method assumes the tree sets are comparable. It is the |
| * caller's responsibility to make sure the parameters make sense. If the 2 |
| * tree sets come from different {@link IWeightedTreeProvider}, they should |
| * be of similar types, otherwise, the comparison may not make sense. |
| * </p> |
| * <p> |
| * The elements of each tree set are paired as follows: |
| * </p> |
| * <p> |
| * 1- If there is only one element in each tree, they will be paired. |
| * </p> |
| * <p> |
| * 2- If the same elements are present in both tree sets, they will be |
| * paired, all other elements are ignored |
| * </p> |
| * <p> |
| * 3- If no elements are paired, they will be paired by name (and |
| * hierarchical names, if it applies). Unmatched elements will be ignored. |
| * </p> |
| * <p> |
| * If elements are not paired at this point, it will return |
| * <code>null</code> |
| * </p> |
| * |
| * @param <N> |
| * The type of element in the tree |
| * @param provider |
| * The base provider of one of the trees, it will be used by the |
| * differential weighted tree provider to display the metrics and |
| * titles, etc.. It could be the provider of the second tree set, |
| * as it serves as the base values. |
| * @param first |
| * The first treeset to compare to |
| * @param second |
| * The second treeset to compare. |
| * @return A differential weighted tree provider wrapping the resulting tree |
| * set, or <code>null</code> if the 2 treesets have no elements in |
| * common |
| */ |
| public static <@NonNull N> @Nullable DifferentialWeightedTreeProvider<N> diffTreeSets(IWeightedTreeProvider<N, ?, WeightedTree<N>> provider, |
| IWeightedTreeSet<N, @NonNull ?, WeightedTree<N>> first, |
| IWeightedTreeSet<N, @NonNull ?, WeightedTree<N>> second) { |
| Collection<Pair<@NonNull ?, @NonNull ?>> pairedElements = pairElementsFromTrees(first, second); |
| if (pairedElements.isEmpty()) { |
| return null; |
| } |
| DifferentialWeightedTreeSet<N> treeSet = new DifferentialWeightedTreeSet<>(); |
| for (Pair<@NonNull ?, @NonNull ?> pair : pairedElements) { |
| Collection<WeightedTree<N>> trees1 = first.getTreesFor(pair.getFirst()); |
| Collection<WeightedTree<N>> trees2 = second.getTreesFor(pair.getSecond()); |
| Collection<DifferentialWeightedTree<N>> diffTrees = WeightedTreeUtils.diffTrees(trees1, trees2); |
| for (DifferentialWeightedTree<N> tree: diffTrees) { |
| treeSet.addWeightedTree(pair.getFirst(), tree); |
| } |
| } |
| |
| return new DifferentialWeightedTreeProvider<>(provider, treeSet); |
| } |
| |
| private static <@NonNull N> Collection<Pair<@NonNull ?, @NonNull ?>> pairElementsFromTrees(IWeightedTreeSet<N, @NonNull ?, WeightedTree<N>> first, IWeightedTreeSet<N, @NonNull ?, WeightedTree<N>> second) { |
| Collection<@NonNull ?> elements1 = first.getElements(); |
| Collection<@NonNull ?> elements2 = second.getElements(); |
| // If there is only one element and it is not a tree, pair it |
| if (elements1.size() == 1 && elements2.size() == 1) { |
| @NonNull Object element1 = elements1.iterator().next(); |
| @NonNull Object element2 = elements2.iterator().next(); |
| if (!(element1 instanceof ITree) && !(element2 instanceof ITree)) { |
| return ImmutableList.of(new Pair(element1, element2)); |
| } |
| } |
| |
| // Try to find equal elements in both trees |
| Collection<Pair<@NonNull ?, @NonNull ?>> pairedElements = pairEqualElements(elements1, elements2); |
| if (!pairedElements.isEmpty()) { |
| return pairedElements; |
| } |
| |
| // Compare ITree elements by names |
| pairedElements = pairSameNameElements(elements1, elements2); |
| return pairedElements; |
| } |
| |
| private static Collection<Pair<@NonNull ?, @NonNull ?>> pairEqualElements(Collection<@NonNull ?> elements1, Collection<@NonNull ?> elements2) { |
| List<Pair<@NonNull ?, @NonNull ?>> pairedElements = new ArrayList<>(); |
| for (@NonNull Object element1 : elements1) { |
| for (@NonNull Object element2 : elements2) { |
| if (element1.equals(element2)) { |
| pairedElements.add(new Pair<>(element1, element1)); |
| if (element1 instanceof ITree && element2 instanceof ITree) { |
| pairedElements.addAll(pairEqualElements(((ITree) element1).getChildren(), ((ITree) element2).getChildren())); |
| } |
| break; |
| } |
| } |
| } |
| return pairedElements; |
| } |
| |
| private static Collection<Pair<@NonNull ?, @NonNull ?>> pairSameNameElements(Collection<@NonNull ?> elements1, Collection<?> elements2) { |
| List<Pair<@NonNull ?, @NonNull ?>> pairedElements = new ArrayList<>(); |
| for (@NonNull Object element1 : elements1) { |
| if (!(element1 instanceof ITree)) { |
| continue; |
| } |
| for (@NonNull Object element2 : elements2) { |
| if (!(element2 instanceof ITree)) { |
| continue; |
| } |
| if (((ITree) element1).getName().equals(((ITree) element2).getName())) { |
| pairedElements.add(new Pair<>(element1, element2)); |
| pairedElements.addAll(pairSameNameElements(((ITree) element1).getChildren(), ((ITree) element2).getChildren())); |
| break; |
| } |
| } |
| } |
| return pairedElements; |
| } |
| |
| private static @Nullable <@NonNull T> WeightedTree<T> findObject(Collection<WeightedTree<T>> tree, @NonNull T object) { |
| for (WeightedTree<T> other : tree) { |
| if (other.getObject().equals(object)) { |
| return other; |
| } |
| } |
| return null; |
| } |
| |
| } |