private boolean branchAndBoundHelper(MetaVariable metaVariable) { if (metaVariable == null) return false; preBacktrack(); if (this.g.getRoot() == null) this.g.addVertex(currentVertex); ConstraintNetwork cn = metaVariable.getConstraintNetwork(); logger.fine("Solving conflict: " + metaVariable); ConstraintNetwork[] values = metaVariable.getMetaConstraint().getMetaValues(metaVariable); if (metaVariable.getMetaConstraint().valOH != null) Arrays.sort(values, metaVariable.getMetaConstraint().valOH); if (values == null || values.length == 0) { this.g.addEdge(new NullConstraintNetwork(null), currentVertex, new TerminalNode(false)); logger.fine("Failure... (1)"); } else { for (ConstraintNetwork value : values) { if (animationTime != 0) { try { Thread.sleep(animationTime); } catch (InterruptedException e) { e.printStackTrace(); } } logger.fine("Trying value: " + Arrays.toString(value.getConstraints())); if (hasConflictClause(value)) continue; this.addResolver(cn, value); setUpperBound(); // System.out.println("test: " + "U: " + getUpperBound() + " L: " + getLowerBound()); if (getUpperBound() <= getLowerBound()) { this.retractResolver(cn, value); continue; } logger.fine("Success..."); metaVariable.getMetaConstraint().markResolvedSub(metaVariable, value); MetaVariable newCon = this.getConflict(); if (newCon != null) { this.g.addEdge(value, currentVertex, newCon); currentVertex = newCon; } if (newCon == null) setLowerBound(); if (branchAndBoundHelper(newCon)) return true; logger.fine("Retracting value: " + Arrays.toString(value.getConstraints())); this.retractResolver(cn, value); logger.fine("Failure... (2)"); } } resetFalseClause(); logger.fine("Backtracking..."); currentVertex = this.g.getParent(currentVertex); postBacktrack(metaVariable); return false; }
/** * Retract all resolvers added to the ground-CSP(s) in order to obtain the current solution to the * meta-CSP. This is useful if one wants to "reset" the meta-CSP to its original unsolved state. */ public void retractResolvers() { Set<ConstraintNetwork> vars = resolvers.keySet(); for (ConstraintNetwork var : vars) { ConstraintNetwork value = resolvers.get(var); logger.fine("=== ||| === Retracting value: " + Arrays.toString(value.getConstraints())); this.retractResolver(var, value); } this.resolvers = new HashMap<ConstraintNetwork, ConstraintNetwork>(); this.resolversInverseMapping = new HashMap<ConstraintNetwork, ConstraintNetwork>(); this.metaVarsToMetaCons.clear(); }
protected final boolean addResolver( ConstraintNetwork metaVarConstraintNetwork, ConstraintNetwork resolverNetwork) { if (!this.addResolverSub(metaVarConstraintNetwork, resolverNetwork)) return false; Constraint[] resolverNetworkConstraints = resolverNetwork.getConstraints(); HashMap<ConstraintSolver, Vector<Constraint>> solvers2constraints = new HashMap<ConstraintSolver, Vector<Constraint>>(); for (Constraint c : resolverNetworkConstraints) { if (!solvers2constraints.containsKey(c.getScope()[0].getConstraintSolver())) { Vector<Constraint> newVec = new Vector<Constraint>(); solvers2constraints.put(c.getScope()[0].getConstraintSolver(), newVec); } solvers2constraints.get(c.getScope()[0].getConstraintSolver()).add(c); } Vector<Constraint[]> addedConstraints = new Vector<Constraint[]>(); for (ConstraintSolver cs : solvers2constraints.keySet()) { Constraint[] toAddOneSolver = solvers2constraints.get(cs).toArray(new Constraint[solvers2constraints.get(cs).size()]); if (cs.addConstraints(toAddOneSolver)) addedConstraints.add(toAddOneSolver); else { for (Constraint[] toDel : addedConstraints) { toDel[0].getScope()[0].getConstraintSolver().removeConstraints(toDel); } this.retractResolverSub(metaVarConstraintNetwork, resolverNetwork); return false; } } return true; }
public static void main(String[] args) { MetaCSPLogging.setLevel(Level.FINEST); String[] symbols = new String[] {"B1", "B2", "B3", "B4", "B5"}; SymbolicVariableConstraintSolver solver = new SymbolicVariableConstraintSolver(symbols, 100, true); solver.setSingleValue(false); int numRobots = 3; Variable[] vars = new Variable[numRobots]; for (int i = 0; i < numRobots; i++) vars[i] = solver.createVariable("Robot" + i); ConstraintNetwork.draw(solver.getConstraintNetwork()); ((SymbolicVariable) vars[0]).setDomain(new String[] {"B1", "B2", "B3"}); // SymbolicValueConstraint con01 = new SymbolicValueConstraint(Type.UNARYEQUALS); // con01.setUnaryValue(new boolean[] {true, true, false, false, false}); // con01.setFrom(vars[0]); // con01.setTo(vars[0]); ((SymbolicVariable) vars[1]).setDomain(new String[] {"B2", "B3", "B4"}); // SymbolicValueConstraint con02 = new SymbolicValueConstraint(Type.UNARYEQUALS); // con02.setUnaryValue(new boolean[] {false, true, true, false, false}); // con02.setFrom(vars[1]); // con02.setTo(vars[1]); ((SymbolicVariable) vars[2]).setDomain(new String[] {"B3"}); // SymbolicValueConstraint con03 = new SymbolicValueConstraint(Type.UNARYEQUALS); // con03.setUnaryValue(new boolean[] {false, false, true, false, false}); // con03.setFrom(vars[2]); // con03.setTo(vars[2]); // alldifferent constraint SymbolicValueConstraint con = new SymbolicValueConstraint(Type.DIFFERENT); con.setScope(vars); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Added constraints? " + solver.addConstraint(con)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Variable varUnion1 = SymbolicVariableConstraintSolver.union(vars); System.out.println("Union of all vars (2): " + varUnion1); Variable varIntersection1 = SymbolicVariableConstraintSolver.intersection(vars); System.out.println("Intersection of all vars (2): " + varIntersection1); }
public Sensor(String name, ConstraintNetworkAnimator animator) { this.animator = animator; this.ans = animator.getActivityNetworkSolver(); this.cn = animator.getConstraintNetwork(); this.name = name; for (Variable timeAct : cn.getVariables("Time")) { if (((SymbolicVariableActivity) timeAct) .getSymbolicVariable() .getSymbols()[0] .equals("Future")) future = (SymbolicVariableActivity) timeAct; } }
protected final void retractResolver(ConstraintNetwork metaVar, ConstraintNetwork res) { this.logger.finest("Retracting resolver:"); this.logger.finest(" MetaVariable: " + metaVar.toString()); this.logger.finest(" MetaValue: " + res.toString()); Constraint[] groundConstraints = res.getConstraints(); HashMap<ConstraintSolver, Vector<Constraint>> solvers2constraints = new HashMap<ConstraintSolver, Vector<Constraint>>(); for (Constraint c : groundConstraints) { if (!solvers2constraints.containsKey(c.getScope()[0].getConstraintSolver())) { Vector<Constraint> newVec = new Vector<Constraint>(); solvers2constraints.put(c.getScope()[0].getConstraintSolver(), newVec); } solvers2constraints.get(c.getScope()[0].getConstraintSolver()).add(c); } for (ConstraintSolver cs : solvers2constraints.keySet()) { Constraint[] toAddOneSolver = solvers2constraints.get(cs).toArray(new Constraint[solvers2constraints.get(cs).size()]); cs.removeConstraints(toAddOneSolver); } this.retractResolverSub(metaVar, res); this.logger.finest("Done retracting resolver."); }
public static void main(String[] args) { String[] symbols = new String[] {"mug1", "mug2", "mug3"}; NameMatchingConstraintSolver solver = new NameMatchingConstraintSolver(symbols); ConstraintNetwork.draw(solver.getConstraintNetwork()); NameVariable[] vars = (NameVariable[]) solver.createVariables(5); vars[0].setConstant("mug1"); // vars[3].setConstant("mug2"); NameMatchingConstraint con01 = new NameMatchingConstraint(Type.EQUALS); con01.setFrom(vars[0]); con01.setTo(vars[1]); System.out.println("n0: " + vars[0]); System.out.println("n1 ground? " + vars[1] + vars[1].isGround()); // System.out.println("n2 ground? " + vars[2] + vars[2].isGround()); System.out.println("Add con01: " + solver.addConstraint(con01)); System.out.println("Removing con01: "); solver.removeConstraint(con01); System.out.println("n1 ground? " + vars[1] + " " + vars[1].isGround()); // Should not be ground // NameMatchingConstraint con12 = new NameMatchingConstraint(Type.EQUALS); // con12.setFrom(vars[1]); // con12.setTo(vars[2]); //// NameMatchingConstraint con23Equals = new NameMatchingConstraint(Type.EQUALS); //// con23Equals.setFrom(vars[2]); //// con23Equals.setTo(vars[3]); //// System.out.println("Add con12 + con23?" + solver.addConstraints(new Constraint[] {con12, // con23Equals})); // // // NameMatchingConstraint con23Different = new NameMatchingConstraint(Type.DIFFERENT); // con23Different.setFrom(vars[2]); // con23Different.setTo(vars[3]); // System.out.println("Add con12 + con23?" + solver.addConstraints(new Constraint[] {con12, // con23Different})); }
/** @param args */ public static void main(String[] args) { MetaCSPLogging.setLevel(TimelinePublisher.class, Level.FINEST); SimplePlanner planner = new SimplePlanner(0, 600, 0); // This is a pointer toward the ActivityNetwork solver of the Scheduler ActivityNetworkSolver groundSolver = (ActivityNetworkSolver) planner.getConstraintSolvers()[0]; MetaCSPLogging.setLevel(planner.getClass(), Level.FINE); // MetaCSPLogging.setLevel(Level.FINEST); // MetaCSPLogging.setLevel(planner.getClass(), Level.FINE); SimpleDomain rd = new SimpleDomain(new int[] {2}, new String[] {"arm"}, "TestDomain"); AllenIntervalConstraint atCupAfterPlace = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint atCup1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint placeCupAfterholding = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint placeCup1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint holdingCupAfterPick = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint holdingCup1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint pickCup1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint atKnifeAfterPlace = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint atKnife1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint placeKnifeAfterholding = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint placeKnife1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint holdingKnifeAfterPick = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint holdingKnife1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint pickKnife1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint atForkAfterPlace = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint atFork1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint placeForkAfterholding = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint placeFork1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint holdingForkAfterPick = new AllenIntervalConstraint( AllenIntervalConstraint.Type.After, AllenIntervalConstraint.Type.After.getDefaultBounds()); AllenIntervalConstraint holdingFork1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); AllenIntervalConstraint pickFork1Duration = new AllenIntervalConstraint( AllenIntervalConstraint.Type.Duration, new Bounds(10, APSPSolver.INF)); SimpleOperator operator1 = new SimpleOperator( "robot1::at_cup1_table1()", new AllenIntervalConstraint[] {atCupAfterPlace}, new String[] {"robot1::place_cup1_table1(arm)"}, new int[] {1}); operator1.addConstraint(atCup1Duration, 0, 0); rd.addOperator(operator1); SimpleOperator operator2 = new SimpleOperator( "robot1::place_cup1_table1(arm)", new AllenIntervalConstraint[] {placeCupAfterholding}, new String[] {"robot1::holding_cup1(arm)"}, new int[] {1}); operator2.addConstraint(placeCup1Duration, 0, 0); rd.addOperator(operator2); SimpleOperator operator3 = new SimpleOperator( "robot1::holding_cup1(arm)", new AllenIntervalConstraint[] {holdingCupAfterPick}, new String[] {"robot1::pick_cup1(arm)"}, new int[] {1}); operator3.addConstraint(holdingCup1Duration, 0, 0); rd.addOperator(operator3); SimpleOperator operator1res = new SimpleOperator("robot1::pick_cup1(arm)", null, null, new int[] {1}); operator1res.addConstraint(pickCup1Duration, 0, 0); rd.addOperator(operator1res); // ........................ SimpleOperator operator4 = new SimpleOperator( "robot1::at_knife1_table1()", new AllenIntervalConstraint[] {atKnifeAfterPlace}, new String[] {"robot1::place_knife1_table1(arm)"}, new int[] {1}); operator4.addConstraint(atKnife1Duration, 0, 0); rd.addOperator(operator4); SimpleOperator operator5 = new SimpleOperator( "robot1::place_knife1_table1(arm)", new AllenIntervalConstraint[] {placeKnifeAfterholding}, new String[] {"robot1::holding_knife1(arm)"}, new int[] {1}); operator5.addConstraint(placeKnife1Duration, 0, 0); rd.addOperator(operator5); SimpleOperator operator6 = new SimpleOperator( "robot1::holding_knife1(arm)", new AllenIntervalConstraint[] {holdingKnifeAfterPick}, new String[] {"robot1::pick_knife1(arm)"}, new int[] {1}); operator6.addConstraint(holdingKnife1Duration, 0, 0); rd.addOperator(operator6); SimpleOperator operator2res = new SimpleOperator("robot1::pick_knife1(arm)", null, null, new int[] {1}); operator2res.addConstraint(pickKnife1Duration, 0, 0); rd.addOperator(operator2res); // ........................ SimpleOperator operator7 = new SimpleOperator( "robot1::at_fork_table1()", new AllenIntervalConstraint[] {atForkAfterPlace}, new String[] {"robot1::place_fork1_table1(arm)"}, new int[] {1}); operator7.addConstraint(atFork1Duration, 0, 0); rd.addOperator(operator7); SimpleOperator operator8 = new SimpleOperator( "robot1::place_fork1_table1(arm)", new AllenIntervalConstraint[] {placeForkAfterholding}, new String[] {"robot1::holding_fork1(arm)"}, new int[] {1}); operator8.addConstraint(placeFork1Duration, 0, 0); rd.addOperator(operator8); SimpleOperator operator9 = new SimpleOperator( "robot1::holding_fork1(arm)", new AllenIntervalConstraint[] {holdingForkAfterPick}, new String[] {"robot1::pick_fork1(arm)"}, new int[] {1}); operator9.addConstraint(holdingFork1Duration, 0, 0); rd.addOperator(operator9); SimpleOperator operator3res = new SimpleOperator("robot1::pick_fork1(arm)", null, null, new int[] {1}); operator3res.addConstraint(pickFork1Duration, 0, 0); rd.addOperator(operator3res); // This adds the domain as a meta-constraint of the SimplePlanner planner.addMetaConstraint(rd); // ... and we also add all its resources as separate meta-constraints MetaCSPLogging.setLevel(Schedulable.class, Level.FINEST); for (Schedulable sch : rd.getSchedulingMetaConstraints()) planner.addMetaConstraint(sch); // INITIAL AND GOAL STATE DEFS Activity three = (Activity) groundSolver.createVariable("robot1"); three.setSymbolicDomain("at_knife1_table1()"); three.setMarking(markings.UNJUSTIFIED); Activity one = (Activity) groundSolver.createVariable("robot1"); one.setSymbolicDomain("at_cup1_table1()"); one.setMarking(markings.UNJUSTIFIED); Activity two = (Activity) groundSolver.createVariable("robot1"); two.setSymbolicDomain("at_fork1_table1()"); two.setMarking(markings.UNJUSTIFIED); planner.backtrack(); TimelinePublisher tp = new TimelinePublisher(groundSolver, new Bounds(0, 100), "robot1"); // TimelinePublisher can also be instantiated w/o bounds, in which case the bounds are // calculated every time publish is called // TimelinePublisher tp = new TimelinePublisher(groundSolver, "Robot1", "Robot2", // "LocalizationService", "RFIDReader1", "LaserScanner1"); TimelineVisualizer viz = new TimelineVisualizer(tp); tp.publish(false, false); // the following call is marked as "skippable" and will most likely be skipped because the // previous call has not finished rendering... tp.publish(false, true); ConstraintNetwork.draw(groundSolver.getConstraintNetwork(), "Constraint Network"); planner.draw(); tp.publish(true, false); }
/** * This backtrack method uses serialization to back up {@link ConstraintNetwork}s before * branching. This allows to backtrack without propagation - but is very memory intensive. In * practice, this does not work on reasonably sized problems. */ private boolean backtrackHelperWithSerialization(MetaVariable metaVariable) { preBacktrack(); if (this.g.getRoot() == null) this.g.addVertex(currentVertex); ConstraintNetwork mostProblematicNetwork = metaVariable.getConstraintNetwork(); logger.fine("Solving conflict: " + metaVariable); ConstraintNetwork[] values = metaVariable.getMetaConstraint().getMetaValues(metaVariable); if (metaVariable.getMetaConstraint().valOH != null && values != null) Arrays.sort(values, metaVariable.getMetaConstraint().valOH); if (values == null || values.length == 0) { this.g.addEdge(new NullConstraintNetwork(null), currentVertex, new TerminalNode(false)); logger.fine("Failure (1)..."); } else { for (ConstraintNetwork value : values) { if (animationTime != 0) { try { Thread.sleep(animationTime); } catch (InterruptedException e) { e.printStackTrace(); } } String valString = ""; if (value.getVariables().length != 0) valString += "Vars = " + Arrays.toString(value.getVariables()); if (value.getConstraints().length != 0) valString += " Cons = " + Arrays.toString(value.getConstraints()); logger.fine("Trying value: " + valString); this.backedUpCNs.add(backupCNs(this)); /** * PRINT INFO ** */ /* long sizeOfBackup = 0; for (HashMap<ConstraintSolver,byte[]> oneHM : backedUpCNs) { for (byte[] oneCN : oneHM.values()) sizeOfBackup += oneCN.length; } DecimalFormat df = new DecimalFormat("#.##"); logger.info("Current backup size: " + df.format((sizeOfBackup/1024.00)) + " KB"); */ /** * END PRINT INFO ** */ if (this.addResolver(mostProblematicNetwork, value)) { this.resolvers.put(mostProblematicNetwork, value); this.metaVarsToMetaCons.put(mostProblematicNetwork, metaVariable.getMetaConstraint()); this.resolversInverseMapping.put(value, mostProblematicNetwork); this.counterMoves++; logger.fine("Success..."); metaVariable.getMetaConstraint().markResolvedSub(metaVariable, value); MetaVariable newConflict = this.getConflict(); if (newConflict == null || breakSearch) { this.g.addEdge(value, currentVertex, new TerminalNode(true)); breakSearch = false; return true; } // addEdege(e,v,v) this.g.addEdge(value, currentVertex, newConflict); currentVertex = newConflict; if (backtrackHelper(newConflict)) return true; logger.fine("Retracting value: " + Arrays.toString(value.getConstraints())); // this.retractResolver(mostProblematicNetwork, value); this.restoreCNs(); this.retractResolverSub(mostProblematicNetwork, value); this.resolvers.remove(mostProblematicNetwork); this.metaVarsToMetaCons.remove(mostProblematicNetwork); this.resolversInverseMapping.remove(value); this.counterMoves--; } else { this.g.addEdge(value, currentVertex, new TerminalNode(false)); logger.fine("Failure... (2)"); } } } logger.fine("Backtracking..."); currentVertex = this.g.getParent(currentVertex); postBacktrack(metaVariable); return false; }
private boolean backtrackHelper(MetaVariable metaVariable) { preBacktrack(); if (this.g.getRoot() == null) this.g.addVertex(currentVertex); ConstraintNetwork mostProblematicNetwork = metaVariable.getConstraintNetwork(); logger.fine("Solving conflict: " + metaVariable); ConstraintNetwork[] values = metaVariable.getMetaConstraint().getMetaValues(metaVariable); if (values != null) for (ConstraintNetwork value : values) value.setAnnotation(metaVariable); if (metaVariable.getMetaConstraint().valOH != null && values != null) { // System.out.println("SORTING with " + metaVariable.getMetaConstraint().valOH.getClass()); Arrays.sort(values, metaVariable.getMetaConstraint().valOH); } if (values == null || values.length == 0) { this.g.addEdge(new NullConstraintNetwork(null), currentVertex, new TerminalNode(false)); logger.fine("Failure (1)..."); } else { for (ConstraintNetwork value : values) { if (animationTime != 0) { try { Thread.sleep(animationTime); } catch (InterruptedException e) { e.printStackTrace(); } } String valString = ""; if (value.getVariables().length != 0) valString += "Vars = " + Arrays.toString(value.getVariables()); if (value.getConstraints().length != 0) valString += " Cons = " + Arrays.toString(value.getConstraints()); logger.fine("Trying value: " + valString); if (this.addResolver(mostProblematicNetwork, value)) { this.resolvers.put(mostProblematicNetwork, value); this.metaVarsToMetaCons.put(mostProblematicNetwork, metaVariable.getMetaConstraint()); this.resolversInverseMapping.put(value, mostProblematicNetwork); this.counterMoves++; logger.fine("Success..."); metaVariable.getMetaConstraint().markResolvedSub(metaVariable, value); MetaVariable newConflict = this.getConflict(); if (newConflict == null || breakSearch) { this.g.addEdge(value, currentVertex, new TerminalNode(true)); breakSearch = false; return true; } // addEdege(e,v,v) this.g.addEdge(value, currentVertex, newConflict); currentVertex = newConflict; if (backtrackHelper(newConflict)) return true; logger.fine("Retracting value: " + Arrays.toString(value.getConstraints())); this.retractResolver(mostProblematicNetwork, value); this.resolvers.remove(mostProblematicNetwork); this.metaVarsToMetaCons.remove(mostProblematicNetwork); this.resolversInverseMapping.remove(value); this.counterMoves--; } else { this.g.addEdge(value, currentVertex, new TerminalNode(false)); logger.fine("Failure... (2)"); } } } logger.fine("Backtracking..."); currentVertex = this.g.getParent(currentVertex); postBacktrack(metaVariable); return false; }