/// <summary> /// Optimizes an already-existing propnet by removing useless leaves. These are components that have no /// outputs, but have no special meaning in GDL that requires them to stay. /// </summary> /// <param name="pn"></param> public static void LopUselessLeaves(PropNet pn) { //Approach: Collect useful propositions based on a backwards //search from goal/legal/terminal (passing through transitions) var usefulComponents = new HashSet<IComponent>(); //TODO: Also try with queue? var toAdd = new Stack<IComponent>(); toAdd.Push(pn.TerminalProposition); usefulComponents.Add(pn.InitProposition); //Can't remove it... IEnumerable<IProposition> goalProps = pn.GoalPropositions.Values.SelectMany(goalProp => goalProp); foreach (IProposition prop in goalProps) toAdd.Push(prop); IEnumerable<IProposition> legalProps = pn.LegalPropositions.Values.SelectMany(legalProp => legalProp); foreach (IProposition prop in legalProps) toAdd.Push(prop); while (toAdd.Any()) { IComponent curComp = toAdd.Pop(); if (usefulComponents.Contains(curComp)) //We've already added it continue; usefulComponents.Add(curComp); foreach (IComponent input in curComp.Inputs) toAdd.Push(input); } //Remove the components not marked as useful var allComponents = new List<IComponent>(pn.Components); foreach (IComponent c in allComponents) if (!usefulComponents.Contains(c)) pn.RemoveComponent(c); }
public void Initialize(List<Expression> description) { _propNet = OptimizingPropNetFactory.Create(description, new BackComponentFactory()); _roles = _propNet.Roles; //TODO: You need to ensure that propositions in your propnet are set to the correct initial state // i.e init nodes set to true and propogated through. constant nodes set to there correct true/false value }
/// <summary> /// Potentially optimizes an already-existing propnet by removing propositions with no special meaning. The inputs and outputs /// of those propositions are connected to one another. This is unlikely to improve performance unless values of every single /// component are stored (outside the propnet). /// </summary> /// <param name="pn"></param> public static void RemoveAnonymousPropositions(PropNet pn) { var toSplice = new List<IProposition>(); var toReplaceWithFalse = new List<IProposition>(); foreach (IProposition p in pn.Propositions) { //If it's important, continue to the next proposition if (p.Inputs.Count == 1 && p.GetSingleInput() is ITransition) //It's a base proposition continue; Fact sentence = p.Name; Fact relation = sentence; int name = relation.RelationName; var parser = GameContainer.Parser; if (name == parser.TokLegal || name == parser.TokGoal || name == parser.TokDoes || name == parser.TokInit || name == parser.TokTerminal || sentence.RelationName == GameContainer.SymbolTable["INIT"]) continue; if (p.Inputs.Count < 1) { //Needs to be handled separately... //because this is an always-false true proposition //and it might have and gates as outputs toReplaceWithFalse.Add(p); continue; } if (p.Inputs.Count != 1) throw new Exception(string.Format("Might have falsely declared {0} to be unimportant?", p.Name)); //Not important //System.out.println("Removing " + p); toSplice.Add(p); } foreach (IProposition p in toSplice) { //Get the inputs and outputs... HashSet<IComponent> inputs = p.Inputs; HashSet<IComponent> outputs = p.Outputs; //Remove the proposition... pn.RemoveComponent(p); //And splice the inputs and outputs back together if (inputs.Count > 1) throw new Exception("Programmer made a bad assumption here... might lead to trouble?"); foreach (IComponent input in inputs) foreach (IComponent output in outputs) { input.AddOutput(output); output.AddInput(input); } } foreach (IProposition p in toReplaceWithFalse) throw new Exception(string.Format("Should be replacing {0} with false, but should do that in the OPNF, really; better equipped to do that there", p)); }
/// <summary> /// Removes from the propnet all components that are discovered through type /// inference to only ever be true or false, replacing them with their values /// appropriately. This method may remove base and input propositions that are /// shown to be always false (or, in the case of base propositions, those that /// are always true). /// </summary> /// <param name="pn"></param> /// <param name="basesTrueByInit">The set of base propositions that are true on the first turn of the game.</param> public void RemoveUnreachableBasesAndInputs(PropNet pn, HashSet<IProposition> basesTrueByInit) { //If this doesn't contain a component, that's the equivalent of Type.NEITHER var reachability = new Dictionary<IComponent, Type>(); //Keep track of the number of true inputs to AND gates and false inputs to //OR gates. var numTrueInputs = new Bag<IComponent>(); var numFalseInputs = new Bag<IComponent>(); var toAdd = new Stack<Tuple<IComponent, TypeCode>>(); //It's easier here if we get just the one-way version of the map var legalsToInputs = new Dictionary<IProposition, IProposition>(); foreach (IProposition legalProp in pn.LegalPropositions.Values.SelectMany(v => v)) { IProposition inputProp = pn.LegalInputMap[legalProp]; if (inputProp != null) legalsToInputs[legalProp] = inputProp; } //All constants have their values foreach (IComponent c in pn.Components) //ConcurrencyUtils.checkForInterruption(); if (c is IConstant) toAdd.Push(c.Value ? Tuple.Create(c, TypeCode.True) : Tuple.Create(c, TypeCode.False)); //Every input can be false (we assume that no player will have just one move allowed all game) foreach (IProposition p in pn.InputPropositions.Values) toAdd.Push(Tuple.Create((IComponent)p, TypeCode.False)); //Every base with "init" can be true, every base without "init" can be false foreach (IProposition baseProp in pn.BasePropositions.Values) toAdd.Push(basesTrueByInit.Contains(baseProp) ? Tuple.Create((IComponent)baseProp, TypeCode.True) : Tuple.Create((IComponent)baseProp, TypeCode.False)); //Keep INIT, for those who use it IProposition initProposition = pn.InitProposition; toAdd.Push(Tuple.Create((IComponent)initProposition, TypeCode.Both)); while (toAdd.Any()) { //ConcurrencyUtils.checkForInterruption(); Tuple<IComponent, TypeCode> curEntry = toAdd.Pop(); IComponent curComp = curEntry.Item1; var newInputType = new Type(curEntry.Item2); Type oldType = reachability[curComp] ?? new Type(TypeCode.Neither); //We want to send only the new addition to our children, //for consistency in our parent-true and parent-false //counts. //Make sure we don't double-apply a type. var typeToAdd = new Type(TypeCode.Neither); // Any new values that we discover we can have this iteration. if (curComp is IProposition) typeToAdd = newInputType; else if (curComp is ITransition) typeToAdd = newInputType; else if (curComp is IConstant) typeToAdd = newInputType; else if (curComp is INot) typeToAdd = new Type(newInputType.Opposite()); else if (curComp is IAnd) { if (newInputType.HasTrue) { numTrueInputs.Add(curComp); if (numTrueInputs.Count(n => n == curComp) == curComp.Inputs.Count) typeToAdd = new Type(TypeCode.True); } if (newInputType.HasFalse) typeToAdd = new Type(typeToAdd.With(TypeCode.False)); } else if (curComp is IOr) { if (newInputType.HasFalse) { numFalseInputs.Add(curComp); if (numFalseInputs.Count(n => n == curComp) == curComp.Inputs.Count) typeToAdd = new Type(TypeCode.False); } if (newInputType.HasTrue) typeToAdd = new Type(typeToAdd.With(TypeCode.True)); } else throw new Exception("Unhandled component type " + curComp.GetType()); if (oldType.Includes(typeToAdd)) //We don't know anything new about curComp continue; reachability[curComp] = new Type(typeToAdd.With(oldType)); typeToAdd = new Type(typeToAdd.Minus(oldType)); if (typeToAdd == TypeCode.Neither) throw new Exception("Something's messed up here"); //Add all our children to the stack foreach (IComponent output in curComp.Outputs) toAdd.Push(Tuple.Create(output, (TypeCode)typeToAdd)); if (legalsToInputs.ContainsKey((IProposition)curComp)) { IProposition inputProp = legalsToInputs[(IProposition)curComp]; if (inputProp == null) throw new Exception("IllegalState"); toAdd.Push(Tuple.Create((IComponent)inputProp, (TypeCode)typeToAdd)); } } IConstant trueConst = _componentFactory.CreateConstant(true); IConstant falseConst = _componentFactory.CreateConstant(false); pn.AddComponent(trueConst); pn.AddComponent(falseConst); //Make them the input of all false/true components foreach (var entry in reachability) { TypeCode type = entry.Value; if (type == TypeCode.True || type == TypeCode.False) { IComponent c = entry.Key; if (c is IConstant) //Don't bother trying to remove this continue; //Disconnect from inputs foreach (IComponent input in c.Inputs) input.RemoveOutput(c); c.RemoveAllInputs(); if (type == TypeCode.True ^ (c is INot)) { c.AddInput(trueConst); trueConst.AddOutput(c); } else { c.AddInput(falseConst); falseConst.AddOutput(c); } } } OptimizingPropNetFactory.OptimizeAwayTrueAndFalse(pn, trueConst, falseConst); }
/// <summary> /// Optimizes an already-existing propnet by removing propositions of the form (init ?x). Does NOT remove the proposition "INIT". /// </summary> /// <param name="pn"></param> public static void RemoveInits(PropNet pn) { var toRemove = (from p in pn.Propositions let relation = p.Name where relation.RelationName == GameContainer.Parser.TokInit select p).ToList(); foreach (IProposition p in toRemove) pn.RemoveComponent(p); }