package org.aksw.jena_sparql_api.sparql.algebra.mapping; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; import org.aksw.combinatorics.algos.KPermutationsOfNUtils; import org.aksw.combinatorics.collections.Cluster; import org.aksw.combinatorics.collections.ClusterStack; import org.aksw.combinatorics.collections.NodeMapping; import org.aksw.commons.collections.stacks.NestedStack; import org.aksw.commons.collections.trees.Tree; import org.aksw.commons.collections.trees.TreeUtils; import org.apache.jena.ext.com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codepoetics.protonpack.functions.TriFunction; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; public class TreeMapperImpl<A, B, S> { private static final Logger logger = LoggerFactory.getLogger(TreeMapperImpl.class); protected Tree<A> aTree; protected Tree<B> bTree; protected List<List<A>> aTreeLevels; protected List<List<B>> bTreeLevels; protected int aTreeDepth; protected int bTreeDepth; protected Multimap<A, B> baseMapping; // Function that given a mapping of parent nodes returns the matching strategy for its children // protected Function<Entry<A, B>, TriFunction< // ? extends Collection<A>, // ? extends Collection<B>, // Multimap<A, B>, // Stream<Map<A, B>>>> matchingStrategy; //protected BiFunction<A, B, ? extends MatchingStrategyFactory<A, B>> isSatisfiable; //matchingStrategy; // TODO Maybe use a triFunction with the current stack //protected BiFunction<A, B, S> makeCluster; /** * Function that for a given mapping of parent nodes returns a function for * dealing with its children * */ protected BiFunction<A, B, ? extends TriFunction<List<A>, List<B>, Multimap<A, B>, S>> matchingStrategyFactory; protected Predicate<S> isSatisfiable; public TreeMapperImpl( Tree<A> aTree, Tree<B> bTree, Multimap<A, B> baseMapping, //BiFunction<A, B, ? extends MatchingStrategyFactory<A, B>> isSatisfiable //BiFunction<A, B, S> makeCluster, BiFunction<A, B, ? extends TriFunction<List<A>, List<B>, Multimap<A, B>, S>> matchingStrategyFactory, Predicate<S> isSatisfiable ) {//, Multimap<A, B> baseMapping) { this.aTree = aTree; this.bTree = bTree; //this.baseMapping = baseMapping; this.aTreeLevels = TreeUtils.nodesPerLevel(aTree); this.bTreeLevels = TreeUtils.nodesPerLevel(bTree); Collections.reverse(aTreeLevels); Collections.reverse(bTreeLevels); this.aTreeDepth = aTreeLevels.size(); this.bTreeDepth = bTreeLevels.size(); //Multimap<A, B> baseMapping this.baseMapping = baseMapping; //this.matchingStrategy = matchingStrategy; this.matchingStrategyFactory = matchingStrategyFactory; this.isSatisfiable = isSatisfiable; } // public static <S, X> Stream<X> stream(Consumer<Consumer<X>> fn, S ) { // List<X> result = new ArrayList<>(); // // fn.accept(baseSolution, (item) -> result.add(item)); // // return result.stream(); // } public void recurse(Map<A, B> parentMapping, Consumer<NestedStack<LayerMapping<A, B, S>>> consumer) { LayerMapping<A, B, S> layerMapping = new LayerMapping<>(Collections.emptyList(), parentMapping); //NestedStack<LayerMapping<A, B, S>> parentMappingStack = new NestedStack<>(null, parentMapping); NestedStack<LayerMapping<A, B, S>> parentMappingStack = new NestedStack<>(null, layerMapping); recurse(0, parentMappingStack, consumer); } public void recurse(int i, NestedStack<LayerMapping<A, B, S>> parentLayerMappingStack, Consumer<NestedStack<LayerMapping<A, B, S>>> consumer) { if(logger.isDebugEnabled()) { logger.debug("Entering level " + i); } //Map<A, B> parentMapping = parentMappingStack.getValue(); Map<A, B> parentMapping = parentLayerMappingStack.getValue().getParentMap(); if(i < aTreeLevels.size()) { Set<A> keys = Sets.newIdentityHashSet(); keys.addAll(aTreeLevels.get(i)); Set<B> values = Sets.newIdentityHashSet(); values.addAll(bTreeLevels.get(i)); Multimap<A, B> effectiveMapping = HashMultimap.create(); // *Only* use the nodes of the current level mapped according to the base mapping Multimap<A, B> levelMapping = Multimaps.filterEntries(baseMapping, new com.google.common.base.Predicate<Entry<A, B>>() { @Override public boolean apply(Entry<A, B> input) { boolean result = keys.contains(input.getKey()) && values.contains(input.getValue()); return result; } }); // In addition to the candidate mappings of the current level, also add those that may have been introduced // by the parent mapping effectiveMapping.putAll(levelMapping); //effectiveMapping.putAll(parentMapping); //effectiveMapping.entries().addAll(parentMapping.entrySet()); parentMapping.entrySet().forEach(e -> effectiveMapping.put(e.getKey(), e.getValue())); // Each cluster stack represents potential mappings of all of this level's parent nodes Stream<ClusterStack<A, B, Entry<A, B>>> stream = KPermutationsOfNUtils.<A, B>kPermutationsOfN( effectiveMapping, aTree, bTree); stream.forEach(parentClusterStack -> { boolean satisfiability = true; //LayerMapping<A, B, S> layerMapping = new LayerMapping<>(); List<NodeMapping<A, B, S>> nodeMappings = new ArrayList<>(); for(Cluster<A, B, Entry<A, B>> cluster : parentClusterStack) { Entry<A, B> parentMap = cluster.getCluster(); A aParent = parentMap.getKey(); B bParent = parentMap.getValue(); // Obtain the matching strategy function for the given parents TriFunction<List<A>, List<B>, Multimap<A, B>, S> matchingStrategy = matchingStrategyFactory.apply(aParent, bParent); //boolean r = isSatisfiable.apply(clusterX); //MatchingStrategyFactory<A, B> predicate = isSatisfiable.apply(parentMap.getKey(), parentMap.getValue()); Multimap<A, B> mappings = cluster.getMappings(); List<A> aChildren = aTree.getChildren(parentMap.getKey()); List<B> bChildren = bTree.getChildren(parentMap.getValue()); S clusterX = matchingStrategy.apply(aChildren, bChildren, mappings); boolean r = isSatisfiable.test(clusterX); NodeMapping<A, B, S> nodeMapping = new NodeMapping<>(aTree, bTree, parentMap, effectiveMapping, clusterX); nodeMappings.add(nodeMapping); // // IterableUnknownSize<Map<A, B>> it = predicate.apply(aChildren, bChildren, mappings); // boolean r = it.mayHaveItems(); if(logger.isDebugEnabled()) { logger.debug(" Source: " + aParent); logger.debug(" Target: " + bParent); logger.debug(" Satisfiable: " + satisfiability); nodeMappings.forEach(m -> m.getChildMapping().entries().forEach(n -> logger.debug(" ChildMapping: " + n))); } if(!r) { satisfiability = false; break; } } if(satisfiability) { //Multimap<A, B> nextParentMapping = HashMultimap.create(); Map<A, B> nextParentMapping = new IdentityHashMap<>(); for(Cluster<A, B, Entry<A, B>> cluster : parentClusterStack) { Entry<A, B> e = cluster.getCluster(); nextParentMapping.put(e.getKey(), e.getValue()); } LayerMapping<A, B, S> layerMapping = new LayerMapping<>(nodeMappings, nextParentMapping); //MultiClusterStack<A, B, Entry<A, B>> nextMcs = new MultiClusterStack<>(null, parentClusterStack); //NestedStack<Map<A, B>> nextParentMappingStack = new NestedStack<>(parentMappingStack, nextParentMapping); NestedStack<LayerMapping<A, B, S>> nextLayerMappingStack = new NestedStack<>(parentLayerMappingStack, layerMapping); // Based on the op-types, determine the matching strategy and check whether any of the clusters has a valid mapping // If any cluster DOES NOT have a satisfiable mapping, we can stop the recursion // System.out.println("GOT at level" + i + " " + nextParentMapping); //System.out.println("GOT at level" + i + " " + parentClusterStack); recurse(i + 1, nextLayerMappingStack, consumer); } }); } else { consumer.accept(parentLayerMappingStack); } } }