/// <summary> /// Finds the minimal elements, in the partial order induced by unification, of the partial model facts. /// The number of minimal elements gives a lower bound on the number of distinct facts that must appear in an valid model. /// </summary> private void BuildPartialModelLowerBounds() { //// Step 1. Bin facts by head symbol. Term pattern; LinkedList <Term> gbin; Map <Term, MutableTuple <bool> > ngbin; var grdBins = new Map <Symbol, LinkedList <Term> >(Symbol.Compare); //// A term maps to true if it is deleted from the bin. var ngBins = new Map <Symbol, Map <Term, MutableTuple <bool> > >(Symbol.Compare); foreach (var f in Facts.Facts) { pattern = MkPattern(f); if (pattern == f) { Contract.Assert(pattern.Groundness == Groundness.Ground); if (!grdBins.TryFindValue(f.Symbol, out gbin)) { gbin = new LinkedList <Term>(); grdBins.Add(f.Symbol, gbin); } gbin.AddLast(f); } else { Contract.Assert(pattern.Groundness == Groundness.Variable); if (!ngBins.TryFindValue(f.Symbol, out ngbin)) { ngbin = new Map <Term, MutableTuple <bool> >(Term.Compare); ngBins.Add(f.Symbol, ngbin); } if (!ngbin.ContainsKey(pattern)) { ngbin.Add(pattern, new MutableTuple <bool>(false)); } } } //// Step 2. Any non-ground fact that matches with a ground fact is not minimal. Matcher matcher; foreach (var kv in ngBins) { ngbin = kv.Value; if (!grdBins.TryFindValue(kv.Key, out gbin)) { continue; } foreach (var p in ngbin) { matcher = new Matcher(p.Key); foreach (var g in gbin) { if (matcher.TryMatch(g)) { p.Value.Item1 = true; break; } } } PruneBin(ngbin); } //// Step 3. Iteratively compute the minimal elements until a fixpoint is reached. Term mgu; bool changed; LinkedList <Term> newGLBs; foreach (var kv in ngBins) { ngbin = kv.Value; changed = true; while (changed) { changed = false; newGLBs = null; foreach (var p1 in ngbin) { foreach (var p2 in ngbin.GetEnumerable(p1.Key)) { if (p1.Key == p2.Key) { continue; } else if (Unifier.IsUnifiable(p1.Key, p2.Key, x => MkNormalizedVar(Facts.Index, x), out mgu)) { if (mgu != p1.Key) { p1.Value.Item1 = true; } if (mgu != p2.Key) { p2.Value.Item1 = true; } if (mgu != p1.Key && mgu != p2.Key && !ngbin.ContainsKey(mgu)) { changed = true; if (newGLBs == null) { newGLBs = new LinkedList <Term>(); } newGLBs.AddLast(mgu); } } } } PruneBin(ngbin, newGLBs); } } //// Step 4. Create lower bounds Cardinality card; foreach (var kv in ngBins) { grdBins.TryFindValue(kv.Key, out gbin); if (gbin == null) { card = new Cardinality(new BigInteger(kv.Value.Count)); } else { card = new Cardinality(new BigInteger(kv.Value.Count) + new BigInteger(gbin.Count)); } AddConstraint(varMap[(UserSymbol)kv.Key].Item3 >= new CardCnst(card)); } foreach (var kv in grdBins) { if (ngBins.ContainsKey(kv.Key)) { continue; } AddConstraint(varMap[(UserSymbol)kv.Key].Item3 >= new CardCnst(new Cardinality(new BigInteger(kv.Value.Count)))); } }