Example #1
0
        public static List<Expression> Run(IList<Expression> description)
        {
            // This class is not put together in any "optimal" way, so it's left in an unpolished state for
            // now. A better version would use estimates of the impact of breaking apart rules. (It also needs
            // to stop itself from making multiple new relations with the same meaning.)

            //This version will be rather advanced. In particular, it will try to incorporate
            //1) More thorough scanning for condensations;
            //2) Condensations that are only safe to perform because of mutexes.

            //TODO: Don't perform condensations on stuff like (Add _ _ _)...
            //In general, don't perform condensations where the headroom is huge?
            //Better yet... DON'T perform condensations on recursive functions!
            //As for headroom... maybe make sure that # of vars eliminated > # "kept"
            //Or make sure none are kept? Use directional connected components?

            //How do we define a condensation, and what needs to be true in it?
            //Definition: A condensation set is a set of conjuncts of a sentence.
            //Restrictions:
            //1) There must be some variable not in the head of the sentence that appears exclusively in the
            //   condensation set. (This means we can easily find sets one of which must be a condensation set.)
            //2) For any variable appearing in a distinct or not conjunct in the set, there must be a positive
            //   conjunct in the set also containing that variable. This does apply to variables found in the head.
            //3) There must be at least one non-distinct literal outside the condensation set.

            //How mutexes work:
            //Say we have a rule
            //  (<= (r1 ?b)
            //      (r2 ?a ?b ?c)
            //      (r3 ?b ?c)
            //		(r4 ?a)
            //		(r5 ?c))
            //If we wanted to factor out ?a, we'd normally have to do
            /*  (<= (r6 ?b ?c)
             * 		(r2 ?a ?b ?c)
             * 		(r4 ?a))
             *  (<= (r1 ?b)
             * 		(r6 ?b ?c)
             * 		(r3 ?b ?c)
             * 		(r5 ?c))
             * But if we know r2 is a mutex, instead we can do (notice r2 splitting):
             *  (<= (r6 ?b)
             * 		(r2 ?a ?b ?c)
             * 		(r4 ?a))
             *  (<= (r1 ?b)
             *  	(r2 ?a ?b ?c)
             *  	(r6 ?b)
             *  	(r3 ?b ?c)
             *  	(r5 ?c))
             * Which in turn becomes:
             *  (<= (r6 ?b)
             * 		(r2 ?a ?b ?c)
             * 		(r4 ?a))
             *  (<= (r7 ?b)
             *  	(r2 ?a ?b ?c)
             *  	(r3 ?b ?c)
             *  	(r5 ?c))
             *  (<= (r1 ?b)
             *  	(r6 ?b)
             *		(r7 ?b))
             * Both r6 and r7 can be further condensed to ignore ?c and ?a, respectively. What just happened?
             * 1) The condensation set for ?a included the mutex r2.
             * 2) r2 (by itself) would have required ?c to be included as an argument passed back to the
             *      original rule, which is undesirable. Instead, as it's a mutex, we leave a copy in the
             *      original rule and don't include the ?c.
             *
             * So, what kind of algorithm can we find to solve this task?
             */
            var newDescription = new List<Expression>();
            var rulesToAdd = new Queue<Implication>();

            foreach (Expression gdl in description)
            {
                var implication = gdl as Implication;
                if (implication == null)
                    newDescription.Add(gdl);
                else
                    rulesToAdd.Enqueue(implication);
            }

            //Don't use the model indiscriminately; it reflects the old description, not necessarily the new one
            ISentenceDomainModel model = SentenceDomainModelFactory.CreateWithCartesianDomains(description);
            model = SentenceDomainModelOptimizer.RestrictDomainsToUsefulValues(model);
            var sentenceNameSource = new UnusedSentenceNameSource(model);
            //TODO: ConstantChecker constantChecker = ConstantCheckerFactory.createWithForwardChaining(model);
            IConstantChecker constantChecker = ConstantCheckerFactory.CreateWithProver(model);

            ISet<ISentenceForm> constantForms = model.ConstantSentenceForms;

            ISentenceDomain condensorDomain = model;
            var curDescription = new List<Expression>(description);
            while (rulesToAdd.Any())
            {
                Implication curRule = rulesToAdd.Dequeue();
                if (IsRecursive(curRule))
                {
                    //Don't mess with it!
                    newDescription.Add(curRule);
                    continue;
                }
                Fact curRuleHead = curRule.Consequent;

                if (SentenceFormAdder.InSentenceFormGroup(curRuleHead, constantForms))
                {
                    newDescription.Add(curRule);
                    continue;
                }
                ISet<Expression> condensationSet = GetCondensationSet(curRule, condensorDomain, constantChecker, sentenceNameSource);

                if (condensationSet != null)
                {
                    List<Implication> newRules = ApplyCondensation(condensationSet, curRule, sentenceNameSource);
                    newRules.ForEach(rulesToAdd.Enqueue);

                    //Since we're making only small changes, we can readjust
                    //the model as we go, instead of recomputing it
                    var oldRules = new List<Expression> { curRule };
                    var replacementDescription = new List<Expression>(curDescription);
                    replacementDescription.RemoveAll(oldRules.Contains);
                    replacementDescription.AddRange(newRules);
                    curDescription = replacementDescription;
                    condensorDomain = AugmentModelWithNewForm(condensorDomain, newRules);
                }
                else
                    newDescription.Add(curRule);
            }
            return newDescription;
        }
