All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.berkeley.nlp.util.GeneralPriorityQueue Maven / Gradle / Ivy

Go to download

The Berkeley parser analyzes the grammatical structure of natural language using probabilistic context-free grammars (PCFGs).

The newest version!
package edu.berkeley.nlp.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * PriorityQueue with explicit double priority values. Larger doubles are higher
 * priorities. BinaryHeap-backed.
 * 
 * @author Dan Klein
 * @author Christopher Manning For each entry, uses ~ 24 (entry) + 16?
 *         (Map.Entry) + 4 (List entry) = 44 bytes?
 */
public class GeneralPriorityQueue implements PriorityQueueInterface, Serializable
{

	/**
	 * An Entry stores an object in the queue along with its
	 * current location (array position) and priority. uses ~ 8 (self) + 4 (key
	 * ptr) + 4 (index) + 8 (priority) = 24 bytes?
	 */
	public static final class Entry implements Serializable
	{
		public E key;

		public int index;

		public double priority;

		@Override
		public String toString()
		{
			return key + " at " + index + " (" + priority + ")";
		}
	}

	public boolean hasNext()
	{
		return size() > 0;
	}

	public E next()
	{

		final E removeFirst = removeFirst();
		return removeFirst;
	}

	public void remove()
	{
		throw new UnsupportedOperationException();
	}

	/**
	 * indexToEntry maps linear array locations (not priorities) to
	 * heap entries.
	 */
	private List> indexToEntry;
	
	

	/**
	 * keyToEntry maps heap objects to their heap entries.
	 */
	private Map> keyToEntry;

	public GeneralPriorityQueue deepCopy()
	{
		GeneralPriorityQueue pq = new GeneralPriorityQueue();
		for (Entry entry : indexToEntry)
		{
			pq.setPriority(entry.key, entry.priority);
		}
		return pq;
	}

	private Entry parent(Entry entry)
	{
		int index = entry.index;
		return (index > 0 ? getEntry((index - 1) / 2) : null);
	}

	private Entry leftChild(Entry entry)
	{
		int leftIndex = entry.index * 2 + 1;
		return (leftIndex < size() ? getEntry(leftIndex) : null);
	}

	private Entry rightChild(Entry entry)
	{
		int index = entry.index;
		int rightIndex = index * 2 + 2;
		return (rightIndex < size() ? getEntry(rightIndex) : null);
	}

	private int compare(Entry entryA, Entry entryB)
	{
		return compare(entryA.priority, entryB.priority);
	}

	protected int compare(double a, double b)
	{
		double diff = a - b;
		if (diff > 0.0) { return 1; }
		if (diff < 0.0) { return -1; }
		return 0;
	}

	/**
	 * Structural swap of two entries.
	 * 
	 * @param entryA
	 * @param entryB
	 */
	private void swap(Entry entryA, Entry entryB)
	{
		int indexA = entryA.index;
		int indexB = entryB.index;
		entryA.index = indexB;
		entryB.index = indexA;
		indexToEntry.set(indexA, entryB);
		indexToEntry.set(indexB, entryA);
	}

	/**
	 * Remove the last element of the heap (last in the index array).
	 */
	private void removeLastEntry()
	{
		Entry entry = indexToEntry.remove(size() - 1);
		keyToEntry.remove(entry.key);
	}

	/**
	 * Get the entry by key (null if none).
	 */
	protected Entry getEntry(Object key)
	{
		Entry entry = keyToEntry.get(key);
		return entry;
	}

	/**
	 * Get entry by index, exception if none.
	 */
	private Entry getEntry(int index)
	{
		Entry entry = indexToEntry.get(index);
		return entry;
	}

	protected Entry makeEntry(E key)
	{
		Entry entry = new Entry();
		entry.index = size();
		entry.key = key;
		entry.priority = Double.NEGATIVE_INFINITY;
		indexToEntry.add(entry);
		keyToEntry.put(key, entry);
		return entry;
	}

	/**
	 * iterative heapify up: move item o at index up until correctly placed
	 */
	protected void heapifyUp(Entry entry)
	{
		while (true)
		{
			if (entry.index == 0)
			{
				break;
			}
			Entry parentEntry = parent(entry);
			if (compare(entry, parentEntry) <= 0)
			{
				break;
			}
			swap(entry, parentEntry);
		}
	}

