package edu.stanford.nlp.sempre;
import java.util.*;
import fig.basic.*;
/**
* Represent a knowledge graph explicitly as triples (e1, r, e2).
*
* The graph is immutable. Once the graph is initialized, we precompute several mappings
* (e.g., list of all outgoing edges from each entity e).
*
* @author ppasupat
*/
public class NaiveKnowledgeGraph extends KnowledgeGraph {
// Represent a triple (entity, relation, entity)
public static class KnowledgeGraphTriple {
public final Value e1, r, e2;
public KnowledgeGraphTriple(Value e1, Value r, Value e2) {
this.e1 = e1;
this.r = r;
this.e2 = e2;
}
public KnowledgeGraphTriple(String e1, String r, String e2) {
this.e1 = new StringValue(e1);
this.r = new StringValue(r);
this.e2 = new StringValue(e2);
}
public KnowledgeGraphTriple(LispTree tree) {
if (tree.children.size() != 3)
throw new RuntimeException("Invalid triple size (" + tree.children.size() + " != 3): "
+ tree);
this.e1 = valueFromLispTree(tree.child(0));
this.r = valueFromLispTree(tree.child(1));
this.e2 = valueFromLispTree(tree.child(2));
}
protected static Value valueFromLispTree(LispTree tree) {
if (tree.isLeaf()) return new NameValue(tree.value, null);
return Values.fromLispTree(tree);
}
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild(e1.toLispTree());
tree.addChild(r.toLispTree());
tree.addChild(e2.toLispTree());
return tree;
}
@Override
public String toString() {
return "<" + e1 + ", " + r + ", " + e2 + ">";
}
}
// Simplest graph representation: triples of values
public final List<KnowledgeGraphTriple> triples;
// ============================================================
// Constructor / Precomputation
// ============================================================
public Map<Value, List<KnowledgeGraphTriple>> relationToTriples;
public Map<Value, List<KnowledgeGraphTriple>> firstToTriples;
public Map<Value, List<KnowledgeGraphTriple>> secondToTriples;
public NaiveKnowledgeGraph(Collection<KnowledgeGraphTriple> triples) {
this.triples = new ArrayList<>(triples);
precomputeMappings();
}
public void precomputeMappings() {
relationToTriples = new HashMap<>();
firstToTriples = new HashMap<>();
secondToTriples = new HashMap<>();
for (KnowledgeGraphTriple triple : triples) {
MapUtils.addToList(relationToTriples, triple.r, triple);
MapUtils.addToList(firstToTriples, triple.e1, triple);
MapUtils.addToList(secondToTriples, triple.e2, triple);
}
}
// ============================================================
// Queries
// ============================================================
@Override
public List<Value> joinFirst(Value r, Collection<Value> firsts) {
if (CanonicalNames.isReverseProperty(r))
return joinSecond(CanonicalNames.reverseProperty(r), firsts);
List<Value> seconds = new ArrayList<>();
List<KnowledgeGraphTriple> relationTriples = relationToTriples.get(r);
if (relationTriples != null) {
for (KnowledgeGraphTriple triple : relationTriples) {
if (firsts.contains(triple.e1))
seconds.add(triple.e2);
}
}
return seconds;
}
@Override
public List<Value> joinSecond(Value r, Collection<Value> seconds) {
if (CanonicalNames.isReverseProperty(r))
return joinFirst(CanonicalNames.reverseProperty(r), seconds);
List<Value> firsts = new ArrayList<>();
List<KnowledgeGraphTriple> relationTriples = relationToTriples.get(r);
if (relationTriples != null) {
for (KnowledgeGraphTriple triple : relationTriples) {
if (seconds.contains(triple.e2))
firsts.add(triple.e1);
}
}
return firsts;
}
@Override
public List<Pair<Value, Value>> filterFirst(Value r, Collection<Value> firsts) {
if (CanonicalNames.isReverseProperty(r))
return getReversedPairs(filterSecond(CanonicalNames.reverseProperty(r), firsts));
List<Pair<Value, Value>> pairs = new ArrayList<>();
List<KnowledgeGraphTriple> relationTriples = relationToTriples.get(r);
if (relationTriples != null) {
for (KnowledgeGraphTriple triple : relationTriples) {
if (firsts.contains(triple.e1))
pairs.add(new Pair<>(triple.e1, triple.e2));
}
}
return pairs;
}
@Override
public List<Pair<Value, Value>> filterSecond(Value r, Collection<Value> seconds) {
if (CanonicalNames.isReverseProperty(r))
return getReversedPairs(filterFirst(CanonicalNames.reverseProperty(r), seconds));
List<Pair<Value, Value>> pairs = new ArrayList<>();
List<KnowledgeGraphTriple> relationTriples = relationToTriples.get(r);
if (relationTriples != null) {
for (KnowledgeGraphTriple triple : relationTriples) {
if (seconds.contains(triple.e2))
pairs.add(new Pair<>(triple.e1, triple.e2));
}
}
return pairs;
}
// ============================================================
// LispTree conversion
// ============================================================
/**
* Convert LispTree to KnowledgeGraph
*
* The |tree| should look like
*
* (graph NaiveKnowledgeGraph
* ((string Obama) (string "born in") (string Hawaii))
* ((string Einstein) (string "born in") (string Ulm))
* ...)
*/
public static KnowledgeGraph fromLispTree(LispTree tree) {
List<KnowledgeGraphTriple> triples = new ArrayList<>();
for (int i = 2; i < tree.children.size(); i++) {
triples.add(new KnowledgeGraphTriple(tree.child(i)));
}
return new NaiveKnowledgeGraph(triples);
}
public static KnowledgeGraph fromFile(String path) {
return fromLispTree(LispTree.proto.parseFromFile(path).next());
}
@Override
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild("graph");
tree.addChild("NaiveKnowledgeGraph");
for (KnowledgeGraphTriple triple : triples) {
tree.addChild(triple.toLispTree());
}
return tree;
}
@Override
public LispTree toShortLispTree() {
if (triples.size() > 1000) {
LispTree tree = LispTree.proto.newList();
tree.addChild("graph");
tree.addChild("NaiveKnowledgeGraph");
tree.addChild(("TooManyTriples"));
return tree;
}
return toLispTree();
}
}