/// <summary> /// Creates a PropNet for the game with the given description. /// </summary> public static PropNet Create(IList<Expression> description, IComponentFactory componentFactory) { Console.WriteLine("Building propnet..."); _componentFactory = componentFactory; DateTime startTime = DateTime.UtcNow; description = GdlCleaner.Run(description); description = DeORer.Run(description); description = VariableConstrainer.ReplaceFunctionValuedVariables(description); description = Relationizer.Run(description); description = CondensationIsolator.Run(description); if (Logger.IsDebugEnabled) foreach (var gdl in description) Logger.Debug(gdl); //We want to start with a rule graph and follow the rule graph. Start by finding general information about the game ISentenceDomainModel model = SentenceDomainModelFactory.CreateWithCartesianDomains(description); //Restrict domains to values that could actually come up in rules. //See chinesecheckers4's "count" relation for an example of why this could be useful. model = SentenceDomainModelOptimizer.RestrictDomainsToUsefulValues(model); Logger.Debug("Setting constants..."); //TODO: ConstantChecker constantChecker = ConstantCheckerFactory.createWithForwardChaining(model); IConstantChecker constantChecker = ConstantCheckerFactory.CreateWithProver(model); Logger.Debug("Done setting constants"); HashSet<String> sentenceFormNames = SentenceForms.GetNames(model.SentenceForms); bool usingBase = sentenceFormNames.Contains("base"); bool usingInput = sentenceFormNames.Contains("input"); //For now, we're going to build this to work on those with a particular restriction on the dependency graph: //Recursive loops may only contain one sentence form. This describes most games, but not all legal games. IDictionary<ISentenceForm, ICollection<ISentenceForm>> dependencyGraph = model.DependencyGraph; Logger.Debug("Computing topological ordering... "); //ConcurrencyUtils.checkForInterruption(); IEnumerable<ISentenceForm> topologicalOrdering = GetTopologicalOrdering(model.SentenceForms, dependencyGraph, usingBase, usingInput); Logger.Debug("done"); List<TermObject> roles = GameContainer.GameInformation.GetRoles(); var components = new Dictionary<Fact, IComponent>(); var negations = new Dictionary<Fact, IComponent>(); IConstant trueComponent = _componentFactory.CreateConstant(true); IConstant falseComponent = _componentFactory.CreateConstant(false); var functionInfoMap = new Dictionary<ISentenceForm, FunctionInfo>(); var completedSentenceFormValues = new Dictionary<ISentenceForm, ICollection<Fact>>(); var sentenceFormAdder = new SentenceFormAdder(_componentFactory, DoesProcessor, TrueProcessor, TempFact); foreach (ISentenceForm form in topologicalOrdering) { //ConcurrencyUtils.checkForInterruption(); Logger.Debug("Adding sentence form " + form); if (constantChecker.IsConstantForm(form)) { Logger.Debug(" (constant)"); //Only add it if it's important if (form.Name.Equals(GameContainer.Parser.TokLegal) || form.Name.Equals(GameContainer.Parser.TokGoal) || form.Name.Equals(GameContainer.Parser.TokInit)) { //Add it foreach (Fact trueSentence in constantChecker.GetTrueSentences(form)) { var trueProp = _componentFactory.CreateProposition(trueSentence); trueProp.AddInput(trueComponent); trueComponent.AddOutput(trueProp); components[trueSentence] = trueComponent; } } Logger.Debug("Checking whether {0} is a functional constant...", form); AddConstantsToFunctionInfo(form, constantChecker, functionInfoMap); AddFormToCompletedValues(form, completedSentenceFormValues, constantChecker); continue; } Logger.Debug(string.Empty); //TODO: Adjust "recursive forms" appropriately //Add a temporary sentence form thingy? ... var temporaryComponents = new Dictionary<Fact, IComponent>(); var temporaryNegations = new Dictionary<Fact, IComponent>(); sentenceFormAdder.AddSentenceForm(form, model, components, negations, trueComponent, falseComponent, usingBase, usingInput, ImmutableHashSet.Create(form), temporaryComponents, temporaryNegations, functionInfoMap, constantChecker, completedSentenceFormValues); //TODO: Pass these over groups of multiple sentence forms if (temporaryComponents.Any()) Logger.Debug("Processing temporary components..."); ProcessTemporaryComponents(temporaryComponents, temporaryNegations, components, negations, trueComponent, falseComponent); AddFormToCompletedValues(form, completedSentenceFormValues, components); //TODO: Add this, but with the correct total number of components (not just Propositions) Console.WriteLine(" {0} components added", completedSentenceFormValues[form].Count); } //Connect "next" to "true" Logger.Debug("Adding transitions..."); AddTransitions(components); //Set up "init" proposition Logger.Debug("Setting up 'init' proposition..."); SetUpInit(components, trueComponent, falseComponent); //Now we can safely... RemoveUselessBasePropositions(components, negations, trueComponent, falseComponent); Logger.Debug("Creating component set..."); var componentSet = new HashSet<IComponent>(components.Values); CompleteComponentSet(componentSet); //ConcurrencyUtils.checkForInterruption(); Logger.Debug("Initializing propnet object..."); //Make it look the same as the PropNetFactory results, until we decide how we want it to look NormalizePropositions(componentSet); var propnet = new PropNet(roles, componentSet); Logger.Debug("Done setting up propnet; took {0}ms, has {1} components and {2} links", (DateTime.UtcNow - startTime).TotalMilliseconds, componentSet.Count, propnet.GetNumLinks()); Logger.Debug("Propnet has {0} ands; {1} ors; {2} nots", propnet.GetNumAnds(), propnet.GetNumOrs(), propnet.GetNumNots()); if (ConfigurationManager.AppSettings["OutputPropNet"] == "true") propnet.RenderToFile("propnet.dot"); return propnet; }
//TODO: Create a version with just a set of components that we can share with post-optimizations private static void OptimizeAwayFalse(IDictionary<Fact, IComponent> components, Dictionary<Fact, IComponent> negations, PropNet pn, IComponent trueComponent, IComponent falseComponent) { Debug.Assert((components != null && negations != null) || pn != null); Debug.Assert((components == null && negations == null) || pn == null); foreach (IComponent output in falseComponent.Outputs.ToList()) { if (IsEssentialProposition(output) || output is ITransition) { //Since this is the false constant, there are a few "essential" types we don't actually want to keep around. if (!IsLegalOrGoalProposition(output)) continue; } var prop = output as IProposition; if (prop != null) { //Move its outputs to be outputs of false foreach (IComponent child in prop.Outputs) { //Disconnect child.RemoveInput(prop); //output.removeOutput(child); //do at end //Reconnect; will get children before returning, if nonessential falseComponent.AddOutput(child); child.AddInput(falseComponent); } prop.RemoveAllOutputs(); if (!IsEssentialProposition(prop)) { //Remove the proposition entirely falseComponent.RemoveOutput(prop); output.RemoveInput(falseComponent); //Update its location to the trueComponent in our map if (components != null) { components[prop.Name] = falseComponent; negations[prop.Name] = trueComponent; } else pn.RemoveComponent(prop); } } else { var and = output as IAnd; if (and != null) { //Attach children of and to falseComponent foreach (IComponent child in and.Outputs) { child.AddInput(falseComponent); falseComponent.AddOutput(child); child.RemoveInput(and); } //Disconnect and completely and.RemoveAllOutputs(); foreach (IComponent parent in and.Inputs) parent.RemoveOutput(and); and.RemoveAllInputs(); if (pn != null) pn.RemoveComponent(and); } else { var or = output as IOr; if (or != null) { //Remove as input from or or.RemoveInput(falseComponent); falseComponent.RemoveOutput(or); //If or has only one input, remove it if (or.Inputs.Count == 1) { IComponent input = or.GetSingleInput(); or.RemoveInput(input); input.RemoveOutput(or); foreach (IComponent output1 in or.Outputs) { //Disconnect from and output1.RemoveInput(or); //or.removeOutput(out); //do at end //Connect directly to the new input output1.AddInput(input); input.AddOutput(output1); } or.RemoveAllOutputs(); if (pn != null) { pn.RemoveComponent(or); } } else if (!or.Inputs.Any()) { if (pn != null) { pn.RemoveComponent(or); } } } else { var not = output as INot; if (not != null) { //Disconnect from falseComponent not.RemoveInput(falseComponent); falseComponent.RemoveOutput(not); //Connect all children of the not to trueComponent foreach (IComponent child in not.Outputs) { //Disconnect child.RemoveInput(not); //not.removeOutput(child); //Do at end //Connect to trueComponent child.AddInput(trueComponent); trueComponent.AddOutput(child); } not.RemoveAllOutputs(); if (pn != null) pn.RemoveComponent(not); } else if (output is ITransition) { //??? throw new Exception("Fix optimizeAwayFalse's case for Transitions"); } } } } } }
private static void OptimizeAwayTrue(IDictionary<Fact, IComponent> components, IDictionary<Fact, IComponent> negations, PropNet pn, IComponent trueComponent, IComponent falseComponent) { Debug.Assert((components != null && negations != null) || pn != null); foreach (IComponent output in trueComponent.Outputs.ToList()) { if (IsEssentialProposition(output) || output is ITransition) continue; var prop = output as IProposition; if (prop != null) { //Move its outputs to be outputs of true foreach (IComponent child in prop.Outputs) { //Disconnect child.RemoveInput(prop); //output.removeOutput(child); //do at end //Reconnect; will get children before returning, if nonessential trueComponent.AddOutput(child); child.AddInput(trueComponent); } prop.RemoveAllOutputs(); if (!IsEssentialProposition(prop)) { //Remove the proposition entirely trueComponent.RemoveOutput(prop); output.RemoveInput(trueComponent); //Update its location to the trueComponent in our map if (components != null) { components[prop.Name] = trueComponent; Debug.Assert(negations != null, "negations != null"); negations[prop.Name] = falseComponent; } else pn.RemoveComponent(prop); } } else { var or = output as IOr; if (or != null) { //Attach children of or to trueComponent foreach (IComponent child in or.Outputs) { child.AddInput(trueComponent); trueComponent.AddOutput(child); child.RemoveInput(or); } //Disconnect or completely or.RemoveAllOutputs(); foreach (IComponent parent in or.Inputs) parent.RemoveOutput(or); or.RemoveAllInputs(); if (pn != null) pn.RemoveComponent(or); } else { var and = output as IAnd; if (and != null) { //Remove as input from and and.RemoveInput(trueComponent); trueComponent.RemoveOutput(and); //If and has only one input, remove it if (and.Inputs.Count == 1) { IComponent input = and.GetSingleInput(); and.RemoveInput(input); input.RemoveOutput(and); foreach (IComponent output1 in and.Outputs) { //Disconnect from and output1.RemoveInput(and); //and.removeOutput(out); //do at end //Connect directly to the new input output1.AddInput(input); input.AddOutput(output1); } and.RemoveAllOutputs(); if (pn != null) pn.RemoveComponent(and); } else if (and.Inputs.Any()) if (pn != null) pn.RemoveComponent(and); } else { var not = output as INot; if (not != null) { //Disconnect from trueComponent not.RemoveInput(trueComponent); trueComponent.RemoveOutput(not); //Connect all children of the not to falseComponent foreach (IComponent child in not.Outputs) { //Disconnect child.RemoveInput(not); //not.removeOutput(child); //Do at end //Connect to falseComponent child.AddInput(falseComponent); falseComponent.AddOutput(child); } not.RemoveAllOutputs(); if (pn != null) pn.RemoveComponent(not); } } } } } }
public static void OptimizeAwayTrueAndFalse(PropNet pn, IComponent trueComponent, IComponent falseComponent) { while (HasNonEssentialChildren(trueComponent) || HasNonEssentialChildren(falseComponent)) { OptimizeAwayTrue(null, null, pn, trueComponent, falseComponent); OptimizeAwayFalse(null, null, pn, trueComponent, falseComponent); } }