Example #2
0
        private static ISet<Expression> GetCondensationSet(Implication rule, ISentenceDomain model, IConstantChecker checker,
                                                           UnusedSentenceNameSource sentenceNameSource)
        {
            //We use each variable as a starting point
            List<TermVariable> varsInRule = rule.VariablesOrEmpty.ToList();
            List<TermVariable> varsInHead = rule.Consequent.VariablesOrEmpty.ToList();
            var varsNotInHead = new List<TermVariable>(varsInRule);
            varsNotInHead.RemoveAll(varsInHead.Contains);

            foreach (TermVariable var in varsNotInHead)
            {
                var minSet = new HashSet<Expression>();
                foreach (Expression literal in rule.Antecedents.Conjuncts)
                    if (literal.VariablesOrEmpty.Contains(var))
                        minSet.Add(literal);

                //#1 is already done
                //Now we try #2
                var varsNeeded = new HashSet<TermVariable>();
                var varsSupplied = new HashSet<TermVariable>();
                foreach (Expression literal in minSet)
                {
                    if (literal is Negation)
                    {
                        foreach (var variable in literal.VariablesOrEmpty)
                            varsNeeded.Add(variable);
                    }
                    else if (literal is Fact)
                    {
                        if (((Fact)literal).RelationName == GameContainer.Parser.TokDistinct)
                            foreach (var variable in literal.VariablesOrEmpty)
                                varsNeeded.Add(variable);
                        else
                            foreach (var variable in literal.VariablesOrEmpty)
                                varsSupplied.Add(variable);
                    }
                }
                varsNeeded.RemoveWhere(varsSupplied.Contains);
                if (varsNeeded.Any())
                    continue;

                var candidateSuppliersList = new List<ISet<Expression>>();
                foreach (TermVariable varNeeded in varsNeeded)
                {
                    var suppliers = new HashSet<Expression>();
                    foreach (Expression literal in rule.Antecedents.Conjuncts)
                        if (literal is Fact)
                            if (literal.VariablesOrEmpty.Contains(varNeeded))
                                suppliers.Add(literal);
                    candidateSuppliersList.Add(suppliers);
                }

                //TODO: Now... I'm not sure if we want to minimize the number of literals added, or the number of variables added
                //Right now, I don't have time to worry about optimization. Currently, we pick one at random
                //TODO: Optimize this
                var literalsToAdd = new HashSet<Expression>();
                foreach (ISet<Expression> suppliers in candidateSuppliersList)
                    if (!suppliers.Intersect(literalsToAdd).Any())
                        literalsToAdd.Add(suppliers.First());

                minSet.UnionWith(literalsToAdd);

                if (GoodCondensationSetByHeuristic(minSet, rule, model, checker, sentenceNameSource))
                    return minSet;

            }
            return null;
        }