	/**
	 * On the assumption that leftChild(entry) and rightChild(entry) satisfy the
	 * heap property, make sure that the heap at entry satisfies this property
	 * by possibly percolating the element o downwards. I've replaced the
	 * obvious recursive formulation with an iterative one to gain (marginal)
	 * speed
	 */
	private void heapifyDown(Entry entry)
	{
		Entry currentEntry = entry;
		Entry bestEntry = null;

		do
		{
			bestEntry = currentEntry;

			Entry leftEntry = leftChild(currentEntry);
			if (leftEntry != null)
			{
				if (compare(bestEntry, leftEntry) < 0)
				{
					bestEntry = leftEntry;
				}
			}

			Entry rightEntry = rightChild(currentEntry);
			if (rightEntry != null)
			{
				if (compare(bestEntry, rightEntry) < 0)
				{
					bestEntry = rightEntry;
				}
			}

			if (bestEntry != currentEntry)
			{
				// Swap min and current
				swap(bestEntry, currentEntry);
				// at start of next loop, we set currentIndex to largestIndex
				// this indexation now holds current, so it is unchanged
			}
		} while (bestEntry != currentEntry);
		// System.err.println("Done with heapify down");
		// verify();
	}

	private void heapify(Entry entry)
	{
		heapifyUp(entry);
		heapifyDown(entry);
	}

	/**
	 * Finds the object with the highest priority, removes it, and returns it.
	 * 
	 * @return the object with highest priority
	 */
	public E removeFirst()
	{
		E first = getFirst();
		removeKey(first);
		return first;
	}

	/**
	 * Finds the object with the highest priority and returns it, without
	 * modifying the queue.
	 * 
	 * @return the object with minimum key
	 */
	public E getFirst()
	{
		if (isEmpty()) throw new NoSuchElementException();
		return getEntry(0).key;
	}

	/**
	 * Searches for the object in the queue and returns it. May be useful if you
	 * can create a new object that is .equals() to an object in the queue but
	 * is not actually identical, or if you want to modify an object that is in
	 * the queue.
	 * 
	 * @return null if the object is not in the queue, otherwise returns the
	 *         object.
	 */
	public E getObject(E key)
	{
		if (!containsKey(key)) return null;
		Entry e = getEntry(key);
		return e.key;
	}

	/**
	 * Get the priority of a key -- if the key is not in the queue,
	 * Double.NEGATIVE_INFINITY is returned.
	 * 
	 * @param key
	 * @return
	 */
	public double getPriority(E key)
	{
		Entry entry = getEntry(key);
		if (entry == null) { return Double.NEGATIVE_INFINITY; }
		return entry.priority;
	}

	public double removeKey(E key)
	{
		Entry entry = getEntry(key);
		if (entry == null) { return Double.NEGATIVE_INFINITY; }
		removeEntry(entry);
		return entry.priority;
	}

	private void removeEntry(Entry entry)
	{
		Entry lastEntry = getLastEntry();
		if (entry != lastEntry)
		{
			swap(entry, lastEntry);
			removeLastEntry();
			heapify(lastEntry);
		}
		else
		{
			removeLastEntry();
		}
		return;
	}

	private Entry getLastEntry()
	{
		return getEntry(size() - 1);
	}

	/**
	 * Promotes a key in the queue, adding it if it wasn't there already. If the
	 * specified priority is worse than the current priority, nothing happens.
	 * Faster than add if you don't care about whether the key is new.
	 * 
	 * @param key
	 *            an Object value
	 * @return whether the priority actually improved.
	 */
	public boolean relaxPriority(E key, double priority)
	{
		Entry entry = getEntry(key);
		if (entry == null)
		{
			entry = makeEntry(key);
		}
		if (compare(priority, entry.priority) <= 0) { return false; }
		entry.priority = priority;
		heapifyUp(entry);
		return true;
	}

	/**
	 * Demotes a key in the queue, adding it if it wasn't there already. If the
	 * specified priority is better than the current priority, nothing happens.
	 * If you decrease the priority on a non-present key, it will get added, but
	 * at its old implicit priority of Double.NEGATIVE_INFINITY.
	 * 
	 * @param key
	 *            an Object value
	 * @return whether the priority actually improved.
	 */
	public boolean decreasePriority(E key, double priority)
	{
		Entry entry = getEntry(key);
		if (entry == null)
		{
			entry = makeEntry(key);
		}
		if (compare(priority, entry.priority) >= 0) { return false; }
		entry.priority = priority;
		heapifyDown(entry);
		return true;
	}

	/**
	 * Changes a priority, either up or down, adding the key it if it wasn't
	 * there already.
	 * 
	 * @param key
	 *            an Object value
	 */
	public void setPriority(E key, double priority)
	{
		Entry entry = getEntry(key);

		if (entry == null)
		{
			entry = makeEntry(key);
		}
		else
		{
			if (entry.key != key)
			{

				entry.key = key;
				keyToEntry.put(key, entry);
			}
		}

		if (compare(priority, entry.priority) == 0) { return; }

		entry.priority = priority;
		heapify(entry);

		//		isValid(entry);
	}

