diff --git a/integraal/integraal-model/src/main/java/fr/boreal/model/logicalElements/api/TermCompound.java b/integraal/integraal-model/src/main/java/fr/boreal/model/logicalElements/api/TermCompound.java
index 71b18ef4e5a593a16dc9d9ef8af4c613bb9f51ba..f54f651af7810acba6233ed328bf4679062a9892 100644
--- a/integraal/integraal-model/src/main/java/fr/boreal/model/logicalElements/api/TermCompound.java
+++ b/integraal/integraal-model/src/main/java/fr/boreal/model/logicalElements/api/TermCompound.java
@@ -56,7 +56,9 @@ public interface TermCompound {
         return this.getTerms()
                 .flatMap(t -> {
                     if (t instanceof TermCompound tc) {
-                        return tc.getAllNestedTerms();
+                        return Stream.concat(
+                                tc.getAllNestedTerms(),
+                                Stream.of(t));
                     }
                     return Stream.of(t);
                 })
@@ -70,15 +72,8 @@ public interface TermCompound {
      * class type filters the terms by type
      */
     default <T extends Term> Stream<T> getNestedTerms(Class<T> classType) {
-        return (Stream<T>) this.getTerms()
-                .flatMap(t -> {
-                    if (t instanceof TermCompound tc) {
-                        return tc.getNestedTerms(classType);
-                    } else if (classType.isInstance(t)) {
-                        return Stream.of(t);
-                    }
-                    return Stream.empty();
-                })
+        return (Stream<T>) this.getAllNestedTerms()
+                .filter(t -> classType.isInstance(t))
                 .distinct();
     }
 }
diff --git a/integraal/integraal-model/src/main/java/fr/boreal/model/queryEvaluation/api/QueryEvaluator.java b/integraal/integraal-model/src/main/java/fr/boreal/model/queryEvaluation/api/QueryEvaluator.java
index 30a8950382d4b1bb0a9963cca3bdcf77cb1ea92a..de685949fbf49ca20be23d7060a2a35b2b37b639 100644
--- a/integraal/integraal-model/src/main/java/fr/boreal/model/queryEvaluation/api/QueryEvaluator.java
+++ b/integraal/integraal-model/src/main/java/fr/boreal/model/queryEvaluation/api/QueryEvaluator.java
@@ -5,14 +5,16 @@ import fr.boreal.model.data.readable.exception.EvaluationException;
 import fr.boreal.model.kb.api.FactBase;
 import fr.boreal.model.logicalElements.api.Substitution;
 import fr.boreal.model.logicalElements.api.Variable;
-import fr.boreal.model.logicalElements.functional.AllVariablesInSubstitutionMapToConstant;
 import fr.boreal.model.logicalElements.functional.SpecificVariablesInSubstitutionMapToConstant;
 import fr.boreal.model.logicalElements.impl.SubstitutionImpl;
 import fr.boreal.model.query.api.Query;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.iterators.FilterIterator;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Spliterators;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
@@ -32,12 +34,17 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * @param query                                query to evaluate
      * @param queryableData                        readable data source in which atoms are stored
      * @param variablesThatMustBeMappedToConstants the set of variables that must be mapped to constants
-     * @param preHomomorphism                      a partial homomorphism from var(query) to terms(readable data source) to extend
+     * @param preHomomorphism                      a partial homomorphism from var(query) to terms(readable data source)
+     *                                             to extend
      * @return an {@link Iterator} of {@link Substitution} over all the answers of
      * the given query in the given readable data source with respect to the query
      * answer variables.
      */
-    Stream<Substitution> evaluate(Q query, QD queryableData, Collection<Variable> variablesThatMustBeMappedToConstants, Substitution preHomomorphism) throws EvaluationException;
+    Stream<Substitution> evaluate(
+            Q query,
+            QD queryableData,
+            Collection<Variable> variablesThatMustBeMappedToConstants,
+            Substitution preHomomorphism) throws EvaluationException;
 
     /**
      * @param query         query to evaluate
@@ -58,7 +65,8 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * homomorphisms of the given query in the given readable data source with respect
      * to the query answer variables.
      */
-    default Stream<Substitution> homomorphism(Q query, QD queryableData, Substitution preHomomorphism) throws EvaluationException {
+    default Stream<Substitution> homomorphism(Q query, QD queryableData, Substitution preHomomorphism)
+            throws EvaluationException {
         return evaluate(query, queryableData, Set.of(), preHomomorphism);
     }
 
@@ -81,7 +89,8 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * answers of the given query in the given readable data source with respect
      * to the query answer variables.
      */
-    default Stream<Substitution> evaluate(Q query, QD queryableData, Substitution preHomomorphism) throws EvaluationException {
+    default Stream<Substitution> evaluate(Q query, QD queryableData, Substitution preHomomorphism)
+            throws EvaluationException {
         return evaluate(query, queryableData, query.getAnswerVariables(), preHomomorphism);
     }
 
@@ -91,7 +100,7 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * @return true iff there exist a substitution s that is a homomorphism of the query on the readable data source
      */
     default boolean existHomomorphism(Q query, QD queryableData) throws EvaluationException {
-        return this.homomorphism(query, queryableData).findAny().isPresent();
+        return this.homomorphism(query, queryableData).parallel().findAny().isPresent();
     }
 
     /**
@@ -100,8 +109,9 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * @param preHomomorphism a partial homomorphism to extend
      * @return true iff there exist a substitution s that is a homomorphism of the query on the readable data source
      */
-    default boolean existHomomorphism(Q query, QD queryableData, Substitution preHomomorphism) throws EvaluationException {
-        return this.homomorphism(query, queryableData, preHomomorphism).findAny().isPresent();
+    default boolean existHomomorphism(Q query, QD queryableData, Substitution preHomomorphism)
+            throws EvaluationException {
+        return this.homomorphism(query, queryableData, preHomomorphism).parallel().findAny().isPresent();
     }
 
     /**
@@ -110,7 +120,7 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * @return true iff there exist a substitution s that is an answer to the query on the readable data source
      */
     default boolean existAnswer(Q query, QD queryableData) throws EvaluationException {
-        return this.evaluate(query, queryableData).findAny().isPresent();
+        return this.evaluate(query, queryableData).parallel().findAny().isPresent();
     }
 
     /**
@@ -120,7 +130,7 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      * @return true iff there exist a substitution s that is an answer to the query on the readable data source
      */
     default boolean existAnswer(Q query, QD queryableData, Substitution preHomomorphism) throws EvaluationException {
-        return this.evaluate(query, queryableData, preHomomorphism).findAny().isPresent();
+        return this.evaluate(query, queryableData, preHomomorphism).parallel().findAny().isPresent();
     }
 
     /**
@@ -151,7 +161,9 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      *         filtered by retaining only substitutions mapping to constants if
      *         required
      */
-    default Stream<Substitution> postprocessResult(Stream<Substitution> unfilteredSubstitutions, Collection<Variable> variablesThatMustBeMappedToConstants) {
+    default Stream<Substitution> postprocessResult(
+            Stream<Substitution> unfilteredSubstitutions,
+            Collection<Variable> variablesThatMustBeMappedToConstants) {
         if (variablesThatMustBeMappedToConstants.isEmpty()) {
             return unfilteredSubstitutions;
         } else {
@@ -160,24 +172,6 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
         }
     }
 
-    /**
-     * Filters the result of a query by removing substitutions which map a variable
-     * to some other variable of the active domain.
-     *
-     * @param unfilteredSubstitutions the unfiltered substitution
-     * @param constantsOnly true iff all variables must be mapped to constants
-     * @return the post-processed query result where results have been possibly
-     *         filtered by retaining only substitutions mapping to constants if
-     *         required
-     */
-    default Stream<Substitution> postprocessResult(Stream<Substitution> unfilteredSubstitutions, boolean constantsOnly) {
-        if (constantsOnly) {
-            return postprocessResult(unfilteredSubstitutions, new AllVariablesInSubstitutionMapToConstant());
-        } else {
-            return unfilteredSubstitutions;
-        }
-    }
-
     /**
      * Filters the result of a query according to a predicate.
      *
@@ -187,7 +181,9 @@ public interface QueryEvaluator<Q extends Query, QD extends QueryableData> {
      *         filtered by retaining only substitutions mapping to constants if
      *         required
      */
-    default Stream<Substitution> postprocessResult(Stream<Substitution> unfilteredSubstitutions, Predicate<Substitution> predicate) {
+    default Stream<Substitution> postprocessResult(
+            Stream<Substitution> unfilteredSubstitutions,
+            Predicate<Substitution> predicate) {
         return StreamSupport.stream(
                 Spliterators.spliteratorUnknownSize(
                         new FilterIterator<>(unfilteredSubstitutions.iterator(), predicate), 0),
diff --git a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/FOQueryEvaluators.java b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/FOQueryEvaluators.java
index 7c185423e2648c46a6f4274e61d02799eba6fa4d..1f1a2b15d24389077f52284ec7e5ab0749da1b08 100644
--- a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/FOQueryEvaluators.java
+++ b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/FOQueryEvaluators.java
@@ -2,6 +2,7 @@ package fr.boreal.query_evaluation;
 
 import fr.boreal.model.formula.FOFormulas;
 import fr.boreal.model.formula.api.FOFormula;
+import fr.boreal.model.logicalElements.api.Atom;
 import fr.boreal.model.logicalElements.api.Substitution;
 import fr.boreal.model.logicalElements.api.Term;
 import fr.boreal.model.logicalElements.api.Variable;
@@ -99,4 +100,21 @@ public class FOQueryEvaluators {
             return FOQueryEvaluators.rebuildAnswer(this.initialQuery, this.partialAnswers.next());
         }
     }
+
+    public static Map<Integer, Term> computeAtomicPositionsAssignation(
+            Atom atom,
+            Substitution preHomomorphism) {
+        Map<Integer, Term> positions = new HashMap<>();
+
+        for (int i = 0; i < atom.getTermSequence().size(); i++) {
+            Term t = atom.getTermSequence().get(i);
+            if (t.isGround()) {
+                positions.put(i, t);
+            } else if (preHomomorphism.keys().contains(t)) {
+                positions.put(i, preHomomorphism.createImageOf(t));
+            }
+        }
+
+        return positions;
+    }
 }
diff --git a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/atomic/AtomicFOQueryEvaluator.java b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/atomic/AtomicFOQueryEvaluator.java
index d3c5f9dfca313a6cddfb99d71d70bb021582896e..7ef33f31f016eb8607c76e8c01c45626b01abfbe 100644
--- a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/atomic/AtomicFOQueryEvaluator.java
+++ b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/atomic/AtomicFOQueryEvaluator.java
@@ -10,6 +10,7 @@ import fr.boreal.model.logicalElements.impl.SubstitutionImpl;
 import fr.boreal.model.partition.TermPartition;
 import fr.boreal.model.query.api.FOQuery;
 import fr.boreal.model.queryEvaluation.api.FOQueryEvaluator;
+import fr.boreal.query_evaluation.FOQueryEvaluators;
 import fr.boreal.query_evaluation.utils.filters.GroundTermAtPositionFilter;
 import fr.boreal.query_evaluation.utils.filters.PositionsAssignationFilter;
 import fr.boreal.query_evaluation.utils.transformers.TermSequenceToSubstitutionTransformer;
@@ -67,7 +68,8 @@ public class AtomicFOQueryEvaluator<QD extends QueryableData> implements FOQuery
 							return t;
 						}}).toList());
 
-			Map<Integer, Term> positionsAffectation = computePositionsAssignation(atom, preHomomorphism);
+			Map<Integer, Term> positionsAffectation =
+					FOQueryEvaluators.computeAtomicPositionsAssignation(atom, preHomomorphism);
 			BasicQuery bq = selectBasicQuery(queryableData, atom.getPredicate(), positionsAffectation).orElseThrow(
 					() -> new EvaluationException(String.format(
 							"The query %s is unsupported by the QueryableData %s",
@@ -95,23 +97,6 @@ public class AtomicFOQueryEvaluator<QD extends QueryableData> implements FOQuery
 				.map(s -> makeSubstitutionOnAnswerVariables(s, query.getAnswerVariables(), query.getVariableEqualities()));
 	}
 
-	private Map<Integer, Term> computePositionsAssignation(
-			Atom atom,
-			Substitution preHomomorphism) {
-		Map<Integer, Term> positions = new HashMap<>();
-
-		for (int i = 0; i < atom.getTermSequence().size(); i++) {
-			Term t = atom.getTermSequence().get(i);
-			if (t.isGround()) {
-				positions.put(i, t);
-			} else if (preHomomorphism.keys().contains(t)) {
-				positions.put(i, preHomomorphism.createImageOf(t));
-			}
-		}
-
-		return positions;
-	}
-
 	private static Substitution makeSubstitutionOnAnswerVariables (
 			Substitution result,
 			Collection<Variable> answerVariables,
diff --git a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/BacktrackEvaluator.java b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/BacktrackEvaluator.java
index 8efca40766f5a0f907da0c0b7260c5e030b8ea80..44e7bb932591c9615cfcec66c560ccc956cc1bbe 100644
--- a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/BacktrackEvaluator.java
+++ b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/BacktrackEvaluator.java
@@ -218,7 +218,9 @@ public class BacktrackEvaluator<QD extends QueryableData> implements FOQueryEval
 					} else {
 						// going down
 						if (this.scheduler.hasNext(this.level)) {
-							FOFormula element = this.scheduler.next(this.level, this.solutionManager.getCurrentSolution());
+							FOFormula element = this.scheduler.next(
+									this.level,
+									this.solutionManager.getCurrentSolution());
 							// answer variables = (general U join) \ already affected
 							Collection<Variable> subquery_vars = new HashSet<>();
 							subquery_vars.addAll(this.query.getAnswerVariables());
@@ -227,13 +229,19 @@ public class BacktrackEvaluator<QD extends QueryableData> implements FOQueryEval
 							// keep variables from (1) the formula and (2) the equalities with an element of the formula
 							Set<Variable> variables = element.getVariables().collect(Collectors.toSet());
 							subquery_vars.removeIf(v -> variables.contains(v) &&
-                                    this.query.getVariableEqualities().getClass(v).stream().noneMatch(variables::contains));
+                                    this.query.getVariableEqualities().getClass(v).stream()
+											.noneMatch(variables::contains));
 							subquery_vars.retainAll(variables);
-							Optional<Substitution> previous_step_subst_opt = this.preHomomorphism.merged(this.solutionManager.getCurrentSolution());
+							Optional<Substitution> previous_step_subst_opt =
+									this.preHomomorphism.merged(this.solutionManager.getCurrentSolution());
 							if (previous_step_subst_opt.isPresent()) {
 								Substitution subquery_subst = previous_step_subst_opt.get();
-								FOQuery<? extends FOFormula> subquery = FOQueryFactory.instance().createOrGetQuery(element, subquery_vars, this.query.getVariableEqualities());
-								Iterator<Substitution> sub_results = this.evaluator.evaluate(subquery, this.queryableData, this.vars, subquery_subst).iterator();
+								FOQuery<? extends FOFormula> subquery =
+										FOQueryFactory.instance().createOrGetQuery(
+												element, subquery_vars, this.query.getVariableEqualities());
+								Iterator<Substitution> sub_results =
+										this.evaluator.evaluate(subquery, this.queryableData, this.vars, subquery_subst)
+												.iterator();
 								if (sub_results.hasNext()) {
 									this.solutionManager.add(this.level, sub_results);
 									if (this.solutionManager.next(this.level)) {
diff --git a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/NaiveDynamicScheduler.java b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/NaiveDynamicScheduler.java
index 985f45cc486617161c41e917a3197293f8dfb103..80cb55454d79ff037465954ebdec45104dc40f79 100644
--- a/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/NaiveDynamicScheduler.java
+++ b/integraal/integraal-query-evaluation/src/main/java/fr/boreal/query_evaluation/conjunction/backtrack/NaiveDynamicScheduler.java
@@ -1,15 +1,21 @@
 package fr.boreal.query_evaluation.conjunction.backtrack;
 
+import com.sun.jdi.InvalidLineNumberException;
 import fr.boreal.model.data.readable.QueryableData;
+import fr.boreal.model.data.readable.query.AtomicPattern;
+import fr.boreal.model.data.readable.query.BasicQuery;
 import fr.boreal.model.formula.api.FOFormula;
 import fr.boreal.model.formula.api.FOFormulaConjunction;
 import fr.boreal.model.formula.api.FOFormulaDisjunction;
 import fr.boreal.model.formula.api.FOFormulaNegation;
 import fr.boreal.model.logicalElements.api.*;
 import fr.boreal.model.query.api.FOQuery;
+import fr.boreal.query_evaluation.FOQueryEvaluators;
+import org.eclipse.rdf4j.query.algebra.Var;
 
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class NaiveDynamicScheduler implements Scheduler {
 
@@ -19,6 +25,8 @@ public class NaiveDynamicScheduler implements Scheduler {
     private final Map<FormulaWrapper, Set<Variable>> sharedVariables;
     private final Map<FormulaWrapper, Boolean> containsFunctionalTerm;
     private final Map<FormulaWrapper, Set<Variable>> functionalTermVariables;
+    private final Map<FormulaWrapper, Set<Variable>> mandatoryVariables;
+    private final Set<Predicate> predicateWithTermFilter;
 
     /**
      * Creates a new scheduler over the given query and fact base
@@ -27,11 +35,14 @@ public class NaiveDynamicScheduler implements Scheduler {
      */
     public NaiveDynamicScheduler(FOQuery<? extends FOFormulaConjunction> query, QueryableData queryableData) {
         this.queryableData = queryableData;
-        this.notOrdered = new ArrayList<>();
-        this.ordered = new ArrayList<>();
+        int numberSubElements = query.getFormula().getSubElements().size();
+        this.notOrdered = new ArrayList<>(numberSubElements);
+        this.ordered = new ArrayList<>(numberSubElements);
         this.sharedVariables = new HashMap<>();
         this.containsFunctionalTerm = new HashMap<>();
         this.functionalTermVariables = new HashMap<>();
+        this.mandatoryVariables = new HashMap<>();
+        this.predicateWithTermFilter = new HashSet<>();
 
         // Populate notOrdered list with initial formulas
         for (FOFormula formula : query.getFormula().getSubElements()) {
@@ -42,6 +53,10 @@ public class NaiveDynamicScheduler implements Scheduler {
             if (containsFunctionalTerm.get(formulaWrapper)) {
                 functionalTermVariables.put(formulaWrapper, computeFunctionalTermVariables(formula));
             }
+            mandatoryVariables.put(formulaWrapper, computeMandatoryVariables(formula));
+            if (formula instanceof Atom a && computePredicateWithTermFilter(a.getPredicate())) {
+                this.predicateWithTermFilter.add(a.getPredicate());
+            }
         }
 
         // Perform static sorting
@@ -91,21 +106,18 @@ public class NaiveDynamicScheduler implements Scheduler {
     }
 
     private boolean computeContainsFunctionalTerm(FOFormula formula) {
-        return formula.getNestedTerms(EvaluableFunction.class).findAny().isPresent();
+        boolean result = formula
+                .getNestedTerms(EvaluableFunction.class)
+                .findAny()
+                .isPresent();
+        return result;
     }
 
     private Set<Variable> computeFunctionalTermVariables(FOFormula formula) {
         return switch (formula) {
-            case Atom atom -> {
-                Set<Variable> variables = new HashSet<>();
-                for (Term term : atom.getTermSequence()) {
-                    if (term.isEvaluableFunction()) {
-                        EvaluableFunction fterm = (EvaluableFunction) term;
-                        fterm.getNestedTerms(Variable.class).forEach(variables::add);
-                    }
-                }
-                yield variables;
-            }
+            case Atom atom -> atom.getNestedTerms(EvaluableFunction.class)
+                        .flatMap(ef -> ef.getNestedTerms(Variable.class))
+                        .collect(Collectors.toSet());
             case FOFormulaNegation neg -> computeFunctionalTermVariables(neg.element());
             case FOFormulaConjunction conj -> conj.getSubElements().stream()
                     .flatMap(f -> this.computeFunctionalTermVariables(f).stream()).collect(Collectors.toSet());
@@ -115,8 +127,25 @@ public class NaiveDynamicScheduler implements Scheduler {
         };
     }
 
+    private Set<Variable> computeMandatoryVariables(FOFormula formula) {
+        Set<Variable> variables = new HashSet<>();
+        for (Atom a : formula.asAtomSet()) {
+            Set<Integer> mandatoryPositions =
+                    this.queryableData.getBasicPattern(a.getPredicate()).getMandatoryPositions();
+            for (Integer mandatoryPosition : mandatoryPositions) {
+                Term t = a.getTerm(mandatoryPosition);
+                if (t instanceof Variable v) {
+                    variables.add(v);
+                } else if (t instanceof TermCompound tc) {
+                    tc.getNestedTerms(Variable.class).forEach(variables::add);
+                }
+            }
+        }
+
+        return variables;
+    }
+
     private boolean containsFunctionalTerm(Atom atom) {
-        //return Arrays.stream(atom.getTerms()).anyMatch(Term::isEvaluableFunction);
         return containsFunctionalTerm.get(new FormulaWrapper(atom));
     }
 
@@ -124,6 +153,34 @@ public class NaiveDynamicScheduler implements Scheduler {
         return formula.getConstants().count();
     }
 
+    private Optional<Long> estimateBound(Atom atom, Substitution currentSolution) {
+        long minBound = Long.MAX_VALUE;
+        AtomicPattern atomicPattern = queryableData.getBasicPattern(atom.getPredicate());
+
+        Map<Integer, Term> positionsAffectation =
+                FOQueryEvaluators.computeAtomicPositionsAssignation(atom, currentSolution);
+        for (BasicQuery bq: (Iterable<BasicQuery>)
+                atomicPattern.createQueries(positionsAffectation)::iterator) {
+            Optional<Long> estimation = queryableData.estimateBound(bq);
+            if (estimation.isPresent()) {
+                if (estimation.get() < minBound) {
+                    minBound = estimation.get();
+                }
+            }
+        }
+
+        return minBound == Long.MAX_VALUE ? Optional.empty() : Optional.of(minBound);
+    }
+
+    private boolean canFilterWithTerms(Predicate p) {
+        return predicateWithTermFilter.contains(p);
+    }
+
+    private boolean computePredicateWithTermFilter(Predicate p) {
+        return this.queryableData.getBasicPattern(p).getIndexablePatterns().stream()
+                .anyMatch(s -> !s.equals(Set.of()));
+    }
+
     @Override
     public FOFormula next(int level, Substitution currentSolution) {
         // Ensure previous levels are cleared
@@ -131,13 +188,21 @@ public class NaiveDynamicScheduler implements Scheduler {
             notOrdered.addLast(ordered.removeLast());
         }
 
+        // If there is only one element to order, we directly return it
         if (notOrdered.size() == 1) {
             ordered.add(notOrdered.removeLast());
             return ordered.getLast();
         }
 
-        // Prioritize evaluable computed atoms
+        // Prioritize evaluable computed atoms and atomic negation.
+        // The idea is that we could backtrack quicker if the evaluation is false
         for (FOFormula formula : notOrdered) {
+            // Consider only formulas where all mandatory variables are assigned
+            if (!areAllMandatoryVariablesAssigned(formula, currentSolution)) {
+                continue;
+            }
+
+            // If formula is an instance of an evaluable computed atom, return it directly
             if (formula instanceof ComputedAtom computedAtom) {
                 if (isEvaluable(computedAtom, currentSolution)) {
                     notOrdered.remove(formula);
@@ -145,6 +210,9 @@ public class NaiveDynamicScheduler implements Scheduler {
                     return formula;
                 }
             } else if (formula instanceof FOFormulaNegation negation && negation.element() instanceof Atom atom) {
+                // Otherwise, if the formula is an atomic negation
+
+                // If it is an atomic negation of an evaluable computed atom, return it
                 if (atom instanceof ComputedAtom computedAtom) {
                     if (isEvaluable(computedAtom, currentSolution)) {
                         notOrdered.remove(formula);
@@ -152,45 +220,58 @@ public class NaiveDynamicScheduler implements Scheduler {
                         return formula;
                     }
                 } else {
+                    // If it is not a computed atom
+                    // First check if there are evaluable functional terms and if so, are there evaluable?
                     if (containsFunctionalTerm(atom) && !areFunctionalTermsEvaluable(atom, currentSolution)) {
                         continue;
                     }
 
-                    // TODO: redo this part to take basic patterns into account
-                    /*Optional<Long> estimate = queryableData.estimateMatchCount(atom, currentSolution);
+                    // Do an estimate to know if the subquery has an answer
+                    // If so, we return the negation so that the algorithm will backtrack
+                    Optional<Long> estimate = estimateBound(atom, currentSolution);
                     if (estimate.isPresent() && estimate.get() > 0) {
                         notOrdered.remove(formula);
                         ordered.add(formula);
                         return formula;
-                    }*/
+                    }
                 }
             }
         }
 
-        // Dynamic sorting based on fact base information
-        // TODO: redo this part to take basic patterns into account
+        // Dynamic sorting based on the estimation of the bound
         FOFormula bestFormula = null;
-        /*long bestEstimate = Long.MAX_VALUE;
+        long bestEstimate = Long.MAX_VALUE;
         for (FOFormula formula : notOrdered) {
+            // Consider only formulas where all mandatory variables are assigned
+            if (!areAllMandatoryVariablesAssigned(formula, currentSolution)) {
+                continue;
+            }
+
+            // We consider atomic formulas that are not computed atoms (they are already treated)
             if (formula instanceof Atom atom && !(atom instanceof ComputedAtom)) {
                 if (containsFunctionalTerm(atom) && !areFunctionalTermsEvaluable(atom, currentSolution)) {
                     continue;
                 }
 
-                Optional<Long> estimate = queryableData.estimateMatchCount(atom, currentSolution);
+                // Try to estimate the bound
+                Optional<Long> estimate = estimateBound(atom, currentSolution);
 
+                // If we have a bound, and we know there is no answer, return the atom so that we backtrack
                 if (estimate.isPresent() && estimate.get() == 0) {
                     notOrdered.remove(formula);
                     ordered.add(formula);
                     return formula;
                 }
 
-                if (!queryableData.canPerformIndexedMatch(atom, currentSolution)) {
+                // If we know that it is not possible to filter the results with the assignations of terms,
+                // we directly return the atom
+                if (!canFilterWithTerms(atom.getPredicate())) {
                     notOrdered.remove(formula);
                     ordered.add(formula);
                     return formula;
                 }
 
+                // If the estimation for this atom is better than for the previous one, we update the best formula
                 long currentEstimate = estimate.orElse(Long.MAX_VALUE);
                 if (currentEstimate < bestEstimate) {
                     bestEstimate = currentEstimate;
@@ -199,12 +280,22 @@ public class NaiveDynamicScheduler implements Scheduler {
             }
         }
 
+        // If we have an estimation, we return the best atom
         if (bestFormula != null) {
             notOrdered.remove(bestFormula);
             ordered.add(bestFormula);
             return bestFormula;
-        } else*/ if (!notOrdered.isEmpty()) {
-            bestFormula = notOrdered.removeLast();
+        }
+
+        // Otherwise, we select any formula that has its mandatory variables assigned
+        if (!notOrdered.isEmpty()) {
+            bestFormula = notOrdered.stream()
+                    .filter(f -> areAllMandatoryVariablesAssigned(f, currentSolution))
+                    .findAny()
+                    .orElseThrow(() -> new RuntimeException(String.format(
+                            "There are no subquery with all mandatory variables assigned: \n" +
+                                    "ordered: %s, notOrdered: %s, currentSolution: %s",
+                            ordered, notOrdered, currentSolution)));
             ordered.add(bestFormula);
             return bestFormula;
         } else {
@@ -238,6 +329,10 @@ public class NaiveDynamicScheduler implements Scheduler {
         return currentSolution.keys().containsAll(functionalVars);
     }
 
+    private boolean areAllMandatoryVariablesAssigned(FOFormula formula, Substitution currentSolution) {
+        return currentSolution.keys().containsAll(mandatoryVariables.get(new FormulaWrapper(formula)));
+    }
+
     // Wrapper class to use default hashCode and equals based on object reference
     private record FormulaWrapper(FOFormula formula) {
         @Override
diff --git a/integraal/integraal-query-evaluation/src/main/java/module-info.java b/integraal/integraal-query-evaluation/src/main/java/module-info.java
index 81709848f9824b64bc21edd0dd6e63ac5b53f91b..a797271bb404fa7e2274d6b072f80228e12c1d94 100644
--- a/integraal/integraal-query-evaluation/src/main/java/module-info.java
+++ b/integraal/integraal-query-evaluation/src/main/java/module-info.java
@@ -19,6 +19,7 @@ module fr.boreal.query_evaluation {
 	requires org.slf4j;
 	requires org.hsqldb;
     requires rdf4j.queryalgebra.model;
+    requires jdk.jdi;
 
     exports fr.boreal.query_evaluation.atomic;
 	exports fr.boreal.query_evaluation.conjunction;
diff --git a/integraal/integraal-query-evaluation/src/test/java/fr/boreal/test/query_evaluation/NaiveDynamicSchedulerTest.java b/integraal/integraal-query-evaluation/src/test/java/fr/boreal/test/query_evaluation/NaiveDynamicSchedulerTest.java
index 309579d527390dfc7cb7460ecf38402b53677762..4ea70fea4012b22d4d463c10b1a324655c9c5e63 100644
--- a/integraal/integraal-query-evaluation/src/test/java/fr/boreal/test/query_evaluation/NaiveDynamicSchedulerTest.java
+++ b/integraal/integraal-query-evaluation/src/test/java/fr/boreal/test/query_evaluation/NaiveDynamicSchedulerTest.java
@@ -9,8 +9,10 @@ import fr.boreal.model.formula.factory.FOFormulaFactory;
 import fr.boreal.model.kb.api.FactBase;
 import fr.boreal.model.logicalElements.api.Atom;
 import fr.boreal.model.logicalElements.api.Substitution;
+import fr.boreal.model.logicalElements.factory.impl.SameObjectTermFactory;
 import fr.boreal.model.logicalElements.impl.AtomImpl;
 import fr.boreal.model.logicalElements.impl.SubstitutionImpl;
+import fr.boreal.model.logicalElements.impl.VariableImpl;
 import fr.boreal.model.partition.TermPartition;
 import fr.boreal.model.query.api.FOQuery;
 import fr.boreal.model.query.factory.FOQueryFactory;
@@ -117,7 +119,11 @@ class NaiveDynamicSchedulerTest {
         Assertions.assertTrue(scheduler.hasNext(1));
 
         substitution = GenericFOQueryEvaluator.defaultInstance().evaluate(
-                FOQueryFactory.instance().createOrGetQuery(firstFormula, List.of()),
+                FOQueryFactory.instance().createOrGetQuery(
+                        firstFormula,
+                        List.of(
+                                SameObjectTermFactory.instance().createOrGetVariable("X"),
+                                SameObjectTermFactory.instance().createOrGetVariable("Y"))),
                 factBase).iterator().next();
         secondFormula = scheduler.next(1, substitution);
         Assertions.assertTrue(ssumxy.equals(secondFormula) || ssumx.equals(secondFormula));
@@ -134,7 +140,7 @@ class NaiveDynamicSchedulerTest {
     @Test
     void testComputedAtom() throws Exception {
         String dlgpFacts = "p(a, b). q(4).";
-        String dlgpQuery = "@prefix ig: <stdfct> ? :- p(X, Y), ig:isEven(ig:sum(X, Y, 7)).";
+        String dlgpQuery = "@computed ig: <stdfct> ? :- p(X, Y), ig:isEven(ig:sum(X, Y, 7)).";
 
         FactBase factBase = new SimpleInMemoryGraphStore(DlgpUtil.parseFacts(dlgpFacts));
         FOQuery<FOFormulaConjunction> query = (FOQuery<FOFormulaConjunction>) DlgpUtil.parseQueries(dlgpQuery).iterator().next();