Package opennlp.ccg.disjunctivizer

Source Code of opennlp.ccg.disjunctivizer.LFGraphDifference

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 Scott Martin
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//////////////////////////////////////////////////////////////////////////////
package opennlp.ccg.disjunctivizer;

import static opennlp.ccg.alignment.PhrasePosition.A;
import static opennlp.ccg.alignment.PhrasePosition.B;
import static opennlp.ccg.disjunctivizer.MatchType.SOURCE_ALIGNED;
import static opennlp.ccg.disjunctivizer.MatchType.SOURCE_MATCH;
import static opennlp.ccg.disjunctivizer.MatchType.SOURCE_UNALIGNED;
import static opennlp.ccg.disjunctivizer.MatchType.TARGET_ALIGNED;
import static opennlp.ccg.disjunctivizer.MatchType.TARGET_UNALIGNED;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import opennlp.ccg.alignment.Alignment;
import opennlp.ccg.alignment.PhrasePosition;
import opennlp.ccg.hylo.graph.LFEdge;
import opennlp.ccg.hylo.graph.LFGraph;
import opennlp.ccg.hylo.graph.LFVertex;
import opennlp.ccg.util.CompositeFilter;
import opennlp.ccg.util.Filter;
import opennlp.ccg.util.FilteredMap;
import opennlp.ccg.util.FilteredSet;
import opennlp.ccg.util.VisitedFilter;

/**
* Represents the difference between two {@link LFGraph}s that characterizes their difference as a set
* of edits: either {@link #inserts()}, {@link #deletes()}, or {@link #substitutions()}. These edits are
* determined by a specified {@linkplain #getAlignment() alignment} between the phrases the graphs
* are supposed to represent. All of the sets of edges returned by this class, and the convenience maps
* build on top of them, are read-only. Attempting to add or remove elements or keys from any of these
* (including via any of the iterators) throws an {@link UnsupportedOperationException}.
* <p>
* This class also provides the convenience methods {@link #insertsFor(LFVertex)} and 
* {@link #deletesFor(LFVertex)}, for getting just the inserts or deletes for a specified vertex.
* The convenience methods {@link #substitutionsFor(LFEdge)} gets the set of substitutions correspoding
* to a given edge, and {@link #substitutionsBySource()} and {@link #substitutionsBySourceFor(LFEdge)}
* are similar methods that provide maps whose keys are the source vertices of the substituted edges.
*
* @author <a href="http://www.ling.ohio-state.edu/~scott/">Scott Martin</a>
*/
public class LFGraphDifference {

  final LFGraph a, b;
  final Alignment alignment;
 
  private Set<LFEdge> deletes, inserts, substitutions;
 
  /**
   * Creates a new graph difference between <tt>a</tt> and <tt>b</tt>, as determined by the specified
   * alignment.
   * @param a The {@linkplain PhrasePosition#A A-position} graph.
   * @param b The {@linkplain PhrasePosition#B B-position} graph.
   * @param alignment An alignment between <tt>a</tt> and <tt>b</tt> where the
   * {@linkplain PhrasePosition#A A-position} indices are understood to correspond to <tt>a</tt> and
   * {@linkplain PhrasePosition#B B-position} indices are understood to correspond to <tt>b</tt>.
   * @throws IllegalArgumentException If either graph is <tt>null</tt>, or if the alignment is
   * <tt>null</tt>.
   */
  public LFGraphDifference(LFGraph a, LFGraph b, Alignment alignment) {
    checkGraph(a, A);
    checkGraph(b, B);
   
    if(alignment == null) {
      throw new IllegalArgumentException("alignment is null");
    }
   
    this.a = a;
    this.b = b;
    this.alignment = alignment;
  }
 
  private void checkGraph(LFGraph g, PhrasePosition pos) {
    if(g == null) {
      throw new IllegalArgumentException(pos.name() + " graph is null");
    }
  }

  /**
   * Gets the {@linkplain PhrasePosition#A A-position} graph.
   */
  public LFGraph getA() {
    return get(A);
  }

  /**
   * Gets the {@linkplain PhrasePosition#B B-position} graph.
   */
  public LFGraph getB() {
    return get(B);
  }
 
  /**
   * Gets the graph in the specified position.
   * @param position The position to retrieve a graph for.
   * @return The value of {@link #getA()} if <tt>position</tt> is {@link PhrasePosition#A}, and
   * the value of {@link #getB()} otherwise.
   */
  public LFGraph get(PhrasePosition position) {
    return (position == A) ? a : b;
  }