	/**
	 * @param entry
	 * @param count
	 */
	private boolean isValid(Entry entry)
	{
		int count = 0;
		for (int i = 0; i < indexToEntry.size(); ++i)
		{
			if (indexToEntry.get(i).key == entry.key) count++;
		}
		assert count == 1;
		return count == 1;
	}

	/**
	 * Checks if the queue is empty.
	 * 
	 * @return a boolean value
	 */
	public boolean isEmpty()
	{
		return indexToEntry.isEmpty();
	}

	/**
	 * Get the number of elements in the queue.
	 * 
	 * @return queue size
	 */
	public int size()
	{
		return indexToEntry.size();
	}

	public List toSortedList()
	{
		List sortedList = new ArrayList(size());
		GeneralPriorityQueue queue = deepCopy();
		while (queue.hasNext())
		{
			sortedList.add(queue.next());
		}
		return sortedList;
	}

	public Iterator iterator()
	{
		return Collections.unmodifiableCollection(toSortedList()).iterator();
	}

	/**
	 * Clears the queue.
	 */
	public void clear()
	{
		indexToEntry.clear();
		keyToEntry.clear();
	}

	//  private void verify() {
	//    for (int i = 0; i < indexToEntry.size(); i++) {
	//      if (i != 0) {
	//        // check ordering
	//        if (compare(getEntry(i), parent(getEntry(i))) < 0) {
	//          System.err.println("Error in the ordering of the heap! ("+i+")");
	//          System.exit(0);
	//        }
	//      }
	//      // check placement
	//      if (i != ((Entry)indexToEntry.get(i)).index)
	//        System.err.println("Error in placement in the heap!");
	//    }
	//  }

	@Override
	public String toString()
	{
		List sortedKeys = toSortedList();
		StringBuffer sb = new StringBuffer("[");
		for (Iterator keyI = sortedKeys.iterator(); keyI.hasNext();)
		{
			E key = keyI.next();
			sb.append(key);
			sb.append("=");
			sb.append(getPriority(key));
			if (keyI.hasNext())
			{
				sb.append(", ");
			}
		}
		sb.append("]");
		return sb.toString();
	}

	public String toVerticalString()
	{
		List sortedKeys = toSortedList();
		StringBuffer sb = new StringBuffer();
		for (Iterator keyI = sortedKeys.iterator(); keyI.hasNext();)
		{
			E key = keyI.next();
			sb.append(key);
			sb.append(" : ");
			sb.append(getPriority(key));
			if (keyI.hasNext())
			{
				sb.append("\n");
			}
		}
		return sb.toString();
	}

	public double getPriority()
	{
		return getPriority(getFirst());
	}

	public boolean containsKey(E e)
	{
		return keyToEntry.containsKey(e);
	}

	public String toString(int maxKeysToPrint)
	{
		GeneralPriorityQueue pq = deepCopy();
		StringBuilder sb = new StringBuilder("[");
		int numKeysPrinted = 0;
		while (numKeysPrinted < maxKeysToPrint && !pq.isEmpty())
		{
			double priority = pq.getPriority();
			E element = pq.removeFirst();
			sb.append(element.toString());
			sb.append(" : ");
			sb.append(priority);
			if (numKeysPrinted < size() - 1)
			//        sb.append("\n");
				sb.append(", ");
			numKeysPrinted++;
		}
		if (numKeysPrinted < size()) sb.append("...");
		sb.append("]");
		return sb.toString();
	}

	public GeneralPriorityQueue()
	{
		this(new MapFactory.HashMapFactory>());
	}

	public GeneralPriorityQueue(MapFactory> mapFactory)
	{
		indexToEntry = new ArrayList>();
		keyToEntry = mapFactory.buildMap();
	}

	public static void main(String[] args)
	{
		GeneralPriorityQueue queue = new GeneralPriorityQueue();
		queue.setPriority("a", 1.0);
		System.out.println("Added a:1 " + queue);
		queue.setPriority("b", 2.0);
		System.out.println("Added b:2 " + queue);
		queue.setPriority("c", 1.5);
		System.out.println("Added c:1.5 " + queue);
		queue.setPriority("a", 3.0);
		System.out.println("Increased a to 3 " + queue);
		queue.setPriority("b", 0.0);
		System.out.println("Decreased b to 0 " + queue);
		System.out.println("removeFirst()=" + queue.next());
		System.out.println("queue=" + queue);
		System.out.println("removeFirst()=" + queue.next());
		System.out.println("queue=" + queue);
		System.out.println("removeFirst()=" + queue.next());
		System.out.println("queue=" + queue);
	}

	public void put(E key, double priority)
	{

		setPriority(key, priority);

	}

	public E peek()
	{
		return getFirst();
	}

	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy