blob: d4283fa70f958729668b7c73473fa8ac797f9cf4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 Willink Transformations and others.
* 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
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
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.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.TracingOption;
import org.eclipse.qvtd.compiler.CompilerChainException;
import org.eclipse.qvtd.compiler.CompilerConstants;
import org.eclipse.qvtd.compiler.CompilerProblem;
import org.eclipse.qvtd.compiler.ProblemHandler;
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.ScheduleManager;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
import org.eclipse.qvtd.pivot.qvtschedule.MappingRegion;
import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.PropertyDatum;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import com.google.common.collect.Iterables;
/**
* The TransformationPartitioner supervises a MappingPartitioner for each mapping region. It provides
* the global analysis to support the local partitioning.
*/
public class TransformationPartitioner
{
public static final @NonNull TracingOption CYCLES = new TracingOption(CompilerConstants.PLUGIN_ID, "qvts2qvts/partition/cycles");
public static final @NonNull TracingOption DISCRIMINATION = new TracingOption(CompilerConstants.PLUGIN_ID, "qvts2qvts/partition/discrimination");
public static final @NonNull TracingOption PREDECESSORS = new TracingOption(CompilerConstants.PLUGIN_ID, "qvts2qvts/partition/predecessors");
public static final @NonNull TracingOption SUCCESSORS = new TracingOption(CompilerConstants.PLUGIN_ID, "qvts2qvts/partition/successors");
public static @NonNull Iterable<@NonNull MappingRegion> partition(@NonNull ScheduleManager scheduleManager, @NonNull ProblemHandler problemHandler, @NonNull Iterable<@NonNull ? extends Region> activeRegions) throws CompilerChainException {
TransformationPartitioner transformationPartitioner = new TransformationPartitioner(scheduleManager, problemHandler, activeRegions);
return transformationPartitioner.partition();
}
protected final @NonNull ScheduleManager scheduleManager;
protected final @NonNull ProblemHandler problemHandler;
protected final @NonNull Iterable<@NonNull ? extends Region> activeRegions;
/**
* The partitioner for each region.
*/
private final @NonNull Map<@NonNull MappingRegion, @NonNull MappingPartitioner> region2mappingPartitioner = new HashMap<>();
/**
* The partitioners in the original activeRegions order.
*/
private final @NonNull List<@NonNull MappingPartitioner> mappingPartitioners = new ArrayList<>();
/**
* The TraceClassAnalysis for each trace class.
*/
private final @NonNull Map<@NonNull CompleteClass, @NonNull TraceClassAnalysis> completeClass2traceClassAnalysis = new HashMap<>();
/**
* All speculated-trace to realized node properties that are automatically assignable once their speculation
* is validated mapped to the regions that speculate them.
*/
private final @NonNull Map<@NonNull Property, @NonNull List<@NonNull MappingRegion>> corrolaryProperty2regions = new HashMap<>();
/**
* Mapping to the cycle analysis that identifies the cycle involvong each mapping partitioner.
*/
private @Nullable Map<@NonNull MappingPartitioner, @NonNull CycleAnalysis> mappingPartitioner2cycleAnalysis = null;
/**
* Mapping to the cycle analysis that identifies the cycle involvong each trace class analysis.
*/
private @Nullable Map<@NonNull TraceClassAnalysis, @NonNull CycleAnalysis> traceClassAnalysis2cycleAnalysis = null;
public TransformationPartitioner(@NonNull ScheduleManager scheduleManager, @NonNull ProblemHandler problemHandler, @NonNull Iterable<@NonNull ? extends Region> activeRegions) {
this.scheduleManager = scheduleManager;
this.problemHandler = problemHandler;
this.activeRegions = activeRegions;
}
public @NonNull TraceClassAnalysis addConsumer(@NonNull CompleteClass completeClass, @NonNull MappingPartitioner consumer) {
TraceClassAnalysis middleAnalysis = completeClass2traceClassAnalysis.get(completeClass);
if (middleAnalysis == null) {
middleAnalysis = new TraceClassAnalysis(this, completeClass);
completeClass2traceClassAnalysis.put(completeClass, middleAnalysis);
}
middleAnalysis.addConsumer(consumer);
return middleAnalysis;
}
public void addCorrolary(@NonNull Property property, @NonNull MappingRegion region) {
List<@NonNull MappingRegion> regions = corrolaryProperty2regions.get(property);
if (regions == null) {
regions = new ArrayList<>();
corrolaryProperty2regions.put(property, regions);
}
if (!regions.contains(region)) {
regions.add(region);
}
}
public void addProblem(@NonNull CompilerProblem problem) {
problemHandler.addProblem(problem);
}
public @NonNull TraceClassAnalysis addProducer(@NonNull CompleteClass completeClass, @NonNull MappingPartitioner producer) {
TraceClassAnalysis middleAnalysis = completeClass2traceClassAnalysis.get(completeClass);
if (middleAnalysis == null) {
middleAnalysis = new TraceClassAnalysis(this, completeClass);
completeClass2traceClassAnalysis.put(completeClass, middleAnalysis);
}
middleAnalysis.addProducer(producer);
return middleAnalysis;
}
/**
* Return a map of the MappingPartitioners that form a cycle including each MappingPartitioner.
*/
private void computeCycles() {
Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> partitioner2predecessors = computeTransitivePredecessors();
Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> partitioner2successors = computeTransitiveSuccessors();
Set<@NonNull Set<@NonNull MappingPartitioner>> allCycleElements = new HashSet<>();
for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
Set<@NonNull MappingPartitioner> newCycleElements = new HashSet<>(partitioner2predecessors.get(mappingPartitioner));
newCycleElements.retainAll(partitioner2successors.get(mappingPartitioner));
if (!newCycleElements.isEmpty()) {
allCycleElements.add(newCycleElements);
}
}
//
List<@NonNull Set<@NonNull MappingPartitioner>> prunedCycleElements = new ArrayList<>(allCycleElements);
for (int i = prunedCycleElements.size(); --i >= 0; ) {
Set<@NonNull MappingPartitioner> iCycleElements = prunedCycleElements.get(i);
boolean pruneIt = false;
for (int j = i; --j >= 0; ) {
Set<@NonNull MappingPartitioner> jCycleElements = prunedCycleElements.get(j);
if (QVTbaseUtil.containsAny(jCycleElements, iCycleElements) && jCycleElements.addAll(iCycleElements)) {
pruneIt = true;
}
}
if (pruneIt) {
prunedCycleElements.remove(i);
}
}
//
/* for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
Set<@NonNull MappingPartitioner> newCycleElements = new HashSet<>(partitioner2predecessors.get(mappingPartitioner));
newCycleElements.retainAll(partitioner2successors.get(mappingPartitioner));
if (!newCycleElements.isEmpty() && !allCycleElements.contains(newCycleElements)) {
List<@NonNull Set<@NonNull MappingPartitioner>> redundantCycleElements = null;
for (@NonNull Set<@NonNull MappingPartitioner> oldCycleElements : allCycleElements) {
if (QVTbaseUtil.containsAny(oldCycleElements, newCycleElements)) {
if (redundantCycleElements == null) {
redundantCycleElements = new ArrayList<>();
}
redundantCycleElements.add(oldCycleElements);
newCycleElements.addAll(oldCycleElements);
}
}
if (redundantCycleElements != null) {
allCycleElements.removeAll(redundantCycleElements);
}
allCycleElements.add(newCycleElements);
}
} */
//
List<@NonNull CycleAnalysis> cycleAnalyses = new ArrayList<>();
for (@NonNull Set<@NonNull MappingPartitioner> cycleElements : prunedCycleElements) {
cycleAnalyses.add(createCycleAnalysis(cycleElements));
}
if (CYCLES.isActive()) {
if (cycleAnalyses.isEmpty()) {
CYCLES.println("No cycles");
}
else {
for (@NonNull CycleAnalysis cycleAnalysis : cycleAnalyses) {
StringBuilder s = new StringBuilder();
s.append("\n MappingPartitioners:");
for (@NonNull MappingPartitioner mappingPartitioner : cycleAnalysis.getMappingPartitioners()) {
s.append("\n\t" + mappingPartitioner);
}
s.append("\n TraceClassAnalyses:");
for (@NonNull TraceClassAnalysis traceClassAnalysis : cycleAnalysis.getTraceClassAnalyses()) {
s.append("\n\t" + traceClassAnalysis);
}
CYCLES.println(s.toString());
}
}
}
}
private void computeCyclicTraceClasses() {
//
// Each mapping partitioner that consumes no trace class, is an acyclic producer.
//
Set<@NonNull MappingPartitioner> acyclicProducers = new HashSet<>();
for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
Iterable<@NonNull TraceClassAnalysis> consumedTraceClassAnalyses = mappingPartitioner.getConsumedTraceClassAnalyses();
if (consumedTraceClassAnalyses == null) {
acyclicProducers.add(mappingPartitioner);
}
}
/* for (@NonNull TraceClassAnalysis middleAnalysis : class2middleAnalysis.values()) {
CompleteClass traceClass = middleAnalysis.getTraceClass();
for (@NonNull CompleteClass superCompleteClass : traceClass.getProperSuperCompleteClasses()) {
for (@NonNull MappingPartitioner producer : middleAnalysis.getProducers()) {
addProducer(superCompleteClass, producer);
}
}
} */
/*partitioner2cycles =*/ computeCycles();
//
// Each MiddleAnalysis produced only by acyclic partitioners identifies an acyclic trace class
//
Set<@NonNull TraceClassAnalysis> acylicAnalysis = new HashSet<>();
for (@NonNull MappingPartitioner acyclicProducer : acyclicProducers) {
for (@NonNull Node traceNode : acyclicProducer.getTraceNodes()) {
CompleteClass traceClass = traceNode.getCompleteClass();
TraceClassAnalysis middleAnalysis = completeClass2traceClassAnalysis.get(traceClass);
assert middleAnalysis != null;
if (QVTbaseUtil.containsAll(acyclicProducers, middleAnalysis.getProducers())) {
acylicAnalysis.add(middleAnalysis);
}
}
}
}
private void computeTraceClassDiscrimination() throws CompilerChainException {
for (@NonNull TraceClassAnalysis traceClassAnalysis : completeClass2traceClassAnalysis.values()) {
traceClassAnalysis.discriminate();
}
}
private void computeTraceClassInheritance() {
for (@NonNull TraceClassAnalysis subTraceClassAnalysis : completeClass2traceClassAnalysis.values()) {
CompleteClass traceClass = subTraceClassAnalysis.getTraceClass();
for (@NonNull CompleteClass superCompleteClass : traceClass.getProperSuperCompleteClasses()) {
TraceClassAnalysis superTraceClassAnalysis = completeClass2traceClassAnalysis.get(superCompleteClass);
if (superTraceClassAnalysis != null) {
superTraceClassAnalysis.addSubTraceClassAnalysis(subTraceClassAnalysis);
subTraceClassAnalysis.addSuperTraceClassAnalysis(superTraceClassAnalysis);
}
}
}
}
/**
* Return a map of the MappingPartitioners that may execute before each MappingPartitioner.
*/
private @NonNull Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> computeTransitivePredecessors() {
Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> partitioner2predecessors = new HashMap<>();
for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
partitioner2predecessors.put(mappingPartitioner, new HashSet<>());
}
Deque<@NonNull MappingPartitioner> worklist = new ArrayDeque<>(mappingPartitioners);
while (!worklist.isEmpty()) {
MappingPartitioner producer = worklist.remove();
Set<@NonNull MappingPartitioner> producerPredecessors = partitioner2predecessors.get(producer);
assert producerPredecessors != null;
Iterable<@NonNull TraceClassAnalysis> producedTraceClassAnalyses = producer.getProducedTraceClassAnalyses();
if (producedTraceClassAnalyses != null) {
for (@NonNull TraceClassAnalysis producedTraceClass : producedTraceClassAnalyses) {
for (@NonNull TraceClassAnalysis superProducedTraceClass : producedTraceClass.getSuperTraceClassAnalyses()) {
for (@NonNull MappingPartitioner consumer : superProducedTraceClass.getConsumers()) {
Set<@NonNull MappingPartitioner> consumerPredecessors = partitioner2predecessors.get(consumer);
assert consumerPredecessors != null;
boolean propagate = consumerPredecessors.add(producer);
if (consumerPredecessors.addAll(producerPredecessors)) {
propagate = true;
}
if (propagate && !worklist.contains(consumer)) {
worklist.add(consumer);
}
}
}
}
}
}
if (PREDECESSORS.isActive()) {
for (@NonNull MappingPartitioner successor : mappingPartitioners) {
StringBuilder s = new StringBuilder();
s.append(successor + ":");
Set<@NonNull MappingPartitioner> predecessors = partitioner2predecessors.get(successor);
assert predecessors != null;
for (@NonNull MappingPartitioner predecessor : predecessors) {
s.append("\n\t" + predecessor);
}
PREDECESSORS.println(s.toString());
}
}
return partitioner2predecessors;
}
/**
* Return a map of the MappingPartitioners that may execute after each MappingPartitioner.
*/
private @NonNull Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> computeTransitiveSuccessors() {
Map<@NonNull MappingPartitioner, @NonNull Set<@NonNull MappingPartitioner>> partitioner2successors = new HashMap<>();
for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
partitioner2successors.put(mappingPartitioner, new HashSet<>());
}
Deque<@NonNull MappingPartitioner> worklist = new ArrayDeque<>(mappingPartitioners);
while (!worklist.isEmpty()) {
MappingPartitioner consumer = worklist.remove();
Set<@NonNull MappingPartitioner> consumerSuccessors = partitioner2successors.get(consumer);
assert consumerSuccessors != null;
Iterable<@NonNull TraceClassAnalysis> consumedTraceClassAnalyses = consumer.getConsumedTraceClassAnalyses();
if (consumedTraceClassAnalyses != null) {
for (@NonNull TraceClassAnalysis consumedTraceClassAnalysis : consumedTraceClassAnalyses) {
for (@NonNull TraceClassAnalysis subConsumedTraceClass : consumedTraceClassAnalysis.getSubTraceClassAnalyses()) {
for (@NonNull MappingPartitioner producer : subConsumedTraceClass.getProducers()) {
Set<@NonNull MappingPartitioner> producerSuccessors = partitioner2successors.get(producer);
assert producerSuccessors != null;
boolean propagate = producerSuccessors.add(consumer);
if (producerSuccessors.addAll(consumerSuccessors)) {
propagate = true;
}
if (propagate && !worklist.contains(producer)) {
worklist.add(producer);
}
}
}
}
}
}
if (SUCCESSORS.isActive()) {
for (@NonNull MappingPartitioner predecessor : mappingPartitioners) {
StringBuilder s = new StringBuilder();
s.append(predecessor + ":");
Set<@NonNull MappingPartitioner> successors = partitioner2successors.get(predecessor);
assert successors != null;
for (@NonNull MappingPartitioner successor : successors) {
s.append("\n\t" + successor);
}
SUCCESSORS.println(s.toString());
}
}
return partitioner2successors;
}
private @NonNull CycleAnalysis createCycleAnalysis(@NonNull Set<@NonNull MappingPartitioner> cyclicMappingPartitioners) {
Map<@NonNull MappingPartitioner, @NonNull CycleAnalysis> mappingPartitioner2cycleAnalysis2 = mappingPartitioner2cycleAnalysis;
if (mappingPartitioner2cycleAnalysis2 == null) {
mappingPartitioner2cycleAnalysis = mappingPartitioner2cycleAnalysis2 = new HashMap<>();
}
Map<@NonNull TraceClassAnalysis, @NonNull CycleAnalysis> traceClassAnalysis2cycleAnalysis2 = traceClassAnalysis2cycleAnalysis;
if (traceClassAnalysis2cycleAnalysis2 == null) {
traceClassAnalysis2cycleAnalysis = traceClassAnalysis2cycleAnalysis2 = new HashMap<>();
}
Set<@NonNull TraceClassAnalysis> consumedTraceClassAnalyses = new HashSet<>();
Set<@NonNull TraceClassAnalysis> superProducedTraceClassAnalyses = new HashSet<>();
for (@NonNull MappingPartitioner cyclicMappingPartitioner : cyclicMappingPartitioners) {
Iterables.addAll(consumedTraceClassAnalyses, cyclicMappingPartitioner.getConsumedTraceClassAnalyses());
Iterables.addAll(superProducedTraceClassAnalyses, cyclicMappingPartitioner.getSuperProducedTraceClassAnalyses());
}
Set<@NonNull TraceClassAnalysis> cyclicTraceClassAnalyses = new HashSet<>(consumedTraceClassAnalyses);
cyclicTraceClassAnalyses.retainAll(superProducedTraceClassAnalyses);
CycleAnalysis cycleAnalysis = new CycleAnalysis(this, cyclicMappingPartitioners, cyclicTraceClassAnalyses);
for (@NonNull MappingPartitioner cyclicMappingPartitioner : cyclicMappingPartitioners) {
CycleAnalysis oldCycleAnalysis = mappingPartitioner2cycleAnalysis2.put(cyclicMappingPartitioner, cycleAnalysis);
assert oldCycleAnalysis == null;
}
for (@NonNull TraceClassAnalysis cyclicTraceClassAnalysis : cyclicTraceClassAnalyses) {
CycleAnalysis oldCycleAnalysis = traceClassAnalysis2cycleAnalysis2.put(cyclicTraceClassAnalysis, cycleAnalysis);
assert oldCycleAnalysis == null;
}
return cycleAnalysis;
}
public @Nullable List<@NonNull MappingRegion> getCorrolaryOf(@NonNull Edge edge) {
if (!edge.isNavigation()) {
return null;
}
return corrolaryProperty2regions.get(((NavigableEdge)edge).getProperty());
}
public @Nullable CycleAnalysis getCycleAnalysis(@NonNull CompleteClass completeClass) {
TraceClassAnalysis traceClassAnalysis = completeClass2traceClassAnalysis.get(completeClass);
if (traceClassAnalysis == null) {
return null;
}
return getCycleAnalysis(traceClassAnalysis);
}
public @Nullable CycleAnalysis getCycleAnalysis(@NonNull MappingPartitioner mappingPartitioner) {
Map<@NonNull MappingPartitioner, @NonNull CycleAnalysis> mappingPartitioner2cycleAnalysis2 = mappingPartitioner2cycleAnalysis;
if (mappingPartitioner2cycleAnalysis2 == null) {
return null;
}
return mappingPartitioner2cycleAnalysis2.get(mappingPartitioner);
}
public @Nullable CycleAnalysis getCycleAnalysis(@NonNull TraceClassAnalysis traceClassAnalysis) {
Map<@NonNull TraceClassAnalysis, @NonNull CycleAnalysis> traceClassAnalysis2cycleAnalysis2 = traceClassAnalysis2cycleAnalysis;
if (traceClassAnalysis2cycleAnalysis2 == null) {
return null;
}
return traceClassAnalysis2cycleAnalysis2.get(traceClassAnalysis);
}
public @NonNull MappingPartitioner getMappingPartitioner(@NonNull MappingRegion region) {
return ClassUtil.nonNullState(region2mappingPartitioner.get(region));
}
public @NonNull PropertyDatum getSuccessPropertyDatum(@NonNull Property successProperty) {
return scheduleManager.getSuccessPropertyDatum(successProperty);
}
public @NonNull TraceClassAnalysis getTraceClassAnalysis(@NonNull CompleteClass completeClass) {
return ClassUtil.nonNullState(completeClass2traceClassAnalysis.get(completeClass));
}
public boolean isCyclic(@NonNull CompleteClass traceClass) {
TraceClassAnalysis traceClassAnalysis = completeClass2traceClassAnalysis.get(traceClass);
if (traceClassAnalysis == null) {
return false;
}
return traceClassAnalysis.isCyclic();
}
public @NonNull Iterable<@NonNull MappingRegion> partition() throws CompilerChainException {
//
// Create the per-mapping partitioner and accumulate the local analyses
//
for (@NonNull Region region : activeRegions) {
if (region instanceof MappingRegion) {
MappingRegion mappingRegion = (MappingRegion)region;
MappingPartitioner mappingPartitioner = new MappingPartitioner(this, mappingRegion);
region2mappingPartitioner.put(mappingRegion, mappingPartitioner);
mappingPartitioners.add(mappingPartitioner);
}
}
//
// Perform global analyses
//
computeTraceClassDiscrimination();
computeTraceClassInheritance();
computeCyclicTraceClasses();
// FIXME check that all head nodes have trace properties
//
// Perform per-mapping partitioning
//
List<@NonNull MappingRegion> partitionedRegions = new ArrayList<>();
Set<@NonNull CycleAnalysis> partitionedCycles = new HashSet<>();
for (@NonNull MappingPartitioner mappingPartitioner : mappingPartitioners) {
CycleAnalysis cycleAnalysis = getCycleAnalysis(mappingPartitioner);
if (Iterables.isEmpty(mappingPartitioner.getTraceNodes())) {
partitionedRegions.add(mappingPartitioner.getRegion());
}
else if (cycleAnalysis == null) {
Iterables.addAll(partitionedRegions, mappingPartitioner.partition());
}
else if (partitionedCycles.add(cycleAnalysis)) {
Iterables.addAll(partitionedRegions, cycleAnalysis.partition(mappingPartitioners));
}
}
return partitionedRegions;
}
}