/** * Search from the root of the tree. This function also defines the default action space to use * during search. This is NOT recommended to be used at test time. * * @see edu.stanford.nlp.naturalli.ClauseSplitterSearchProblem#search(Predicate) * @param candidateFragments The callback function. * @param classifier The classifier for whether an arc should be on the path to a clause split, a * clause split itself, or neither. * @param featurizer The featurizer to use during search, to be dot producted with the weights. */ public void search( // The output specs final Predicate<Triple<Double, List<Counter<String>>, Supplier<SentenceFragment>>> candidateFragments, // The learning specs final Classifier<ClauseSplitter.ClauseClassifierLabel, String> classifier, final Map<String, List<String>> hardCodedSplits, final Function<Triple<State, Action, State>, Counter<String>> featurizer, final int maxTicks) { Collection<Action> actionSpace = new ArrayList<>(); // SIMPLE SPLIT actionSpace.add( new Action() { @Override public String signature() { return "simple"; } @Override public boolean prerequisitesMet(SemanticGraph originalTree, SemanticGraphEdge edge) { char tag = edge.getDependent().tag().charAt(0); return !(tag != 'V' && tag != 'N' && tag != 'J' && tag != 'P' && tag != 'D'); } @Override public Optional<State> applyTo( SemanticGraph tree, State source, SemanticGraphEdge outgoingEdge, SemanticGraphEdge subjectOrNull, SemanticGraphEdge objectOrNull) { return Optional.of( new State( outgoingEdge, subjectOrNull == null ? source.subjectOrNull : subjectOrNull, subjectOrNull == null ? (source.distanceFromSubj + 1) : 0, objectOrNull == null ? source.objectOrNull : objectOrNull, source.thunk.andThen( toModify -> { assert Util.isTree(toModify); simpleClause(toModify, outgoingEdge); if (outgoingEdge.getRelation().toString().endsWith("comp")) { stripAuxMark(toModify); } assert Util.isTree(toModify); }), false)); } }); // CLONE ROOT actionSpace.add( new Action() { @Override public String signature() { return "clone_root_as_nsubjpass"; } @Override public boolean prerequisitesMet(SemanticGraph originalTree, SemanticGraphEdge edge) { // Only valid if there's a single nontrivial outgoing edge from a node. Otherwise it's a // whole can of worms. Iterator<SemanticGraphEdge> iter = originalTree.outgoingEdgeIterable(edge.getGovernor()).iterator(); if (!iter.hasNext()) { return false; // what? } boolean nontrivialEdge = false; while (iter.hasNext()) { SemanticGraphEdge outEdge = iter.next(); switch (outEdge.getRelation().toString()) { case "nn": case "amod": break; default: if (nontrivialEdge) { return false; } nontrivialEdge = true; } } return true; } @Override public Optional<State> applyTo( SemanticGraph tree, State source, SemanticGraphEdge outgoingEdge, SemanticGraphEdge subjectOrNull, SemanticGraphEdge objectOrNull) { return Optional.of( new State( outgoingEdge, subjectOrNull == null ? source.subjectOrNull : subjectOrNull, subjectOrNull == null ? (source.distanceFromSubj + 1) : 0, objectOrNull == null ? source.objectOrNull : objectOrNull, source.thunk.andThen( toModify -> { assert Util.isTree(toModify); simpleClause(toModify, outgoingEdge); addSubtree( toModify, outgoingEdge.getDependent(), "nsubjpass", tree, outgoingEdge.getGovernor(), Collections.singleton(outgoingEdge)); // addWord(toModify, outgoingEdge.getDependent(), "auxpass", // mockNode(outgoingEdge.getDependent().backingLabel(), "is", "VBZ")); assert Util.isTree(toModify); }), true)); } }); // COPY SUBJECT actionSpace.add( new Action() { @Override public String signature() { return "clone_nsubj"; } @Override public boolean prerequisitesMet(SemanticGraph originalTree, SemanticGraphEdge edge) { // Don't split into anything but verbs or nouns char tag = edge.getDependent().tag().charAt(0); if (tag != 'V' && tag != 'N') { return false; } for (SemanticGraphEdge grandchild : originalTree.outgoingEdgeIterable(edge.getDependent())) { if (grandchild.getRelation().toString().contains("subj")) { return false; } } return true; } @Override public Optional<State> applyTo( SemanticGraph tree, State source, SemanticGraphEdge outgoingEdge, SemanticGraphEdge subjectOrNull, SemanticGraphEdge objectOrNull) { if (subjectOrNull != null && !outgoingEdge.equals(subjectOrNull)) { return Optional.of( new State( outgoingEdge, subjectOrNull, 0, objectOrNull == null ? source.objectOrNull : objectOrNull, source.thunk.andThen( toModify -> { assert Util.isTree(toModify); simpleClause(toModify, outgoingEdge); addSubtree( toModify, outgoingEdge.getDependent(), "nsubj", tree, subjectOrNull.getDependent(), Collections.singleton(outgoingEdge)); assert Util.isTree(toModify); stripAuxMark(toModify); assert Util.isTree(toModify); }), false)); } else { return Optional.empty(); } } }); // COPY OBJECT actionSpace.add( new Action() { @Override public String signature() { return "clone_dobj"; } @Override public boolean prerequisitesMet(SemanticGraph originalTree, SemanticGraphEdge edge) { // Don't split into anything but verbs or nouns char tag = edge.getDependent().tag().charAt(0); if (tag != 'V' && tag != 'N') { return false; } for (SemanticGraphEdge grandchild : originalTree.outgoingEdgeIterable(edge.getDependent())) { if (grandchild.getRelation().toString().contains("subj")) { return false; } } return true; } @Override public Optional<State> applyTo( SemanticGraph tree, State source, SemanticGraphEdge outgoingEdge, SemanticGraphEdge subjectOrNull, SemanticGraphEdge objectOrNull) { if (objectOrNull != null && !outgoingEdge.equals(objectOrNull)) { return Optional.of( new State( outgoingEdge, subjectOrNull == null ? source.subjectOrNull : subjectOrNull, subjectOrNull == null ? (source.distanceFromSubj + 1) : 0, objectOrNull, source.thunk.andThen( toModify -> { assert Util.isTree(toModify); // Split the clause simpleClause(toModify, outgoingEdge); // Attach the new subject addSubtree( toModify, outgoingEdge.getDependent(), "nsubj", tree, objectOrNull.getDependent(), Collections.singleton(outgoingEdge)); // Strip bits we don't want assert Util.isTree(toModify); stripAuxMark(toModify); assert Util.isTree(toModify); }), false)); } else { return Optional.empty(); } } }); for (IndexedWord root : tree.getRoots()) { search( root, candidateFragments, classifier, hardCodedSplits, featurizer, actionSpace, maxTicks); } }
/** * A helper to add an entire subtree to a given dependency tree. * * @param toModify The tree to add the subtree to. * @param root The root of the tree where we should be adding the subtree. * @param rel The relation to add the subtree with. * @param originalTree The orignal tree (i.e., {@link ClauseSplitterSearchProblem#tree}). * @param subject The root of the clause to add. * @param ignoredEdges The edges to ignore adding when adding this subtree. */ private static void addSubtree( SemanticGraph toModify, IndexedWord root, String rel, SemanticGraph originalTree, IndexedWord subject, Collection<SemanticGraphEdge> ignoredEdges) { if (toModify.containsVertex(subject)) { return; // This subtree already exists. } Queue<IndexedWord> fringe = new LinkedList<>(); Collection<IndexedWord> wordsToAdd = new ArrayList<>(); Collection<SemanticGraphEdge> edgesToAdd = new ArrayList<>(); // Search for subtree to add for (SemanticGraphEdge edge : originalTree.outgoingEdgeIterable(subject)) { if (!ignoredEdges.contains(edge)) { if (toModify.containsVertex(edge.getDependent())) { // Case: we're adding a subtree that's not disjoint from toModify. This is bad news. return; } edgesToAdd.add(edge); fringe.add(edge.getDependent()); } } while (!fringe.isEmpty()) { IndexedWord node = fringe.poll(); wordsToAdd.add(node); for (SemanticGraphEdge edge : originalTree.outgoingEdgeIterable(node)) { if (!ignoredEdges.contains(edge)) { if (toModify.containsVertex(edge.getDependent())) { // Case: we're adding a subtree that's not disjoint from toModify. This is bad news. return; } edgesToAdd.add(edge); fringe.add(edge.getDependent()); } } } // Add subtree // (add subject) toModify.addVertex(subject); toModify.addEdge( root, subject, GrammaticalRelation.valueOf(Language.English, rel), Double.NEGATIVE_INFINITY, false); // (add nodes) wordsToAdd.forEach(toModify::addVertex); // (add edges) for (SemanticGraphEdge edge : edgesToAdd) { assert !toModify.incomingEdgeIterator(edge.getDependent()).hasNext(); toModify.addEdge( edge.getGovernor(), edge.getDependent(), edge.getRelation(), edge.getWeight(), edge.isExtra()); } }