blob: 80e4ca819fb8a6800e9a9e854915f43fcc8368bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import org.eclipse.qvtd.pivot.qvtschedule.CyclicPartition;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleConstants;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* The CyclicPartitionsAnalysis identifies the Partitions and TraceClassAnalysis' that contribute to a cycle.
*
* This forms the basis of the cycle encpasulation and parallel schedule.
*/
public class CyclicPartitionsAnalysis
{
protected final @NonNull TransformationPartitioner transformationPartitioner;
protected final @NonNull Iterable<@NonNull PartitionAnalysis> leafPartitionAnalyses;
/**
* Mapping to the cycle analysis that identifies the cycle involving each mapping partitioner.
*/
protected final @NonNull Map<@NonNull PartitionAnalysis, @NonNull CyclicPartition> partitionAnalysis2cyclicPartition = new HashMap<>();
/**
* Mapping to the cycle analysis that identifies the cycle involving each trace class analysis.
*/
protected final @NonNull Map<@NonNull TraceClassPartitionAnalysis, @NonNull CyclicPartition> traceClassAnalysis2cyclicPartition = new HashMap<>();
public CyclicPartitionsAnalysis(@NonNull TransformationPartitioner transformationPartitioner, @NonNull Iterable<@NonNull PartitionAnalysis> leafPartitions) {
this.transformationPartitioner = transformationPartitioner;
this.leafPartitionAnalyses = leafPartitions;
}
/**
* Return a map of the RegionAnalyses that form a cycle including each Partition.
*
* NB cycles may involve trace classes and their trace class properties.
*/
public @NonNull RootPartitionAnalysis analyze(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis) {
Map<@NonNull PartitionAnalysis, @NonNull Set<@NonNull PartitionAnalysis>> leafPartitionAnalysis2predecessors = CompilerUtil.computeTransitivePredecessors(leafPartitionAnalyses);
Map<@NonNull PartitionAnalysis, @NonNull Set<@NonNull PartitionAnalysis>> leafPartitionAnalysis2successors = CompilerUtil.computeTransitiveSuccessors(leafPartitionAnalysis2predecessors);
Set<@NonNull Set<@NonNull PartitionAnalysis>> intersections = new HashSet<>();
for (@NonNull PartitionAnalysis leafPartitionAnalysis : leafPartitionAnalyses) {
Set<@NonNull PartitionAnalysis> predecessors = leafPartitionAnalysis2predecessors.get(leafPartitionAnalysis);
Set<@NonNull PartitionAnalysis> successors = leafPartitionAnalysis2successors.get(leafPartitionAnalysis);
Set<@NonNull PartitionAnalysis> intersection = new HashSet<>(predecessors);
intersection.retainAll(successors);
if (!intersection.isEmpty()) {
intersections.add(intersection);
}
}
//
if (intersections.isEmpty()) {
// Set<@NonNull TraceClassPartitionAnalysis> cyclicTraceClassAnalyses = computeTraceClassAnalysisDependencies(partitionedTransformationAnalysis, leafPartitionAnalyses);
// Set<@NonNull TracePropertyPartitionAnalysis> cyclicTracePropertyAnalyses = computeTracePropertyAnalysisDependencies(partitionedTransformationAnalysis, leafPartitionAnalyses);
String rootName = QVTscheduleConstants.ROOT_MAPPING_NAME;
return RootPartitionAnalysis.createRootPartitionAnalysis(partitionedTransformationAnalysis, transformationPartitioner.getTransformationAnalysis(), rootName, leafPartitionAnalysis2predecessors);
}
intersections.add(Sets.newHashSet(leafPartitionAnalyses));
return createAcyclicPartitionHierarchy(partitionedTransformationAnalysis, intersections, leafPartitionAnalysis2predecessors);
}
private @NonNull Set<@NonNull TraceClassPartitionAnalysis> computeTraceClassAnalysisDependencies(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, @NonNull Iterable<@NonNull PartitionAnalysis> nestedPartitions) {
Set<@NonNull TraceClassPartitionAnalysis> consumedTraceClassAnalyses = new HashSet<>();
Set<@NonNull TraceClassPartitionAnalysis> superProducedTraceClassAnalyses = new HashSet<>();
for (@NonNull PartitionAnalysis nestedPartitionAnalysis : nestedPartitions) {
Iterable<@NonNull TraceClassPartitionAnalysis> consumedTraceClassAnalyses2 = nestedPartitionAnalysis.getConsumedTraceClassAnalyses();
if (consumedTraceClassAnalyses2 != null) {
Iterables.addAll(consumedTraceClassAnalyses, consumedTraceClassAnalyses2);
}
Iterable<@NonNull TraceClassPartitionAnalysis> superProducedTraceClassAnalyses2 = nestedPartitionAnalysis.getSuperProducedTraceClassAnalyses();
if (superProducedTraceClassAnalyses2 != null) {
Iterables.addAll(superProducedTraceClassAnalyses, superProducedTraceClassAnalyses2);
}
}
Set<@NonNull TraceClassPartitionAnalysis> cyclicTraceClassAnalyses = new HashSet<>(consumedTraceClassAnalyses);
cyclicTraceClassAnalyses.retainAll(superProducedTraceClassAnalyses);
return cyclicTraceClassAnalyses;
}
/* private @NonNull Set<@NonNull TracePropertyPartitionAnalysis> computeTracePropertyAnalysisDependencies(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, @NonNull Iterable<@NonNull PartitionAnalysis> nestedPartitions) {
Set<@NonNull TracePropertyPartitionAnalysis> consumedTracePropertyAnalyses = new HashSet<>();
Set<@NonNull TracePropertyPartitionAnalysis> producedTracePropertyAnalyses = new HashSet<>();
for (@NonNull PartitionAnalysis nestedPartitionAnalysis : nestedPartitions) {
Iterable<@NonNull TracePropertyPartitionAnalysis> consumedTracePropertyAnalyses2 = nestedPartitionAnalysis.getConsumedTracePropertyAnalyses();
if (consumedTracePropertyAnalyses2 != null) {
Iterables.addAll(consumedTracePropertyAnalyses, consumedTracePropertyAnalyses2);
}
Iterable<@NonNull TracePropertyPartitionAnalysis> producedTracePropertyAnalyses2 = nestedPartitionAnalysis.getProducedTracePropertyAnalyses();
if (producedTracePropertyAnalyses2 != null) {
Iterables.addAll(producedTracePropertyAnalyses, producedTracePropertyAnalyses2);
}
}
Set<@NonNull TracePropertyPartitionAnalysis> cyclicTracePropertyAnalyses = new HashSet<>(consumedTracePropertyAnalyses);
cyclicTracePropertyAnalyses.retainAll(producedTracePropertyAnalyses);
return cyclicTracePropertyAnalyses;
} */
/**
* Return a RootPartition/CyclicPartition hierarchy with a CyclicPartition wrapping the non-last partitionings and a
* RootPartition for the last partitionings.
*
* NB The partitionings is a working collection modified on return.
* @param leafPartition2successors
*/
private @NonNull RootPartitionAnalysis createAcyclicPartitionHierarchy(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, @NonNull Iterable<@NonNull Set<@NonNull PartitionAnalysis>> partitionings,
@NonNull Map<@NonNull PartitionAnalysis, @NonNull Set<@NonNull PartitionAnalysis>> partition2predecessors) {
List<@NonNull Set<@NonNull PartitionAnalysis>> sortedPartitionings = new ArrayList<>();
Map<@NonNull Set<@NonNull PartitionAnalysis>, @NonNull Set<@NonNull PartitionAnalysis>> partitioning2predecessors = new HashMap<>();
for (@NonNull Set<@NonNull PartitionAnalysis> partitioning : partitionings) {
sortedPartitionings.add(partitioning);
Set<@NonNull PartitionAnalysis> predecessors = new HashSet<>();
for (@NonNull PartitionAnalysis partitionAnalysis : partitioning) {
predecessors.addAll(partition2predecessors.get(partitionAnalysis));
}
partitioning2predecessors.put(partitioning, predecessors);
}
// Collections.sort(sortedPartitionings, QVTbaseUtil.CollectionSizeComparator.INSTANCE); // Smallest first
Collections.sort(sortedPartitionings, new Comparator<@NonNull Set<@NonNull PartitionAnalysis>>() {
@Override
public int compare(@NonNull Set<@NonNull PartitionAnalysis> o1, @NonNull Set<@NonNull PartitionAnalysis> o2) {
Set<@NonNull PartitionAnalysis> predecessors1 = partitioning2predecessors.get(o1);
Set<@NonNull PartitionAnalysis> predecessors2 = partitioning2predecessors.get(o2);
assert predecessors1 != null;
assert predecessors2 != null;
int s1 = predecessors1.size();
int s2 = predecessors2.size();
if (s1 == s2) {
s1 = o1.size();
s2 = o2.size();
}
return s1 - s2;
}});
//
// A nested cycle is necessarily fully contained by a nesting cycle. We can therefore steadily replace
// each set of nested elements by a cyclic element in the nesting context. The smallest first ordering
// does not need to be recomputed after each nesting simplification since nested candidates continue to
// precede their potential nestings.
//
List<@NonNull CompositePartitionAnalysis> acyclicPartitionHierarchy = new ArrayList<>();
int iMax = sortedPartitionings.size();
assert iMax >= 2;
assert sortedPartitionings.get(iMax-1).equals(Sets.newHashSet(leafPartitionAnalyses));
for (int i = 0; i < iMax-1; i++) {
Set<@NonNull PartitionAnalysis> nestedPartitioning = sortedPartitionings.get(i);
String cycleName = "«cycle-" + i + "»";
// CyclicPartition nestedCyclicPartition = createCyclicPartition(cycleName, nestedPartitioning, partition2predecessors, partition2successors);
Set<@NonNull TraceClassPartitionAnalysis> cyclicTraceClassAnalyses = computeTraceClassAnalysisDependencies(partitionedTransformationAnalysis, nestedPartitioning);
// Set<@NonNull TracePropertyPartitionAnalysis> cyclicTracePropertyAnalyses = computeTracePropertyAnalysisDependencies(partitionedTransformationAnalysis, nestedPartitioning);
CyclicPartitionAnalysis nestedCyclicPartitionAnalysis = CyclicPartitionAnalysis.createCyclicPartitionAnalysis(partitionedTransformationAnalysis, cycleName, nestedPartitioning, partition2predecessors);
CyclicPartition nestedCyclicPartition = nestedCyclicPartitionAnalysis.getPartition();
acyclicPartitionHierarchy.add(nestedCyclicPartitionAnalysis);
//
for (@NonNull PartitionAnalysis nestedPartition : nestedPartitioning) {
CyclicPartition oldCycleAnalysis = partitionAnalysis2cyclicPartition.put(nestedPartition, nestedCyclicPartition);
assert oldCycleAnalysis == null;
partition2predecessors.remove(nestedPartition);
}
partition2predecessors.put(nestedCyclicPartitionAnalysis, nestedCyclicPartitionAnalysis.getExplicitPredecessors());
//
for (@NonNull TraceClassPartitionAnalysis cyclicTraceClassAnalysis : cyclicTraceClassAnalyses) {
CyclicPartition oldCycleAnalysis = traceClassAnalysis2cyclicPartition.put(cyclicTraceClassAnalysis, nestedCyclicPartition);
assert oldCycleAnalysis == null;
}
//
// Replace the nestedPartitioning partitions by nestedCyclicPartition in the potentially nesting cycles
//
for (int j = i+1; j < iMax; j++) {
Set<@NonNull PartitionAnalysis> nestingPartitioning = sortedPartitionings.get(j);
int oldSize = nestingPartitioning.size();
if (nestingPartitioning.removeAll(nestedPartitioning)) {
int newSize = nestingPartitioning.size();
assert (oldSize - newSize) == nestedPartitioning.size();
nestingPartitioning.add(nestedCyclicPartitionAnalysis);
}
}
for (@NonNull PartitionAnalysis partition : partition2predecessors.keySet()) {
Set<@NonNull PartitionAnalysis> predecessors = partition2predecessors.get(partition);
assert predecessors != null;
int oldSize = predecessors.size();
if (predecessors.removeAll(nestedPartitioning)) {
int newSize = predecessors.size();
assert (oldSize - newSize) == nestedPartitioning.size();
predecessors.add(nestedCyclicPartitionAnalysis);
}
}
}
// Set<@NonNull PartitionAnalysis> rootPartitioning = sortedPartitionings.get(iMax-1);
// Set<@NonNull TraceClassPartitionAnalysis> rootTraceClassAnalyses = computeTraceClassAnalysisDependencies(partitionedTransformationAnalysis, rootPartitioning);
// Set<@NonNull TracePropertyPartitionAnalysis> rootTracePropertyAnalyses = computeTracePropertyAnalysisDependencies(partitionedTransformationAnalysis, rootPartitioning);
String rootName = QVTscheduleConstants.ROOT_MAPPING_NAME;
RootPartitionAnalysis rootPartitionAnalysis = RootPartitionAnalysis.createRootPartitionAnalysis(partitionedTransformationAnalysis, transformationPartitioner.getTransformationAnalysis(), rootName, partition2predecessors);
acyclicPartitionHierarchy.add(rootPartitionAnalysis);
if (TransformationPartitioner.CYCLES.isActive()) {
showCycles(partitionedTransformationAnalysis, acyclicPartitionHierarchy);
}
return rootPartitionAnalysis;
}
protected void showCycles(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, Iterable<@NonNull CompositePartitionAnalysis> cyclicPartitionAnalyses) {
if (Iterables.isEmpty(cyclicPartitionAnalyses)) {
TransformationPartitioner.CYCLES.println("No cycles");
}
else {
for (@NonNull CompositePartitionAnalysis cyclicPartitionAnalysis : cyclicPartitionAnalyses) {
StringBuilder s = new StringBuilder();
s.append("\n Partitions:");
List<@NonNull PartitionAnalysis> partitions2 = Lists.newArrayList(cyclicPartitionAnalysis.getPartitionAnalyses());
Collections.sort(partitions2, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull PartitionAnalysis partition : partitions2) {
s.append("\n\t" + partition);
Iterable<@NonNull ? extends TraceClassPartitionAnalysis> consumedTraceClassAnalyses = partition.getConsumedTraceClassAnalyses();
if (consumedTraceClassAnalyses != null) {
for (@NonNull TraceClassPartitionAnalysis traceClassAnalysis : consumedTraceClassAnalyses) {
s.append("\n\t =>" + traceClassAnalysis);
}
}
Iterable<@NonNull ? extends TraceClassPartitionAnalysis> producedTraceClassAnalyses = partition.getProducedTraceClassAnalyses();
if (producedTraceClassAnalyses != null) {
for (@NonNull TraceClassPartitionAnalysis traceClassAnalysis : producedTraceClassAnalyses) {
s.append("\n\t <=" + traceClassAnalysis);
}
}
Iterable<@NonNull ? extends TracePropertyPartitionAnalysis> consumedTracePropertyAnalyses = partition.getConsumedTracePropertyAnalyses();
if (consumedTracePropertyAnalyses != null) {
for (@NonNull TracePropertyPartitionAnalysis tracePropertyAnalysis : consumedTracePropertyAnalyses) {
s.append("\n\t =>" + tracePropertyAnalysis);
}
}
Iterable<@NonNull ? extends TracePropertyPartitionAnalysis> producedTracePropertyAnalyses = partition.getProducedTracePropertyAnalyses();
if (producedTracePropertyAnalyses != null) {
for (@NonNull TracePropertyPartitionAnalysis tracePropertyAnalysis : producedTracePropertyAnalyses) {
s.append("\n\t <=" + tracePropertyAnalysis);
}
}
}
/* s.append("\n TraceClassAnalyses:");
List<@NonNull TraceClassPartitionAnalysis> traceClassAnalyses = Lists.newArrayList(cyclicPartitionAnalysis.getTraceClassAnalyses());
Collections.sort(traceClassAnalyses, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull TraceClassPartitionAnalysis traceClassAnalysis : traceClassAnalyses) {
s.append("\n\t" + traceClassAnalysis);
}
s.append("\n TracePropertyAnalyses:");
List<@NonNull TracePropertyPartitionAnalysis> tracePropertyAnalyses = Lists.newArrayList(cyclicPartitionAnalysis.getTracePropertyAnalyses());
Collections.sort(tracePropertyAnalyses, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull TracePropertyPartitionAnalysis tracePropertyAnalysis : tracePropertyAnalyses) {
s.append("\n\t" + tracePropertyAnalysis);
} */
TransformationPartitioner.CYCLES.println(s.toString());
}
}
}
}