  /**
   * Gets the alignment used to determine the edits between the two graphs.
   */
  public Alignment getAlignment() {
    return alignment;
  }
 
  /**
   * Computes a hash code for this graph difference based on its graphs and the
   * alignment between them.
   */
  @Override
  public int hashCode() {
    return 31 * a.hashCode() + b.hashCode() + alignment.hashCode();
  }

  /**
   * Tests whether this LF graph difference is equivalent to another by comparing their
   * graphs and the alignment between them.
   */
  @Override
  public boolean equals(Object obj) {
    if(obj instanceof LFGraphDifference) {
      LFGraphDifference diff = (LFGraphDifference)obj;
      return a.equals(diff.a) && b.equals(diff.b) && alignment.equals(diff.alignment);
    }
   
    return false;
  }
 
  /**
   * Gets a string representation of this graph difference.
   */
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder("difference for graphs: ");
   
    for(PhrasePosition pos : PhrasePosition.values()) {
      sb.append(pos);
      sb.append(": ");
      sb.append(get(pos));
      sb.append(", ");
    }
   
    sb.append("alignment: ");
    sb.append(alignment.toString());
   
    return sb.toString();
  }

  /**
   * Gets an LF graph difference that is the reverse of the present one.
   * @return An LF graph difference whose {@linkplain PhrasePosition#A A-position} graph is the value of this
   * difference's {@link #getB()}, whose {@linkplain PhrasePosition#B B-position} graph is the value of this
   * difference's {@link #getA()}, and whose alignments are the {@linkplain Alignment#reverse() reverse} of
   * this difference's {@link #getAlignment()}.
   */
  public LFGraphDifference reverse() {
    return new LFGraphDifference(b, a, alignment.reverse());
  }
 
  /**
   * Gets the deletes for this graph difference.
   * @return The set of edges in the {@linkplain PhrasePosition#A A-position} graph that have an aligned
   * {@linkplain LFEdge#getSource() source vertex} and an unaligned
   * {@linkplain LFEdge#getTarget() target vertex}.
   *
   * @see AlignedEdgeFilter
   */
  public Set<LFEdge> deletes() {
    return (deletes == null) ? (deletes = doDeletes(A)) : deletes;
  }
 
  /**
   * Gets the inserts for this graph difference.
   * @return The set of edges in the {@linkplain PhrasePosition#B B-position} graph that have an aligned
   * {@linkplain LFEdge#getSource() source vertex} and an unaligned
   * {@linkplain LFEdge#getTarget() target vertex}.
   *
   * @see AlignedEdgeFilter
   */
  public Set<LFEdge> inserts() {
    return (inserts == null) ? (inserts = doDeletes(B)) : inserts;
  }
 
  Set<LFEdge> doDeletes(PhrasePosition keyPosition) {
    return Collections.unmodifiableSet(new FilteredLFEdgeSet(get(keyPosition).edgeSet(),
        new AlignedEdgeFilter(alignment.asMap(keyPosition).keySet(),
            SOURCE_ALIGNED, TARGET_UNALIGNED)));
  }
 
  /**
   * Gets the inserts for a specified vertex. 
   * @param vertex The vertex to return the inserts for.
   * @return The subset of {@link #inserts()} whose {@linkplain LFEdge#getSource() source} index is among the
   * {@linkplain Alignment#getTargets(Integer) targets} for the specified vertex, or {@link Collections#EMPTY_SET}
   * if none exist.
   *
   * @see AlignedEdgeFilter
   */
  @SuppressWarnings("unchecked")
  public Set<LFEdge> insertsFor(LFVertex vertex) {
    Set<Integer> indices = alignment.getTargets(vertex.getIndex());
   
    return (indices.isEmpty()) ? Collections.EMPTY_SET
        : new FilteredLFEdgeSet(inserts(), new AlignedEdgeFilter(indices, SOURCE_ALIGNED));
  }
 
  /**
   * Gets the deletes for a specified vertex.
   * @param vertex The vertex to get the deletes for.
   * @return The subset of {@link #deletes()} whose {@linkplain LFEdge#getSource() source vertex} is
   * the specified vertex.
   *
   * @see VertexMatchFilter
   */
  public Set<LFEdge> deletesFor(LFVertex vertex) {
    return new FilteredLFEdgeSet(deletes(), new VertexMatchFilter(vertex, SOURCE_MATCH));
  }
 
  /**
   * Gets the substitutions for this graph difference.
   * @return The subset of the {@linkplain PhrasePosition#B B-position} graph's edges for which there
   * exists an edge in the {@linkplain PhrasePosition#A A-position} graph that meets the following
   * conditions:
   * <ol>
   *   <li>The B edge's source is aligned to the A edge's source, but the B edge's target is not.</li>
   *   <li>The B edge's target is aligned to the A edge's target, but the B edge's source is not.</li>
   * </ol>
   *
   * @see CompositeFilter
   */
  public Set<LFEdge> substitutions() {
    if(substitutions == null) {
      substitutions = new LinkedHashSet<LFEdge>();
      Set<LFEdge> bEdges = b.edgeSet();
     
      AlignedEdgeFilter sourceFilter = null, targetFilter = null;
      CompositeFilter<LFEdge> filter = new CompositeFilter<LFEdge>();
     
      for(LFEdge aEdge : a.edgeSet()) {
        Set<Integer> sMaps = alignment.getTargets(aEdge.getSource().getIndex()),
                tMaps = alignment.getTargets(aEdge.getTarget().getIndex());
       
        if(!sMaps.isEmpty() && !tMaps.isEmpty()) {
          if(sourceFilter == null) {
            sourceFilter = new AlignedEdgeFilter(sMaps, SOURCE_ALIGNED, TARGET_UNALIGNED);
            targetFilter = new AlignedEdgeFilter(tMaps, TARGET_ALIGNED, SOURCE_UNALIGNED);
           
            filter.addFilter(sourceFilter);
            filter.addFilter(targetFilter);
          }
          else {
            sourceFilter.setAlignmentIndices(sMaps);
            targetFilter.setAlignmentIndices(tMaps);
          }
         
          substitutions.addAll(new FilteredLFEdgeSet(bEdges, filter));
        }
      }
    }
   
    return Collections.unmodifiableSet(substitutions);
  }
 
  /**
   * Gets the substitutions for the specified edge.
   * @param edge The edge to get substitutions for.
   * @return The subset of {@link #substitutions()} whose source is aligned to the edge's source and
   * whose target is aligned to the edge's target, or {@link Collections#EMPTY_SET} if none exist.
   */
  @SuppressWarnings("unchecked")
  public Set<LFEdge> substitutionsFor(LFEdge edge) {
    Set<Integer> srcMapsTo = alignment.getTargets(edge.getSource().getIndex()),
      trgMapsTo = alignment.getTargets(edge.getTarget().getIndex());
   
    return (srcMapsTo.isEmpty() || trgMapsTo.isEmpty()) ? Collections.EMPTY_SET
        : Collections.unmodifiableSet(new FilteredLFEdgeSet(substitutions(),
            new CompositeFilter<LFEdge>(new AlignedEdgeFilter(srcMapsTo, SOURCE_ALIGNED),
                new AlignedEdgeFilter(trgMapsTo, TARGET_ALIGNED))));
  }
 
  /**
   * Gets a map view of the substitutions in this graph difference.
   * @return A map whose keys are the source vertices in the set of {@link #substitutions()} and whose values
   * are the edges whose {@linkplain LFEdge#getSource() source vertex} is the same as the corresponding key.
   * If there are no substitutions, {@link Collections#EMPTY_MAP} is returned.
   * <p>
   * Note that the returned map is
   * read-only, that is, both its {@link Map#put(Object, Object)} method and its
   * {@linkplain Map#entrySet() entry set}'s iterator's {@link Iterator#remove()} method throw an
   * {@link UnsupportedOperationException}. Also, the members of the returned map's entry set are immutable,
   * so that their {@link Entry#setValue(Object)} methods also throw an {@link UnsupportedOperationException}.
   */
  @SuppressWarnings("unchecked")
  public Map<LFVertex, Set<LFEdge>> substitutionsBySource() {
    Set<LFEdge> subs = substitutions();
   
    return subs.isEmpty() ? Collections.EMPTY_MAP
        : Collections.unmodifiableMap(
            new FilteredMap<LFVertex, Set<LFEdge>>(new SourceView(),
                new VisitedFilter<LFVertex>()));
  }
 
  /**
   * Gets a map view of the substitutions for the specified edge.
   * @param edge The edge to get substitutions for.
   * @return The subset of {@link #substitutionsBySource()} in which the keys are aligned to the specified edge's
   * source and the associated values' targets are aligned to the specified edge's target. Since this map is
   * based on the one returned by {@link #substitutionsBySource()}, it is also read-only, and the same stipulations
   * apply to it.
   *
   * @see #substitutionsBySource()
   */
  public Map<LFVertex, Set<LFEdge>> substitutionsBySourceFor(LFEdge edge) {
    Map<LFVertex, Set<LFEdge>> subsBySource = substitutionsBySource();   
    return subsBySource.isEmpty() ? subsBySource : new SubstitutedSourceView(subsBySource, edge);
  }
 
  class SourceView extends AbstractMap<LFVertex, FilteredLFEdgeSet> {

    @Override
    public Set<Entry<LFVertex, FilteredLFEdgeSet>> entrySet() {
      return new AbstractSet<Entry<LFVertex,FilteredLFEdgeSet>>() {
        Set<LFEdge> subs = substitutions();
       
        @Override
        public int size() {
          return subs.size();
        }

        @Override
        public Iterator<Entry<LFVertex, FilteredLFEdgeSet>> iterator() {
          return new Iterator<Entry<LFVertex,FilteredLFEdgeSet>>() {
            private Iterator<LFEdge> edgeIterator = null;
                                     
            @Override
            public boolean hasNext() {
              if(edgeIterator == null) {
                edgeIterator = subs.iterator();
              }
             
              return edgeIterator.hasNext();
            }

            @Override
            public Entry<LFVertex, FilteredLFEdgeSet> next() {
              if(edgeIterator == null) {
                edgeIterator = subs.iterator();
              }
             
              LFVertex src = edgeIterator.next().getSource();
             
              return new SimpleImmutableEntry<LFVertex, FilteredLFEdgeSet>(src,
                  new FilteredLFEdgeSet(subs, new VertexMatchFilter(src, SOURCE_MATCH)));
            }
           
            @Override
            public void remove() { // subs.iterator() should be read-only, but just in case
              throw new UnsupportedOperationException();
            }
          };
        }
      };
    }
  }
 
  class SubstitutedSourceView extends AbstractMap<LFVertex, Set<LFEdge>> {

    Map<LFVertex, Set<LFEdge>> sourceView;
    LFEdge edge;
   
    private Set<Entry<LFVertex, Set<LFEdge>>> entrySet;
   
    SubstitutedSourceView(Map<LFVertex, Set<LFEdge>> sourceView, LFEdge edge) {
      this.sourceView = sourceView;
      this.edge = edge;
    }
   
    @Override
    public Set<Entry<LFVertex, Set<LFEdge>>> entrySet() {
      return (entrySet == null) ? (entrySet = new EntrySet()) : entrySet;
    }
   
    class EntrySet extends AbstractSet<Entry<LFVertex, Set<LFEdge>>> {
      private Set<Entry<LFVertex, Set<LFEdge>>> entries;
      Set<Integer> srcMapsTo = alignment.getTargets(edge.getSource().getIndex()),
                trgMapsTo = alignment.getTargets(edge.getTarget().getIndex());
     
      Set<Entry<LFVertex, Set<LFEdge>>> entries() {
        if(entries == null) {
          entries = new FilteredSet<Entry<LFVertex, Set<LFEdge>>>(
            sourceView.entrySet(),
            new Filter<Entry<LFVertex, Set<LFEdge>>>() {
              @Override
              public boolean allows(Entry<LFVertex, Set<LFEdge>> e) {
                if(srcMapsTo.contains(e.getKey().getIndex())) {
                  for(LFEdge t : e.getValue()) {
                    if(trgMapsTo.contains(t.getTarget().getIndex())) {
                      return true;
                    }
                  }
                }
               
                return false;
              }
            }
          );
        }
       
        return entries;
      }
     
      @Override
      public int size() {
        return entries().size();
      }
     
      @Override
      public Iterator<Entry<LFVertex, Set<LFEdge>>> iterator() {
        return new Iterator<Entry<LFVertex, Set<LFEdge>>>() {
          private Iterator<Entry<LFVertex, Set<LFEdge>>> i = entries().iterator();
         
          @Override
          public boolean hasNext() {
            return i.hasNext();
          }

          @Override
          public Entry<LFVertex, Set<LFEdge>> next() {
            Entry<LFVertex, Set<LFEdge>> e = i.next();
            return new SimpleImmutableEntry<LFVertex, Set<LFEdge>>(
                e.getKey(),
                new FilteredLFEdgeSet(e.getValue(), new Filter<LFEdge>() {
                  @Override
                  public boolean allows(LFEdge e) {
                    return trgMapsTo.contains(e.getTarget().getIndex());
                  }                 
                }
              )
            );
          }

          @Override
          public void remove() { // source view is already read-only, so just in case
            throw new UnsupportedOperationException();           
          }         
        };
      }
    }
  }
}
TOP

Related Classes of opennlp.ccg.disjunctivizer.LFGraphDifference

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.