/// <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)))); } }
private bool Propagate(CardVar cvar) { Cardinality cd; CardRange lhs, rhs; CardRange intr, intrp; CardConstraint con; var stack = new Stack <CardConstraint>(); EnqueueConstraints(cvar, stack); while (stack.Count > 0) { con = stack.Pop(); lhs = GetRange(con.Lhs); rhs = Eval(con.Rhs); con.IsQueued = false; switch (con.Op) { case CardConstraint.OpKind.LEq: if ((cd = Cardinality.Min(lhs.Upper, rhs.Upper)) != lhs.Upper) { if (cd < lhs.Lower) { return(false); } UpdateRange(con.Lhs, lhs = new CardRange(lhs.Lower, cd)); EnqueueConstraints(con.Lhs, stack); } if (rhs.Lower < lhs.Lower) { foreach (var rhsvar in con.RhsVars) { rhs = GetRange(rhsvar); if (Eval(con.Rhs, rhsvar, rhs.Lower, false) >= lhs.Lower) { continue; } intr = new CardRange(rhs.Lower, Cardinality.Min(lhs.Lower, rhs.Upper)); while (intr.Lower + 1 < intr.Upper) { intrp = intr.Bisect(true); if (Eval(con.Rhs, rhsvar, intrp.Lower, false) >= lhs.Lower) { intr = new CardRange(intr.Lower, intrp.Lower); } else { intr = new CardRange(intrp.Lower, intr.Upper); } } if (Eval(con.Rhs, rhsvar, intr.Lower, false) >= lhs.Lower) { if (intr.Lower > rhs.Lower) { UpdateRange(rhsvar, new CardRange(intr.Lower, rhs.Upper)); EnqueueConstraints(rhsvar, stack); continue; } } else if (Eval(con.Rhs, rhsvar, intr.Upper, false) >= lhs.Lower) { if (intr.Upper > rhs.Lower) { UpdateRange(rhsvar, new CardRange(intr.Upper, rhs.Upper)); EnqueueConstraints(rhsvar, stack); continue; } } else { return(false); } } } break; case CardConstraint.OpKind.GEq: if ((cd = Cardinality.Max(lhs.Lower, rhs.Lower)) != lhs.Lower) { if (cd > lhs.Upper) { return(false); } UpdateRange(con.Lhs, lhs = new CardRange(cd, lhs.Upper)); EnqueueConstraints(con.Lhs, stack); } if (rhs.Upper > lhs.Upper) { foreach (var rhsvar in con.RhsVars) { rhs = GetRange(rhsvar); if (Eval(con.Rhs, rhsvar, rhs.Upper, true) <= lhs.Upper) { continue; } intr = new CardRange(rhs.Lower, Cardinality.Min(rhs.Upper, lhs.Upper)); while (intr.Lower + 1 < intr.Upper) { intrp = intr.Bisect(false); if (Eval(con.Rhs, rhsvar, intrp.Upper, true) <= lhs.Upper) { intr = new CardRange(intrp.Upper, intr.Upper); } else { intr = new CardRange(intr.Lower, intrp.Upper); } } if (Eval(con.Rhs, rhsvar, intr.Upper, true) <= lhs.Upper) { if (intr.Upper < rhs.Upper) { UpdateRange(rhsvar, new CardRange(rhs.Lower, intr.Upper)); EnqueueConstraints(rhsvar, stack); continue; } } else if (Eval(con.Rhs, rhsvar, intr.Lower, true) <= lhs.Upper) { if (intr.Lower < rhs.Upper) { UpdateRange(rhsvar, new CardRange(rhs.Lower, intr.Lower)); EnqueueConstraints(rhsvar, stack); continue; } } else { return(false); } } } break; default: throw new NotImplementedException(); } } return(true); }
public static Cardinality Max(Cardinality v1, Cardinality v2) { return(v1 >= v2 ? v1 : v2); }
public CardCnst(Cardinality value) { Value = value; }
public static Cardinality Min(Cardinality v1, Cardinality v2) { return(v1 <= v2 ? v1 : v2); }