package edu.stanford.nlp.naturalli;
import edu.stanford.nlp.util.Pair;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* <p>
* A class intended to be attached to a lexical item, determining what mutations are valid on it while
* maintaining valid Natural Logic inference.
* </p>
*
* @author Gabor Angeli
*/
@SuppressWarnings("UnusedDeclaration")
public class Polarity {
/**
* The default (very permissive) polarity.
*/
public static final Polarity DEFAULT = new Polarity(Collections.singletonList(Pair.makePair(Monotonicity.MONOTONE, MonotonicityType.BOTH)));
/** The projection function, as a table from a relations fixed index to the projected fixed index */
private final byte[] projectionFunction = new byte[7];
/** Create a polarity from a list of operators in scope */
protected Polarity(List<Pair<Monotonicity, MonotonicityType>> operatorsInNarrowingScopeOrder) {
if (operatorsInNarrowingScopeOrder.isEmpty()) {
for (byte i = 0; i < projectionFunction.length; ++i) {
projectionFunction[i] = i;
}
} else {
for (int rel = 0; rel < 7; ++rel) {
NaturalLogicRelation relation = NaturalLogicRelation.byFixedIndex(rel);
for (int op = operatorsInNarrowingScopeOrder.size() - 1; op >= 0; --op) {
relation = project(relation, operatorsInNarrowingScopeOrder.get(op).first, operatorsInNarrowingScopeOrder.get(op).second);
}
projectionFunction[rel] = (byte) relation.fixedIndex;
}
}
}
/**
* Create a polarity item by directly copying the projection function from {@link edu.stanford.nlp.naturalli.NaturalLogicRelation}s to
* their projected relation.
*/
public Polarity(byte[] projectionFunction) {
if (projectionFunction.length != 7) {
throw new IllegalArgumentException("Invalid projection function: " + Arrays.toString(projectionFunction));
}
for (int i = 0; i < 7; ++i) {
if (projectionFunction[i] < 0 || projectionFunction[i] > 6) {
throw new IllegalArgumentException("Invalid projection function: " + Arrays.toString(projectionFunction));
}
}
System.arraycopy(projectionFunction, 0, this.projectionFunction, 0, 7);
}
/**
* Encode the projection table in painful detail.
*
* @param input The input natural logic relation to project up through the operator.
* @param mono The monotonicity of the operator we are projecting through.
* @param type The monotonicity type of the operator we are projecting through.
*
* @return The projected relation, once passed through an operator with the given specifications.
*/
private NaturalLogicRelation project(NaturalLogicRelation input, Monotonicity mono, MonotonicityType type) {
switch (input) {
case EQUIVALENT:
return NaturalLogicRelation.EQUIVALENT;
case FORWARD_ENTAILMENT:
switch (mono) {
case MONOTONE:
return NaturalLogicRelation.FORWARD_ENTAILMENT;
case ANTITONE:
return NaturalLogicRelation.REVERSE_ENTAILMENT;
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
case REVERSE_ENTAILMENT:
switch (mono) {
case MONOTONE:
return NaturalLogicRelation.REVERSE_ENTAILMENT;
case ANTITONE:
return NaturalLogicRelation.FORWARD_ENTAILMENT;
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
case NEGATION:
switch (type) {
case NONE:
return NaturalLogicRelation.INDEPENDENCE;
case ADDITIVE:
switch (mono) {
case MONOTONE:
return NaturalLogicRelation.COVER;
case ANTITONE:
return NaturalLogicRelation.ALTERNATION;
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
case MULTIPLICATIVE:
switch (mono) {
case MONOTONE:
return NaturalLogicRelation.ALTERNATION;
case ANTITONE:
return NaturalLogicRelation.COVER;
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
break;
case BOTH:
return NaturalLogicRelation.NEGATION;
}
break;
case ALTERNATION:
switch (mono) {
case MONOTONE:
switch (type) {
case NONE:
case ADDITIVE:
return NaturalLogicRelation.INDEPENDENCE;
case MULTIPLICATIVE:
case BOTH:
return NaturalLogicRelation.ALTERNATION;
}
case ANTITONE:
switch (type) {
case NONE:
case ADDITIVE:
return NaturalLogicRelation.INDEPENDENCE;
case MULTIPLICATIVE:
case BOTH:
return NaturalLogicRelation.COVER;
}
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
case COVER:
switch (mono) {
case MONOTONE:
switch (type) {
case NONE:
case MULTIPLICATIVE:
return NaturalLogicRelation.INDEPENDENCE;
case ADDITIVE:
case BOTH:
return NaturalLogicRelation.COVER;
}
case ANTITONE:
switch (type) {
case NONE:
case MULTIPLICATIVE:
return NaturalLogicRelation.INDEPENDENCE;
case ADDITIVE:
case BOTH:
return NaturalLogicRelation.ALTERNATION;
}
case NONMONOTONE:
case INVALID:
return NaturalLogicRelation.INDEPENDENCE;
}
case INDEPENDENCE:
return NaturalLogicRelation.INDEPENDENCE;
}
throw new IllegalStateException("[should not happen!] Projection table is incomplete for " + mono + " : " + type + " on relation " + input);
}
/**
* Project the given natural logic lexical relation on this word. So, for example, if we want to go up the
* Hypernymy hierarchy ({@link edu.stanford.nlp.naturalli.NaturalLogicRelation#FORWARD_ENTAILMENT}) on this word,
* then this function will tell you what relation holds between the new mutated fact and this fact.
*
* @param lexicalRelation The lexical relation we are applying to this word.
* @return The relation between the mutated sentence and the original sentence.
*/
public NaturalLogicRelation projectLexicalRelation(NaturalLogicRelation lexicalRelation) {
return NaturalLogicRelation.byFixedIndex( projectionFunction[lexicalRelation.fixedIndex] );
}
/**
* If true, applying this lexical relation to this word creates a sentence which is entailed by the original sentence,
* Note that both this, and {@link Polarity#negatesTruth(NaturalLogicRelation)} can be false. If this is the case, then
* natural logic can neither verify nor disprove this mutation.
*/
public boolean maintainsTruth(NaturalLogicRelation lexicalRelation) {
return projectLexicalRelation(lexicalRelation).maintainsTruth;
}
/**
* If true, applying this lexical relation to this word creates a sentence which is negated by the original sentence
* Note that both this, and {@link Polarity#maintainsTruth(NaturalLogicRelation)}} can be false. If this is the case, then
* natural logic can neither verify nor disprove this mutation.
*/
public boolean negatesTruth(NaturalLogicRelation lexicalRelation) {
return projectLexicalRelation(lexicalRelation).negatesTruth;
}
/**
* @see Polarity#maintainsTruth(NaturalLogicRelation)
*/
public boolean maintainsFalsehood(NaturalLogicRelation lexicalRelation) {
return projectLexicalRelation(lexicalRelation).maintainsFalsehood;
}
/**
* @see Polarity#negatesTruth(NaturalLogicRelation)
*/
public boolean negatesFalsehood(NaturalLogicRelation lexicalRelation) {
return projectLexicalRelation(lexicalRelation).negatesFalsehood;
}
/**
* Ignoring exclusion, determine if this word has upward polarity.
*/
public boolean isUpwards() {
return projectLexicalRelation(NaturalLogicRelation.FORWARD_ENTAILMENT) == NaturalLogicRelation.FORWARD_ENTAILMENT &&
projectLexicalRelation(NaturalLogicRelation.REVERSE_ENTAILMENT) == NaturalLogicRelation.REVERSE_ENTAILMENT;
}
/**
* Ignoring exclusion, determine if this word has downward polarity.
*/
public boolean isDownwards() {
return projectLexicalRelation(NaturalLogicRelation.FORWARD_ENTAILMENT) == NaturalLogicRelation.REVERSE_ENTAILMENT &&
projectLexicalRelation(NaturalLogicRelation.REVERSE_ENTAILMENT) == NaturalLogicRelation.FORWARD_ENTAILMENT;
}
@Override
public String toString() {
if (isUpwards()) {
return "up";
} else if (isDownwards()) {
return "down";
} else {
return "flat";
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Polarity)) return false;
Polarity polarity = (Polarity) o;
return Arrays.equals(projectionFunction, polarity.projectionFunction);
}
@Override
public int hashCode() {
return projectionFunction != null ? Arrays.hashCode(projectionFunction) : 0;
}
}