package org.aksw.jena_sparql_api.concept_cache.combinatorics; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import org.aksw.combinatorics.algos.StateCombinatoricCallback; import org.aksw.combinatorics.solvers.ProblemNeighborhoodAware; import org.aksw.commons.collections.MapUtils; import org.aksw.jena_sparql_api.utils.ExprUtils; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.expr.Expr; import org.apache.jena.sparql.expr.ExprFunction; import org.apache.jena.sparql.expr.ExprVars; import org.apache.jena.sparql.expr.FunctionLabel; import org.apache.jena.sparql.expr.NodeValue; import com.google.common.collect.HashBiMap; // Multimap<Set<Set<>>> // ContainmentMap /** * Match two sets of clauses (a clause is treated as a conjunction) of expressions against each other * * In general: the query must be more specific than the cache, hence, the query may apply additional filters to the cache * * Query: DNF = { {?a = <foo>, ?b = <bar>, ?c = <boo>}, {?a = <foo>, ?b = <bar>, ?d = <boo>} } * Cache: DNF = { {?a = <foo>, ?b = <bar>}, {?a = <foo>, ?b = <baz>} } * * At least one cache clause must be fully contained in one of the query's DNF clauses. * * * * Query: CNF = { {?a = <foo>}, {?b = <bar>}, {?c = <boo>, ?d = <boo>} } * Cache: CNF = { {?a = <foo>}, {?b = <bar>, ?b = <baz>} } * * * * * * Each DNF clause of the query must contain a clause of the cache. * * For over CNF clause of the cache, for at least one literal there must exist a clause of the query that contains it * * * * All expressions of the first set must match to expressions of the second set * * @author raven * */ public class ProblemVarMappingExpr extends ProblemMappingVarsBase<Expr, Expr, Var, Var> { // public ProblemVarMappingExpr(Collection<? extends Collection<Expr>> as, // Collection<? extends Collection<Expr>> bs, Map<Var, Var> baseSolution) { // super(as, bs, baseSolution); // } public ProblemVarMappingExpr(Collection<Expr> as, Collection<Expr> bs, Map<Var, Var> baseSolution) { super(as, bs, baseSolution); //System.out.println("Created: " + as + " - " + bs); } @Override public Stream<Map<Var, Var>> generateSolutions() { Stream<Map<Var, Var>> result; if(as.isEmpty()) { result = Stream.of(Collections.emptyMap()); } else { result = StateCombinatoricCallback .createKPermutationsOfN( as, bs, Collections.<Var, Var>emptyMap(), (baseSol, x, y) -> { Stream<Map<Var, Var>> r = createVarMap(x, y, baseSol).map(z -> z.getVarMap()); //r.map(i -> { System.out.println("map: " + i); return i; }); return r; }) .map(stack -> stack.getValue().getSolution()); //.map(stack -> { System.out.println(stack ); return stack.getValue().getSolution(); }); } // Collection<Map<Var, Var>> tmp = result.collect(Collectors.toList()); // System.out.println("Solution contributions: " + tmp + " for " + as + " - " + bs); // result = tmp.stream(); // if(result..isEmpty()) { // result = Stream.of(Collections.emptyMap()); // } return result; } /** * * { (?a = ?b) } vs { (?x = ?y) } * * if partialSolution contained ?a -> ?z, then * * * */ @Override public Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> refine(Map<Var, Var> partialSolution) { Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> result = Refinement.refineExprs( as, bs, baseSolution, partialSolution); return result; } // // Collection<Problem<Map<Var, Var>>> result; // // System.out.println("Refining " + as + " - " + bs + " via " + partialSolution); // // boolean isCompatible = MapUtils.isPartiallyCompatible(baseSolution, partialSolution); // if(!isCompatible) { // result = Collections.emptySet(); //Collections.singleton(new ProblemUnsolvable<>()); // } else { // Map<Var, Var> newBase = new HashMap<>(); // newBase.putAll(baseSolution); // newBase.putAll(partialSolution); // // NodeTransform signaturizer = NodeTransformSignaturize.create(newBase);//partialSolution); // // Multimap<Expr, Expr> sigToAs = HashMultimap.create(); // as.forEach(e -> { // Expr sig = NodeTransformLib.transform(signaturizer, e); // sigToAs.put(sig, e); // }); // // // //partialSolution // Map<Var, Var> identity = newBase.values().stream().collect(Collectors.toMap(x -> x, x -> x)); // NodeTransform s2 = NodeTransformSignaturize.create(identity);//partialSolution); // Multimap<Expr, Expr> sigToBs = HashMultimap.create(); // bs.forEach(e -> { // Expr sig = NodeTransformLib.transform(s2, e); // sigToBs.put(sig, e); // }); // // Map<Expr, Entry<Set<Expr>, Set<Expr>>> group = org.aksw.jena_sparql_api.utils.MapUtils.groupByKey(sigToAs.asMap(), sigToBs.asMap()); // // group.values().stream().forEach(e -> // System.out.println(" Refined to " + e + " from " + as + " - " + bs + " via " + newBase)); // // result = group.values().stream() // .map(e -> new ProblemVarMappingExpr(e.getKey(), e.getValue(), newBase)) // .collect(Collectors.toList()); // } // // return result; // } // public static Iterable<Map<Var, Var>> createSolutions(Collection<Expr> as, Collection<Expr> bs, Map<Var, Var> baseSolution) { // //Map<Var, Var> baseSolution = Collections.emptyMap(); // Iterable<Map<Var, Var>> result = // () -> IsoMapUtils.createSolutionStream( // as, // bs, // ProblemVarMappingExpr::createSingleVarMap, // baseSolution);.iterator(); // // return result; // } // public static Map<Var, Var> createVarMap(Collection<Expr> as, Collection<Expr> bs) { // Map<Var, Var> baseVarMap = Collections.emptyMap(); // // // TODO Not finished // return baseVarMap; // } // public static Map<Var, Var> createSingleVarMap(Expr a, Expr b) { // Map<Var, Var> result = createVarMap(a, b).findFirst().map(x -> x.getVarMap()).orElse(null); // return result; // } // // public static Stream<ExprMapSolution> createVarMap(Expr a, Expr b) { // Stream<ExprMapSolution> result = createVarMap(a, b, Collections.emptyMap()); // return result; // } /** * * It is important to note that linearization causes the leaf nodes to be * matched first. Hence, inner nodes are only processed once variable mappings * have been obtained from the map. * * @param needle * @param b * @return */ public static Stream<ExprMapSolution> createVarMap(Expr needleA, Expr haystackB, Map<Var, Var> baseSolution) { List<Expr> as = ExprUtils.linearizePrefix(needleA, Collections.singleton(null)).collect(Collectors.toList()); List<Expr> bs = ExprUtils.linearizePrefix(haystackB, Collections.singleton(null)).collect(Collectors.toList()); // Get the number of leafs of b //int n = ExprUtils.countLeafs(a); //int m = ExprUtils.countLeafs(b); int m = as.size(); int n = bs.size(); // If there is a match, we can continue by the size of m, as there cannot be another overlap //Collection<ExprMapSolution> result = new ArrayList<>(); // IntStream.range(0, n - m).forEach(i -> { // }); //for(int i = 0; i < n - m + 1; ++i) { Stream<ExprMapSolution> result = IntStream.range(0, n - m + 1) .mapToObj(i -> { Map<Var, Var> varMap = HashBiMap.create(baseSolution); //new HashMap<Var, Var>(baseSolution); Expr be = null; for(int j = 0; j < m; ++j) { //Stream<ExprMapSolution> r = IntStream.range(0, m).map(j -> { Expr ae = as.get(j); be = bs.get(i + j); boolean isCompatible; if(ae == null && be == null) { isCompatible = true; } else if(ae == null || be == null) { isCompatible = false; } else if(ae.isVariable() && be.isVariable()) { Var av = ae.getExprVar().asVar(); Var bv = be.getExprVar().asVar(); // Add a mapping to varMap if it is compatible Map<Var, Var> tmp = Collections.singletonMap(av, bv); isCompatible = MapUtils.isCompatible(tmp, varMap); varMap.putAll(tmp); } else if(ae.isConstant() && be.isConstant()) { NodeValue ac = ae.getConstant(); NodeValue bc = be.getConstant(); isCompatible = ac.equals(bc); } else if(ae.isFunction() && be.isFunction()) { // The function symbols must match - the sub-expressions were already matched // TODO Deal with sparql EXIST ExprFunction af = ae.getFunction(); ExprFunction bf = be.getFunction(); FunctionLabel al = af.getFunctionSymbol(); FunctionLabel bl = bf.getFunctionSymbol(); isCompatible = al.equals(bl); } else { isCompatible = false; } if(!isCompatible) { varMap = null; break; } } ExprMapSolution r = varMap == null ? null : new ExprMapSolution(varMap, needleA, haystackB, be); return r; }) .filter(x -> !Objects.isNull(x)) ; return result; } // @Override // public String toString() { // return super.toString(); // } @Override public Collection<Var> getSourceNeighbourhood() { Set<Var> result = as.stream() .flatMap(expr -> ExprVars.getVarsMentioned(expr).stream()) .collect(Collectors.toSet()); return result; } // @Override // public Collection<Var> exposeTargetNeighbourhood() { // Set<Var> result = bs.stream() // .flatMap(expr -> ExprVars.getVarsMentioned(expr).stream()) // .collect(Collectors.toSet()); // // return result; // } }