aima.core.logic.propositional.kb.data.Clause Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aima-core Show documentation
Show all versions of aima-core Show documentation
AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.
The newest version!
package aima.core.logic.propositional.kb.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import aima.core.logic.propositional.parsing.ast.PropositionSymbol;
import aima.core.util.SetOps;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): page 253.
*
* A Clause: A disjunction of literals. Here we view a Clause as a set of
* literals. This respects the restriction, under resolution, that a resulting
* clause should contain only 1 copy of a resulting literal. In addition,
* clauses, as implemented, are immutable.
*
*
* @author Ciaran O'Reilly
*
*/
public class Clause {
public static final Clause EMPTY = new Clause();
//
private Set literals = new LinkedHashSet();
//
private Set cachedPositiveSymbols = new LinkedHashSet();
private Set cachedNegativeSymbols = new LinkedHashSet();
private Set cachedSymbols = new LinkedHashSet();
//
private Boolean cachedIsTautologyResult = null;
private String cachedStringRep = null;
private int cachedHashCode = -1;
/**
* Default constructor - i.e. the empty clause, which is 'False'.
*/
public Clause() {
// i.e. the empty clause
this(new ArrayList());
}
/**
* Construct a clause from the given literals. Note: literals the are always
* 'False' (i.e. False or ~True) are not added to the instantiated clause.
*
* @param literals
* the literals to be added to the clause.
*/
public Clause(Literal... literals) {
this(Arrays.asList(literals));
}
/**
* Construct a clause from the given literals. Note: literals the are always
* 'False' (i.e. False or ~True) are not added to the instantiated clause.
*
* @param literals
*/
public Clause(Collection literals) {
for (Literal l : literals) {
if (l.isAlwaysFalse()) {
// Don't add literals of the form
// False | ~True
continue;
}
if (this.literals.add(l)) {
// Only add to caches if not already added
if (l.isPositiveLiteral()) {
this.cachedPositiveSymbols.add(l.getAtomicSentence());
} else {
this.cachedNegativeSymbols.add(l.getAtomicSentence());
}
}
}
cachedSymbols.addAll(cachedPositiveSymbols);
cachedSymbols.addAll(cachedNegativeSymbols);
// Make immutable
this.literals = Collections.unmodifiableSet(this.literals);
cachedSymbols = Collections.unmodifiableSet(cachedSymbols);
cachedPositiveSymbols = Collections
.unmodifiableSet(cachedPositiveSymbols);
cachedNegativeSymbols = Collections
.unmodifiableSet(cachedNegativeSymbols);
}
/**
* If a clause is empty - a disjunction of no disjuncts - it is equivalent
* to 'False' because a disjunction is true only if at least one of its
* disjuncts is true.
*
* @return true if an empty clause, false otherwise.
*/
public boolean isFalse() {
return isEmpty();
}
/**
*
* @return true if the clause is empty (i.e. 'False'), false otherwise.
*/
public boolean isEmpty() {
return literals.size() == 0;
}
/**
* Determine if a clause is unit, i.e. contains a single literal.
*
* @return true if the clause is unit, false otherwise.
*/
public boolean isUnitClause() {
return literals.size() == 1;
}
/**
* Determine if a definite clause. A definite clause is a disjunction of
* literals of which exactly one is positive. For example, the
* clause (¬L1,1 ∨ ¬Breeze ∨ B1,1) is a
* definite clause, whereas (¬B1,1 ∨ P1,2 ∨
* P2,1) is not.
*
*
* @return true if a definite clause, false otherwise.
*/
public boolean isDefiniteClause() {
return cachedPositiveSymbols.size() == 1;
}
/**
* Determine if an implication definite clause. An implication definite
* clause is disjunction of literals of which exactly 1 is positive and
* there is 1 or more negative literals.
*
* @return true if an implication definite clause, false otherwise.
*/
public boolean isImplicationDefiniteClause() {
return isDefiniteClause() && cachedNegativeSymbols.size() >= 1;
}
/**
* Determine if a Horn clause. A horn clause is a disjunction of literals of
* which at most one is positive.
*
* @return true if a Horn clause, false otherwise.
*/
public boolean isHornClause() {
return !isEmpty() && cachedPositiveSymbols.size() <= 1;
}
/**
* Clauses with no positive literals are called goal clauses.
*
* @return true if a Goal clause, false otherwise.
*/
public boolean isGoalClause() {
return !isEmpty() && cachedPositiveSymbols.size() == 0;
}
/**
* Determine if the clause represents a tautology, of which the following
* are examples:
*
*
* {..., True, ...}
* {..., ~False, ...}
* {..., P, ..., ~P, ...}
*
*
* @return true if the clause represents a tautology, false otherwise.
*/
public boolean isTautology() {
if (cachedIsTautologyResult == null) {
for (Literal l : literals) {
if (l.isAlwaysTrue()) {
// {..., True, ...} is a tautology.
// {..., ~False, ...} is a tautology
cachedIsTautologyResult = true;
}
}
// If we still don't know
if (cachedIsTautologyResult == null) {
if (SetOps.intersection(cachedPositiveSymbols, cachedNegativeSymbols)
.size() > 0) {
// We have:
// P | ~P
// which is always true.
cachedIsTautologyResult = true;
}
else {
cachedIsTautologyResult = false;
}
}
}
return cachedIsTautologyResult;
}
/**
*
* @return the number of literals contained by the clause.
*/
public int getNumberLiterals() {
return literals.size();
}
/**
*
* @return the number of positive literals contained by the clause.
*/
public int getNumberPositiveLiterals() {
return cachedPositiveSymbols.size();
}
/**
*
* @return the number of negative literals contained by the clause.
*/
public int getNumberNegativeLiterals() {
return cachedNegativeSymbols.size();
}
/**
*
* @return the set of literals making up the clause.
*/
public Set getLiterals() {
return literals;
}
/**
*
* @return the set of symbols from the clause's positive and negative literals.
*/
public Set getSymbols() {
return cachedSymbols;
}
/**
*
* @return the set of symbols from the clause's positive literals.
*/
public Set getPositiveSymbols() {
return cachedPositiveSymbols;
}
/**
*
* @return the set of symbols from the clause's negative literals.
*/
public Set getNegativeSymbols() {
return cachedNegativeSymbols;
}
@Override
public String toString() {
if (cachedStringRep == null) {
StringBuilder sb = new StringBuilder();
boolean first = true;
sb.append("{");
for (Literal l : literals) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(l);
}
sb.append("}");
cachedStringRep = sb.toString();
}
return cachedStringRep;
}
@Override
public boolean equals(Object othObj) {
if (null == othObj) {
return false;
}
if (this == othObj) {
return true;
}
if (!(othObj instanceof Clause)) {
return false;
}
Clause othClause = (Clause) othObj;
return othClause.literals.equals(this.literals);
}
@Override
public int hashCode() {
if (cachedHashCode == -1) {
cachedHashCode = literals.hashCode();
}
return cachedHashCode;
}
}