package LBJ2.infer;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Represents the disjunction of two propositional constraints.
*
* @author Nick Rizzolo
**/
public class PropositionalDisjunction extends PropositionalNAryConstraint
{
/** Default constructor. */
private PropositionalDisjunction() { }
/**
* If either of the arguments is itself a
* <code>PropositionalDisjunction</code>, its contents are flattened into
* this <code>PropositionalDisjunction</code>.
*
* @param c1 One constraint to disjunct.
* @param c2 Another constraint to disjunct.
**/
public PropositionalDisjunction(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 true;
return false;
}
/**
* 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() {
PropositionalDisjunction result = new PropositionalDisjunction();
for (Iterator I = children.iterator(); I.hasNext(); )
result.add(((PropositionalConstraint) I.next()).simplify());
if (result.children.contains(PropositionalConstant.True))
return PropositionalConstant.True;
result.children.remove(PropositionalConstant.False);
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.True;
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(j));
if (terms[j].moreGeneralThan(terms[i])) toRemove.add(new Integer(i));
}
for (Iterator I = toRemove.iterator(); I.hasNext(); )
result.children.remove(terms[((Integer) I.next()).intValue()]);
*/
if (result.children.size() == 0) return PropositionalConstant.False;
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();
PropositionalConjunction result =
new PropositionalConjunction(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() {
PropositionalConstraint c = factor();
if (!(c instanceof PropositionalDisjunction)) return c.CNF();
PropositionalDisjunction simplified = (PropositionalDisjunction) c;
PropositionalDisjunction childrenCNF = new PropositionalDisjunction();
for (Iterator I = simplified.children.iterator(); I.hasNext(); )
childrenCNF.add(((PropositionalConstraint) I.next()).CNF());
if (childrenCNF.children.size() == 1)
return (PropositionalConstraint) childrenCNF.getChildren()[0];
PropositionalConstraint[][] children =
new PropositionalConstraint[childrenCNF.children.size()][];
int i = 0;
boolean foundConjunction = false;
for (Iterator I = childrenCNF.children.iterator(); I.hasNext(); ++i) {
PropositionalConstraint parent = (PropositionalConstraint) I.next();
if (parent instanceof PropositionalConjunction) {
children[i] = (PropositionalConstraint[]) parent.getChildren();
foundConjunction = true;
}
else {
children[i] = new PropositionalConstraint[1];
children[i][0] = parent;
}
}
if (!foundConjunction) return childrenCNF;
int[] indexes = new int[children.length];
PropositionalConstraint result =
new PropositionalDisjunction(children[0][0], children[1][0]);
for (i = 2; i < children.length; ++i)
result = new PropositionalDisjunction(result, children[i][0]);
while (increment(children, indexes)) {
PropositionalConstraint combination =
new PropositionalDisjunction(children[0][indexes[0]],
children[1][indexes[1]]);
for (i = 2; i < children.length; ++i)
combination =
new PropositionalDisjunction(combination, children[i][indexes[i]]);
result = new PropositionalConjunction(result, combination);
}
return result;
}
/**
* Utility method for iterating through all combinations of constraint
* children.
*
* @param c Each element of this array is an array of children, exactly
* one child of which appears in each combination.
* @param I The indexes of the children in the current combination.
* @return <code>true</code> iff <code>I</code> contains valid indexes for
* a new combination; <code>false</code> iff if there are no more
* combinations.
**/
public static boolean increment(PropositionalConstraint[][] c, int[] I) {
int i = 0;
while (i < c.length && ++I[i] == c[i].length) ++i;
if (i == c.length) return false;
for (--i; i >= 0; --i) I[i] = 0;
return true;
}
/**
* 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() {
PropositionalDisjunction result = new PropositionalDisjunction();
for (Iterator I = children.iterator(); I.hasNext(); )
result.add(((PropositionalConstraint) I.next()).DNF());
return result.simplify();
}
/**
* 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 false;
}
/**
* 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 false;
}
/**
* 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 false;
}
/**
* 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 c.size() > size() && c.containsAll(this);
}
/**
* 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 false;
}
/**
* 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 false;
}
/**
* 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 PropositionalDisjunction) {
PropositionalConstraint[] terms =
(PropositionalConstraint[]) c.getChildren();
for (int i = 0; i < terms.length; ++i) add(terms[i]);
}
else children.add(c);
}
/**
* Factoring a disjunction is the opposite of distributing a conjunction
* over a disjunction.
*
* @return A constraint that represents a factoring of this disjunction.
**/
public PropositionalConstraint factor() {
PropositionalConstraint c = simplify();
if (!(c instanceof PropositionalDisjunction)) return c;
PropositionalDisjunction simplified = (PropositionalDisjunction) c;
PropositionalConstraint[] best = new PropositionalConstraint[0];
while (best != null) {
int bestConjunction = -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 PropositionalConjunction) {
if (o2 instanceof PropositionalConjunction) return 0;
return -1;
}
if (o2 instanceof PropositionalConjunction) return 1;
return 0;
}
});
for (int i = 0;
i < children.length - 1
&& children[i] instanceof PropositionalConjunction;
++i)
for (int j = i + 1; j < children.length; ++j) {
PropositionalConstraint[] current =
((PropositionalConjunction) children[i]).intersect(children[j]);
if (current != null
&& (best == null || current.length > best.length)) {
best = current;
bestConjunction = i;
bestOther = j;
}
}
if (best != null) {
PropositionalConstraint toAdd = null;
if (best.length == 1) toAdd = best[0];
else {
toAdd = new PropositionalConjunction(best[0], best[1]);
for (int i = 2; i < best.length; ++i)
toAdd = new PropositionalConjunction(toAdd, best[i]);
}
if (children[bestOther] instanceof PropositionalConjunction) {
PropositionalConstraint conjunct1 =
((PropositionalConjunction) children[bestConjunction])
.subtract(best);
PropositionalConstraint conjunct2 =
((PropositionalConjunction) children[bestOther]).subtract(best);
toAdd =
new PropositionalConjunction(
toAdd,
new PropositionalDisjunction(conjunct1, conjunct2))
.simplify();
}
simplified.children.remove(children[bestConjunction]);
simplified.children.remove(children[bestOther]);
simplified.add(toAdd);
}
}
if (simplified.children.size() == 1)
return (PropositionalConstraint) simplified.getChildren()[0];
return simplified;
}
/**
* The intersection of two disjunctions is the set of all terms that are
* common to both disjunctions; the intersection of a disjunction and some
* other constraint <i>c</i> is <i>c</i> if <i>c</i> is contained in the
* disjunction 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 PropositionalDisjunction)) {
if (children.contains(c)) return new PropositionalConstraint[]{ c };
return null;
}
PropositionalDisjunction disjunction = (PropositionalDisjunction) c;
LinkedList result = new LinkedList();
for (Iterator I = children.iterator(); I.hasNext(); ) {
Object next = I.next();
if (disjunction.children.contains(next)) result.add(next);
}
if (result.size() == 0) return null;
return (PropositionalConstraint[])
result.toArray(new PropositionalConstraint[result.size()]);
}
/**
* Subtraction from a disjunction 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) {
PropositionalDisjunction clone = (PropositionalDisjunction) clone();
for (int i = 0; i < terms.length; ++i) clone.children.remove(terms[i]);
if (clone.children.size() == 0) return new PropositionalConstant(false);
if (clone.children.size() == 1)
return (PropositionalConstraint) clone.getChildren()[0];
return clone;
}
/**
* Distributes the given conjunction over this disjunction.
*
* @return A simplified constraint representing the distribution of the
* given conjunction over this disjunction.
**/
public PropositionalConstraint distribute(PropositionalConjunction c) {
PropositionalConstraint[] array =
(PropositionalConstraint[])
children.toArray(new PropositionalConstraint[children.size()]);
for (int i = 0; i < array.length; ++i) {
PropositionalConjunction clone = (PropositionalConjunction) c.clone();
clone.add(array[i]);
array[i] = clone;
}
if (array.length == 1) return array[0].simplify();
PropositionalDisjunction result =
new PropositionalDisjunction(array[0], array[1]);
for (int i = 2; i < array.length; ++i) result.add(array[i]);
return result.simplify();
}
/**
* Determines whether this disjunction contains all of the terms that the
* given disjunction contains.
*
* @param d The given disjunction.
* @return <code>true</code> iff this disjunction contains all of the terms
* that the given disjunction contains.
**/
public boolean containsAll(PropositionalDisjunction d) {
return children.containsAll(d.children);
}
/**
* The hash code of a <code>PropositionalDisjunction</code> is the sum of
* the hash codes of its children.
*
* @return The hash code for this <code>PropositionalDisjunction</code>.
**/
public int hashCode() {
int result = 0;
for (Iterator I = children.iterator(); I.hasNext(); )
result += I.next().hashCode();
return result;
}
/**
* Two <code>PropositionalDisjunction</code>s are equivalent when they are
* topologically equivalent, respecting the associativity and commutivity
* of disjunction.
*
* @return <code>true</code> iff the argument is an equivalent
* <code>PropositionalDisjunction</code>.
**/
public boolean equals(Object o) {
if (!(o instanceof PropositionalDisjunction)) return false;
PropositionalDisjunction d = (PropositionalDisjunction) o;
return children.equals(d.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(")");
}
}