package edu.stanford.nlp.sequences; import edu.stanford.nlp.util.ArrayUtils; import edu.stanford.nlp.util.Generics; import java.io.*; import java.util.*; /** * This class is meant to represent a clique in a (directed * or undirected) linear-chain graphical model. It encodes * the relative indices that are included in a clique with * respect to the current index (0). For instance if you have a clique * that is current label and two-ago label, then the relative * indices clique would look like [-2, 0]. The relativeIndices[] * array should be sorted. Cliques are immutable. Also, for two * cliques, c1 and c2, (c1 == c2) iff c1.equals(c2). * * @author Jenny Finkel */ public class Clique implements Serializable { private static final long serialVersionUID = -8109637472035159453L; private final int[] relativeIndices; protected static final Map<CliqueEqualityWrapper, Clique> interner = Generics.newHashMap(); private static class CliqueEqualityWrapper { private final Clique c; public CliqueEqualityWrapper(Clique c) { this.c = c; } @Override public boolean equals(Object o) { if (!(o instanceof CliqueEqualityWrapper)) { return false; } CliqueEqualityWrapper otherC = (CliqueEqualityWrapper)o; if (otherC.c.relativeIndices.length != c.relativeIndices.length) { return false; } for (int i = 0; i < c.relativeIndices.length; i++) { if (c.relativeIndices[i] != otherC.c.relativeIndices[i]) { return false; } } return true; } @Override public int hashCode() { int h = 1; for (int i : c.relativeIndices) { h *= 17; h += i; } return h; } } // end static class CliqueEqualityWrapper private static Clique intern(Clique c) { CliqueEqualityWrapper wrapper = new CliqueEqualityWrapper(c); Clique newC = interner.get(wrapper); if (newC == null) { interner.put(wrapper, c); newC = c; } return newC; } private Clique(int[] relativeIndices) { this.relativeIndices = relativeIndices; } public static Clique valueOf(int maxLeft, int maxRight) { int[] ri = new int[-maxLeft+maxRight+1]; int j = maxLeft; for (int i = 0; i < ri.length; i++) { ri[i] = j++; } return valueOfHelper(ri); } /** Make a clique over the provided relativeIndices. * relativeIndices should be sorted. */ public static Clique valueOf(int[] relativeIndices) { checkSorted(relativeIndices); // copy the array so as to be safe return valueOfHelper(ArrayUtils.copy(relativeIndices)); } public static Clique valueOf(Clique c, int offset) { int[] ri = new int[c.relativeIndices.length]; for (int i = 0; i < ri.length; i++) { ri[i] = c.relativeIndices[i]+offset; } return valueOfHelper(ri); } /** This version assumes relativeIndices array no longer needs to * be copied. Further it is assumed that it has already been * checked or assured by construction that relativeIndices * is sorted. */ private static Clique valueOfHelper(int[] relativeIndices) { // if clique already exists, return that one Clique c = new Clique(relativeIndices); return intern(c); } /** Parameter validity check. */ private static void checkSorted(int[] sorted) { for (int i = 0; i < sorted.length-1; i++) { if (sorted[i] > sorted[i+1]) { throw new RuntimeException("input must be sorted!"); } } } /** * Convenience method for finding the most far left * relative index. */ public int maxLeft() { return relativeIndices[0]; } /** * Convenience method for finding the most far right * relative index. */ public int maxRight() { return relativeIndices[relativeIndices.length-1]; } /** The number of nodes in the clique. */ public int size() { return relativeIndices.length; } /** @return the ith relativeIndex */ public int relativeIndex(int i) { return relativeIndices[i]; } /** * For a particular relative index, returns which element in * the Clique it is. For instance, if you created a Clique * c with relativeIndices [-2, -1, 0], then c.indexOfRelativeIndex(-1) * will return 1. If the relative index is not present, it * will return -1. */ public int indexOfRelativeIndex(int relativeIndex) { for (int i = 0; i < relativeIndices.length; i++) { if (relativeIndices[i] == relativeIndex) { return i; } } return -1; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); for (int i = 0; i < relativeIndices.length; i++) { sb.append(relativeIndices[i]); if (i != relativeIndices.length-1) { sb.append(", "); } } sb.append(']'); return sb.toString(); } public Clique leftMessage() { int[] ri = new int[relativeIndices.length-1]; System.arraycopy(relativeIndices, 0, ri, 0, ri.length); return valueOfHelper(ri); } public Clique rightMessage() { int[] ri = new int[relativeIndices.length-1]; System.arraycopy(relativeIndices, 1, ri, 0, ri.length); return valueOfHelper(ri); } public Clique shift(int shiftAmount) { if (shiftAmount == 0) { return this; } int[] ri = new int[relativeIndices.length]; for (int i = 0; i < ri.length; i++) { ri[i] = relativeIndices[i]+shiftAmount; } return valueOfHelper(ri); } private int hashCode = -1; @Override public int hashCode() { if (hashCode == -1) { hashCode = toString().hashCode(); } return hashCode; } protected Object readResolve() { return intern(this); } }