package LBJ2.infer; import java.util.*; /** * Represents the conjunction of two propositional constraints. * * @author Nick Rizzolo **/ public class PropositionalConjunction extends PropositionalNAryConstraint { /** Default constructor. */ private PropositionalConjunction() { } /** * If either of the arguments is itself a * <code>PropositionalConjunction</code>, its contents are flattened into * this <code>PropositionalConjunction</code>. * * @param c1 One constraint to disjunct. * @param c2 Another constraint to disjunct. **/ public PropositionalConjunction(PropositionalConstraint c1, PropositionalConstraint c2) { add(c1); add(c2); } /** Determines whether the constraint is satisfied. */ public boolean evaluate() { for (Iterator I = children.iterator(); I.hasNext(); ) if (!((PropositionalConstraint) I.next()).evaluate()) return false; return true; } /** * Produces a new, logically simplified version of this constraint, * preserving variable consolidation. * * @see Constraint#consolidateVariables(java.util.AbstractMap) * @return A logically simplified version of this constraint. **/ public PropositionalConstraint simplify() { return simplify(false); } /** * Same as <code>simplify()</code>, except this method gives the caller the * ability to optionally leave double implications that are immediate * children of this conjunction in tact. * * @param d <code>true</code> iff double implications that are immediate * children of this conjunction are to be left in tact. * @return A logically simplified version of this constraint. **/ public PropositionalConstraint simplify(boolean d) { PropositionalConjunction result = new PropositionalConjunction(); if (d) { for (Iterator I = children.iterator(); I.hasNext(); ) { PropositionalConstraint c = (PropositionalConstraint) I.next(); if (c instanceof PropositionalDoubleImplication) { PropositionalDoubleImplication di = (PropositionalDoubleImplication) c; di.left = di.left.simplify(); di.right = di.right.simplify(); if (di.left.equals(di.right)) c = PropositionalConstant.True; else if (di.left.equals(PropositionalConstant.False)) c = di.right.negate().simplify(); else if (di.left.equals(PropositionalConstant.True)) c = di.right; else if (di.right.equals(PropositionalConstant.False)) c = di.left.negate().simplify(); else if (di.right.equals(PropositionalConstant.True)) c = di.left; /* else if (di.right instanceof PropositionalNegation && di.left.equals(di.right.getChildren()[0]) || di.left instanceof PropositionalNegation && di.right.equals(di.left.getChildren()[0])) c = PropositionalConstant.False; */ } else c = c.simplify(); result.add(c); } } else { for (Iterator I = children.iterator(); I.hasNext(); ) result.add(((PropositionalConstraint) I.next()).simplify()); } if (result.children.contains(PropositionalConstant.False)) return PropositionalConstant.False; result.children.remove(PropositionalConstant.True); if (result.children.size() == 1) return (PropositionalConstraint) result.children.iterator().next(); /* HashSet positive = new HashSet(); HashSet negative = new HashSet(); for (Iterator I = result.children.iterator(); I.hasNext(); ) { Object next = I.next(); if (next instanceof PropositionalNegation) negative.add(((PropositionalConstraint) next).getChildren()[0]); else positive.add(next); } for (Iterator I = positive.iterator(); I.hasNext(); ) if (negative.contains(I.next())) return PropositionalConstant.False; PropositionalConstraint[] terms = (PropositionalConstraint[]) getChildren(); HashSet toRemove = new HashSet(); for (int i = 0; i < terms.length - 1; ++i) for (int j = i + 1; j < terms.length; ++j) { if (terms[i].moreGeneralThan(terms[j])) toRemove.add(new Integer(i)); if (terms[j].moreGeneralThan(terms[i])) toRemove.add(new Integer(j)); } for (Iterator I = toRemove.iterator(); I.hasNext(); ) result.children.remove(terms[((Integer) I.next()).intValue()]); */ if (result.children.size() == 0) return PropositionalConstant.True; return result; } /** * Uses DeMorgan's law to compute the negation of this constraint by * distributing that negation to each child. * * @return A simplified constraint representing the negation of this * constraint. **/ public PropositionalConstraint negate() { if (children.size() == 1) return ((PropositionalConstraint) children.iterator().next()).negate(); PropositionalConstraint[] array = (PropositionalConstraint[]) children.toArray(new PropositionalConstraint[children.size()]); for (int i = 0; i < array.length; ++i) array[i] = array[i].negate(); PropositionalDisjunction result = new PropositionalDisjunction(array[0], array[1]); for (int i = 2; i < array.length; ++i) result.add(array[i]); return result; } /** * Produces a new, logically simplified version of this constraint in * conjunctive normal form (CNF). * * @return The conjunctive normal form of this constraint. **/ public PropositionalConstraint CNF() { PropositionalConjunction result = new PropositionalConjunction(); for (Iterator I = children.iterator(); I.hasNext(); ) result.add(((PropositionalConstraint) I.next()).CNF()); return result.simplify(); } /** * Produces a new, logically simplified version of this constraint in * disjunctive normal form (DNF). * * @return The disjunctive normal form of this constraint. **/ public PropositionalConstraint DNF() { PropositionalConstraint c = factor(); if (!(c instanceof PropositionalConjunction)) return c.DNF(); PropositionalConjunction simplified = (PropositionalConjunction) c; PropositionalConjunction childrenDNF = new PropositionalConjunction(); for (Iterator I = simplified.children.iterator(); I.hasNext(); ) childrenDNF.add(((PropositionalConstraint) I.next()).DNF()); if (childrenDNF.children.size() == 1) return (PropositionalConstraint) childrenDNF.getChildren()[0]; PropositionalConstraint[][] children = new PropositionalConstraint[childrenDNF.children.size()][]; int i = 0; boolean foundDisjunction = false; for (Iterator I = childrenDNF.children.iterator(); I.hasNext(); ++i) { PropositionalConstraint parent = (PropositionalConstraint) I.next(); if (parent instanceof PropositionalDisjunction) { children[i] = (PropositionalConstraint[]) parent.getChildren(); foundDisjunction = true; } else { children[i] = new PropositionalConstraint[1]; children[i][0] = parent; } } if (!foundDisjunction) return childrenDNF; int[] indexes = new int[children.length]; PropositionalConstraint result = new PropositionalConjunction(children[0][0], children[1][0]); for (i = 2; i < children.length; ++i) result = new PropositionalConjunction(result, children[i][0]); while (PropositionalDisjunction.increment(children, indexes)) { PropositionalConstraint combination = new PropositionalConjunction(children[0][indexes[0]], children[1][indexes[1]]); for (i = 2; i < children.length; ++i) combination = new PropositionalConjunction(combination, children[i][indexes[i]]); result = new PropositionalDisjunction(result, combination); } return result; } /** * Compares topology to determine if this constraint is more general than * the given constraint; <i>note: this method is not required to be correct * when it answers <code>false</code></i>. * * @param c The given constraint. * @return <code>true</code> if a topological analysis determined that this * constraint is more general than the given constraint. **/ public boolean moreGeneralThan(PropositionalConstraint c) { return c.moreSpecificThan(this); } /** * Compares topology to determine if this constraint is more specific than * the given implication; <i>note: this method is not required to be * correct when it answers <code>false</code></i>. * * @param c The given implication. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given implication. **/ public boolean moreSpecificThan(PropositionalImplication c) { return size() > 1 && contains(c); } /** * Compares topology to determine if this constraint is more specific than * the given double implication; <i>note: this method is not required to be * correct when it answers <code>false</code></i>. * * @param c The given double implication. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given double implication. **/ public boolean moreSpecificThan(PropositionalDoubleImplication c) { return size() > 1 && contains(c); } /** * Compares topology to determine if this constraint is more specific than * the given conjunction; <i>note: this method is not required to be * correct when it answers <code>false</code></i>. * * @param c The given conjunction. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given conjunction. **/ public boolean moreSpecificThan(PropositionalConjunction c) { return size() > c.size() && containsAll(c); } /** * Compares topology to determine if this constraint is more specific than * the given disjunction; <i>note: this method is not required to be * correct when it answers <code>false</code></i>. * * @param c The given disjunction. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given disjunction. **/ public boolean moreSpecificThan(PropositionalDisjunction c) { return size() > 1 && contains(c); } /** * Compares topology to determine if this constraint is more specific than * the given at-least; <i>note: this method is not required to be correct * when it answers <code>false</code></i>. * * @param c The given at-least. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given disjunction. **/ public boolean moreSpecificThan(PropositionalAtLeast c) { return false; } /** * Compares topology to determine if this constraint is more specific than * the given negation; <i>note: this method is not required to be correct * when it answers <code>false</code></i>. * * @param c The given negation. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given negation. **/ public boolean moreSpecificThan(PropositionalNegation c) { return size() > 1 && contains(c); } /** * Compares topology to determine if this constraint is more specific than * the given variable; <i>note: this method is not required to be correct * when it answers <code>false</code></i>. * * @param c The given variable. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given variable. **/ public boolean moreSpecificThan(PropositionalVariable c) { return size() > 1 && contains(c); } /** * Compares topology to determine if this constraint is more specific than * the given constant; <i>note: this method is not required to be correct * when it answers <code>false</code></i>. * * @param c The given constant. * @return <code>true</code> if a topological analysis determined that this * constraint is more specific than the given constant. **/ public boolean moreSpecificThan(PropositionalConstant c) { return c.evaluate(); } /** * If the given constraint has the same type as this constraint, its terms * are merged into this constraint; otherwise, it is added as a new term. * * @param c The constraint to add. **/ public void add(PropositionalConstraint c) { if (c instanceof PropositionalConjunction) { PropositionalConstraint[] terms = (PropositionalConstraint[]) c.getChildren(); for (int i = 0; i < terms.length; ++i) add(terms[i]); } else children.add(c); } /** * Factoring a conjunction is the opposite of distributing a disjunction * over a conjunction. * * @return A constraint that represents a factoring of this conjunction. **/ public PropositionalConstraint factor() { PropositionalConstraint c = simplify(); if (!(c instanceof PropositionalConjunction)) return c; PropositionalConjunction simplified = (PropositionalConjunction) c; PropositionalConstraint[] best = new PropositionalConstraint[0]; while (best != null) { int bestDisjunction = -1; int bestOther = -1; best = null; PropositionalConstraint[] children = (PropositionalConstraint[]) simplified.getChildren(); Arrays.sort(children, new Comparator() { public int compare(Object o1, Object o2) { if (o1 instanceof PropositionalDisjunction) { if (o2 instanceof PropositionalDisjunction) return 0; return -1; } if (o2 instanceof PropositionalDisjunction) return 1; return 0; } }); for (int i = 0; i < children.length - 1 && children[i] instanceof PropositionalDisjunction; ++i) for (int j = i + 1; j < children.length; ++j) { PropositionalConstraint[] current = ((PropositionalDisjunction) children[i]).intersect(children[j]); if (current != null && (best == null || current.length > best.length)) { best = current; bestDisjunction = i; bestOther = j; } } if (best != null) { PropositionalConstraint toAdd = null; if (best.length == 1) toAdd = best[0]; else { toAdd = new PropositionalDisjunction(best[0], best[1]); for (int i = 2; i < best.length; ++i) toAdd = new PropositionalDisjunction(toAdd, best[i]); } if (children[bestOther] instanceof PropositionalDisjunction) { PropositionalConstraint disjunct1 = ((PropositionalDisjunction) children[bestDisjunction]) .subtract(best); PropositionalConstraint disjunct2 = ((PropositionalDisjunction) children[bestOther]).subtract(best); toAdd = new PropositionalDisjunction( toAdd, new PropositionalConjunction(disjunct1, disjunct2)) .simplify(); } simplified.children.remove(children[bestDisjunction]); simplified.children.remove(children[bestOther]); simplified.add(toAdd); } } if (simplified.children.size() == 1) return (PropositionalConstraint) simplified.getChildren()[0]; return simplified; } /** * The intersection of two conjunctions is the set of all terms that are * common to both conjunctions; the intersection of a conjunction and some * other constraint <i>c</i> is <i>c</i> if <i>c</i> is contained in the * conjunction and the empty set otherwise. * * @param c The constraint to intersect with. * @return The set of common terms in array form or <code>null</code> if * there are none. **/ public PropositionalConstraint[] intersect(PropositionalConstraint c) { if (!(c instanceof PropositionalConjunction)) { if (children.contains(c)) return new PropositionalConstraint[]{ c }; return null; } PropositionalConjunction conjunction = (PropositionalConjunction) c; LinkedList result = new LinkedList(); for (Iterator I = children.iterator(); I.hasNext(); ) { Object next = I.next(); if (conjunction.children.contains(next)) result.add(next); } if (result.size() == 0) return null; return (PropositionalConstraint[]) result.toArray(new PropositionalConstraint[result.size()]); } /** * Subtraction from a conjunction simply removes all of the specified * terms from it; this method returns a new constraint representing the * subtraction. * * @param terms The terms to remove. * @return A new representation of this n-ary constraint with the specified * terms removed. **/ public PropositionalConstraint subtract(PropositionalConstraint[] terms) { PropositionalConjunction clone = (PropositionalConjunction) clone(); for (int i = 0; i < terms.length; ++i) clone.children.remove(terms[i]); if (clone.children.size() == 0) return new PropositionalConstant(true); if (clone.children.size() == 1) return (PropositionalConstraint) clone.getChildren()[0]; return clone; } /** * Distributes the given disjunction over this conjunction. * * @return A simplified constraint representing the distribution of the * given disjunction over this conjunction. **/ public PropositionalConstraint distribute(PropositionalDisjunction d) { PropositionalConstraint[] array = (PropositionalConstraint[]) children.toArray(new PropositionalConstraint[children.size()]); for (int i = 0; i < array.length; ++i) { PropositionalDisjunction clone = (PropositionalDisjunction) d.clone(); clone.add(array[i]); array[i] = clone; } if (array.length == 1) return array[0].simplify(); PropositionalConjunction result = new PropositionalConjunction(array[0], array[1]); for (int i = 2; i < array.length; ++i) result.add(array[i]); return result.simplify(); } /** * Determines whether this conjunction contains all of the terms that the * given conjunction contains. * * @param c The given conjunction. * @return <code>true</code> iff this conjunction contains all of the terms * that the given conjunction contains. **/ public boolean containsAll(PropositionalConjunction c) { return children.containsAll(c.children); } /** * The hash code of a <code>PropositionalConjunction</code> is the sum of * the hash codes of its children plus one. * * @return The hash code for this <code>PropositionalConjunction</code>. **/ public int hashCode() { int result = 1; for (Iterator I = children.iterator(); I.hasNext(); ) result += I.next().hashCode(); return result; } /** * Two <code>PropositionalConjunction</code>s are equivalent when they are * topologically equivalent, respecting the associativity and commutivity * of conjunction. * * @return <code>true</code> iff the argument is an equivalent * <code>PropositionalConjunction</code>. **/ public boolean equals(Object o) { if (!(o instanceof PropositionalConjunction)) return false; PropositionalConjunction c = (PropositionalConjunction) o; return children.equals(c.children); } /** * Calls the appropriate <code>visit(·)</code> method of the given * <code>Inference</code> for this <code>Constraint</code>, as per the * visitor pattern. * * @param infer The inference visiting this constraint. **/ public void runVisit(Inference infer) { infer.visit(this); } /** * Creates a string respresentation of this constraint using the string * representations of the objects involved. * * @param buffer The output of this method will be appended to this buffer. **/ public void write(StringBuffer buffer) { buffer.append("("); PropositionalConstraint[] children = (PropositionalConstraint[]) getChildren(); children[0].write(buffer); for (int i = 1; i < children.length; ++i) { buffer.append(" /\\ "); children[i].write(buffer); } buffer.append(")"); } }