Example #3
0
        private static bool GoodCondensationSetByHeuristic(ICollection<Expression> minSet, Implication rule, ISentenceDomain model,
            IConstantChecker checker, UnusedSentenceNameSource sentenceNameSource)
        {
            //We actually want the sentence model here so we can see the domains
            //also, if it's a constant, ...
            //Anyway... we want to compare the heuristic for the number of assignments
            //and/or links that will be generated with or without the condensation set
            //Heuristic for a rule is A*(L+1), where A is the number of assignments and
            //L is the number of literals, unless L = 1, in which case the heuristic is
            //just A. This roughly captures the number of links that would be generated
            //if this rule were to be generated.
            //Obviously, there are differing degrees of accuracy with which we can
            //represent A.
            //One way is taking the product of all the variables in all the domains.
            //However, we can do better by actually asking the Assignments class for
            //its own heuristic of how it would implement the rule as-is.
            //The only tricky aspect here is that we need an up-to-date SentenceModel,
            //and in some cases this could be expensive to compute. Might as well try
            //it, though...

            //Heuristic for the rule as-is:

            long assignments = AssignmentsImpl.GetNumAssignmentsEstimate(rule,
                SentenceDomainModels.GetVarDomains(rule, model, SentenceDomainModels.VarDomainOpts.IncludeHead),
                checker);
            int literals = rule.Consequent.Arity;
            if (literals > 1)
                literals++;

            //We have to "and" the literals together
            //Note that even though constants will be factored out, we're concerned here
            //with getting through them in a reasonable amount of time, so we do want to
            //count them. TODO: Not sure if they should be counted in L, though...
            long curRuleHeuristic = assignments * literals;
            //And if we split them up...
            List<Implication> newRules = ApplyCondensation(minSet, rule, sentenceNameSource);
            Implication r1 = newRules[0], r2 = newRules[1];

            //Augment the model
            ISentenceDomain newModel = AugmentModelWithNewForm(model, newRules);

            long a1 = AssignmentsImpl.GetNumAssignmentsEstimate(r1,
                SentenceDomainModels.GetVarDomains(r1, newModel, SentenceDomainModels.VarDomainOpts.IncludeHead), checker);
            long a2 = AssignmentsImpl.GetNumAssignmentsEstimate(r2,
                SentenceDomainModels.GetVarDomains(r2, newModel, SentenceDomainModels.VarDomainOpts.IncludeHead), checker);
            int l1 = r1.Consequent.Arity; if (l1 > 1) l1++;
            int l2 = r2.Consequent.Arity; if (l2 > 1) l2++;

            //Whether we split or not depends on what the two heuristics say
            long newRulesHeuristic = a1 * l1 + a2 * l2;
            return newRulesHeuristic < curRuleHeuristic;
        }
Example #4
0
        private static List<Implication> ApplyCondensation(ICollection<Expression> condensationSet, Implication rule,
            UnusedSentenceNameSource sentenceNameSource)
        {
            var varsInCondensationSet = new HashSet<TermVariable>();
            foreach (Expression literal in condensationSet)
                foreach (var variable in literal.VariablesOrEmpty)
                    varsInCondensationSet.Add(variable);
            var varsToKeep = new HashSet<TermVariable>();
            //Which vars do we "keep" (put in our new condensed literal)?
            //Vars that are both:
            //1) In the condensation set, in a non-mutex literal
            //2) Either in the head or somewhere else outside the condensation set
            foreach (Expression literal in condensationSet)
                foreach (var variable in literal.VariablesOrEmpty)
                    varsToKeep.Add(variable);

            var varsToKeep2 = new HashSet<TermVariable>();
            foreach (var variable in rule.Consequent.VariablesOrEmpty)
                varsToKeep2.Add(variable);
            foreach (Expression literal in rule.Antecedents.Conjuncts)
                if (!condensationSet.Contains(literal))
                    foreach (var variable in literal.VariablesOrEmpty)
                        varsToKeep2.Add(variable);

            varsToKeep.IntersectWith(varsToKeep2);

            //Now we're ready to split it apart
            //Let's make the new rule
            var orderedVars = new List<Term>(varsToKeep);
            int condenserName = sentenceNameSource.GetNameWithPrefix(rule.Consequent.RelationName);
            //Make the rule head
            var condenserHead = new VariableFact(false, condenserName, orderedVars.ToArray());
            var condenserBody = new List<Expression>(condensationSet);
            var condenserRule = new Implication(condenserHead, condenserBody.ToArray());

            //TODO: Look for existing rules matching the new one
            var remainingLiterals = rule.Antecedents.Conjuncts.Where(literal => !condensationSet.Contains(literal)).ToList();

            remainingLiterals.Add(condenserHead);
            var modifiedRule = new Implication(rule.Consequent, remainingLiterals.ToArray());

            var newRules = new List<Implication>(2) { condenserRule, modifiedRule };
            return newRules;
        }