package edu.stanford.nlp.sempre.tables.lambdadcs;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.AggregateFormula.Mode;
import edu.stanford.nlp.sempre.tables.lambdadcs.LambdaDCSException.Type;
import fig.basic.LispTree;
import fig.basic.Pair;
/**
* Implicitly represent a pair list using a single predicate:
* - NORMAL: a relation (fb:people.person.parent) or its reverse
* - COMPARISON: != < > <= >=
* - COLON: (: u) is an empty unary if u is empty; STAR otherwise.
* - EQUAL: identity map
* @author ppasupat
*
*/
public class PredicatePairList implements PairList {
enum PredicateType { NORMAL, COMPARISON, COLON, EQUAL }
protected final PredicateType type;
// predicate fb:people.person.birthdate maps key (date 1961 8 4) to value fb:en.barack_obama
// predicate > maps key 3 to value 7
// predicate : maps any non-empty key to everything (*)
// predicate = maps any key to itself
protected final Value predicate;
protected final String predicateId;
protected final KnowledgeGraph graph;
// ============================================================
// Constructors
// ============================================================
public PredicatePairList(Value predicate, KnowledgeGraph graph) {
this.predicate = predicate;
this.predicateId = (predicate instanceof NameValue) ? ((NameValue) predicate).id : null;
this.graph = graph;
if ("=".equals(predicateId)) {
type = PredicateType.EQUAL;
} else if (CanonicalNames.COLON.equals(predicateId)) {
type = PredicateType.COLON;
} else if (CanonicalNames.COMPARATORS.contains(predicateId)) {
type = PredicateType.COMPARISON;
} else {
type = PredicateType.NORMAL;
assert graph != null;
}
}
public static final PredicatePairList IDENTITY = new PredicatePairList(new NameValue("="), null);
// ============================================================
// Representation
// ============================================================
@Override
public String toString() {
return toLispTree().toString();
}
@Override
public LispTree toLispTree() {
return predicate.toLispTree();
}
@Override
public PairListValue toValue() {
return explicitlyFilterOnKey(InfiniteUnaryDenotation.STAR_UNARY).toValue();
}
// ============================================================
// Getter
// ============================================================
// If needed, the explicit list of pairs are computed and cached
protected ExplicitPairList explicitPairListCache;
protected ExplicitPairList getExplicit() {
if (explicitPairListCache == null)
explicitPairListCache = explicitlyFilterOnKey(InfiniteUnaryDenotation.STAR_UNARY);
return explicitPairListCache;
}
@Override
public UnaryDenotation domain() {
switch (type) {
case EQUAL: case COLON: case COMPARISON:
return InfiniteUnaryDenotation.STAR_UNARY;
default:
return getExplicit().domain();
}
}
@Override
public UnaryDenotation range() {
switch (type) {
case EQUAL: case COLON: case COMPARISON:
return InfiniteUnaryDenotation.STAR_UNARY;
default:
return getExplicit().range();
}
}
@Override
public UnaryDenotation get(Value key) {
switch (type) {
case EQUAL:
return new ExplicitUnaryDenotation(key);
case COLON:
return InfiniteUnaryDenotation.STAR_UNARY;
case COMPARISON:
return new InfiniteUnaryDenotation.ComparisonUnaryDenotation(predicateId, key);
default:
return getExplicit().get(key);
}
}
@Override
public UnaryDenotation inverseGet(Value value) {
switch (type) {
case EQUAL:
return new ExplicitUnaryDenotation(value);
case COLON:
throw new LambdaDCSException(Type.invalidFormula, "Cannot perform inverseGet on COLON");
case COMPARISON:
return new InfiniteUnaryDenotation.ComparisonUnaryDenotation(
CanonicalNames.COMPARATOR_REVERSE.get(predicateId), value);
default:
return getExplicit().inverseGet(value);
}
}
// ============================================================
// Operations
// ============================================================
@Override
public PairList aggregate(Mode mode) {
switch (type) {
case EQUAL:
return this;
case COLON: case COMPARISON:
throw new LambdaDCSException(Type.infiniteList, "Cannot call aggregate on %s", this);
default:
return getExplicit().aggregate(mode);
}
}
@Override
public PairList filter(UnaryDenotation upperBound, UnaryDenotation domainUpperBound) {
return explicitlyFilter(upperBound, domainUpperBound);
}
@Override
public PairList reverse() {
switch (type) {
case EQUAL:
return this;
case COLON:
throw new LambdaDCSException(Type.invalidFormula, "Cannot perform reverse on COLON");
default:
return new PredicatePairList(CanonicalNames.reverseProperty(predicate), graph);
}
}
@Override
public UnaryDenotation joinOnKey(UnaryDenotation keys) {
switch (type) {
case EQUAL:
return keys;
case COLON:
return keys.isEmpty() ? UnaryDenotation.EMPTY : InfiniteUnaryDenotation.STAR_UNARY;
case COMPARISON:
return InfiniteUnaryDenotation.create(predicateId, keys);
default:
return new ExplicitUnaryDenotation(graph.joinSecond(predicate, keys));
}
}
@Override
public UnaryDenotation joinOnValue(UnaryDenotation values) {
switch (type) {
case EQUAL:
return values;
case COLON:
throw new LambdaDCSException(Type.invalidFormula, "Cannot perform reverse on COLON");
case COMPARISON:
return InfiniteUnaryDenotation.create(CanonicalNames.COMPARATOR_REVERSE.get(predicateId), values);
default:
return new ExplicitUnaryDenotation(graph.joinFirst(predicate, values));
}
}
@Override
public ExplicitPairList explicitlyFilterOnKey(UnaryDenotation keys) {
switch (type) {
case EQUAL:
if (keys.size() == Integer.MAX_VALUE)
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
List<Pair<Value, Value>> pairs = new ArrayList<>();
for (Value x : keys) pairs.add(new Pair<>(x, x));
return new ExplicitPairList(pairs);
case COLON: case COMPARISON:
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
default:
return new ExplicitPairList(graph.filterSecond(predicate, keys));
}
}
@Override
public ExplicitPairList explicitlyFilterOnValue(UnaryDenotation values) {
switch (type) {
case EQUAL:
if (values.size() == Integer.MAX_VALUE)
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
List<Pair<Value, Value>> pairs = new ArrayList<>();
for (Value x : values) pairs.add(new Pair<>(x, x));
return new ExplicitPairList(pairs);
case COLON: case COMPARISON:
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
default:
return new ExplicitPairList(graph.filterFirst(predicate, values));
}
}
public ExplicitPairList explicitlyFilter(UnaryDenotation values, UnaryDenotation keys) {
List<Pair<Value, Value>> pairs = new ArrayList<>();
switch (type) {
case EQUAL:
UnaryDenotation domain = values.merge(keys, MergeFormula.Mode.and);
if (domain.size() == Integer.MAX_VALUE)
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
for (Value x : domain) pairs.add(new Pair<>(x, x));
return new ExplicitPairList(pairs);
case COLON: case COMPARISON:
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
default:
try {
for (Pair<Value, Value> pair : graph.filterSecond(predicate, keys))
if (values.contains(pair.getFirst())) pairs.add(pair);
return new ExplicitPairList(pairs);
} catch (LambdaDCSException e) {
try {
for (Pair<Value, Value> pair : graph.filterFirst(predicate, values))
if (keys.contains(pair.getSecond())) pairs.add(pair);
return new ExplicitPairList(pairs);
} catch (LambdaDCSException e2) {
throw new LambdaDCSException(Type.infiniteList, "Cannot call explicitlyFilter* on %s", this);
}
}
}
}
}