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

org.neo4j.gds.allshortestpaths.IntPriorityQueue Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.neo4j.gds.allshortestpaths;

import com.carrotsearch.hppc.IntDoubleScatterMap;
import com.carrotsearch.hppc.IntLongScatterMap;
import org.jetbrains.annotations.TestOnly;
import org.neo4j.gds.collections.ArrayUtil;
import org.neo4j.gds.collections.ha.HugeIntArray;

/**
 * A PriorityQueue specialized for ints that maintains a partial ordering of
 * its elements such that the smallest value can always be found in constant time.
 * The definition of what small means is up to the implementing subclass.
 * 

* Put()'s and pop()'s require log(size) time but the remove() cost implemented here is linear. *

* NOTE: Iteration order is not specified. * * Implementation has been copied from https://issues.apache.org/jira/browse/SOLR-2092 * and slightly adapted to our needs. */ public abstract class IntPriorityQueue { private static final int DEFAULT_CAPACITY = 14; private static final int[] EMPTY_INT = new int[0]; private HugeIntArray heap; private final IntLongScatterMap mapElementToIndex; private long size = 0; /** * Creates a new queue with the given capacity. * The queue dynamically grows to hold all elements. */ IntPriorityQueue(final int initialCapacity) { final int heapSize; if (0 == initialCapacity) { // We allocate 1 extra to avoid if statement in top() heapSize = 2; } else { // NOTE: we add +1 because all access to heap is // 1-based not 0-based. heap[0] is unused. heapSize = initialCapacity + 1; } this.heap = HugeIntArray.newArray(ArrayUtil.oversizeHuge(heapSize, Integer.BYTES)); this.mapElementToIndex = new IntLongScatterMap(initialCapacity); } /** * Defines the ordering of the queue. * Returns true iff {@code a} is strictly less than {@code b}. *

* The default behavior assumes a min queue, where the smallest value is on top. * To implement a max queue, return {@code b < a}. * The resulting order is not stable. */ protected abstract boolean lessThan(int a, int b); /** * Adds the cost for the given element. * * @return true if the cost was changed, indicating that the node is already in the queue. */ protected abstract boolean addCost(int element, double cost); /** * remove given element from cost management */ protected abstract void removeCost(int element); /** * Gets the cost for the given element. */ protected abstract double cost(int element); /** * Adds an int associated with the given weight to a queue in log(size) time. *

* NOTE: The default implementation does nothing with the cost parameter. * It is up to the implementation how the cost parameter is used. */ public final void add(int element, double cost) { addCost(element, cost); size++; ensureCapacityForInsert(); placeElement(size, element); upHeap(size); } private void add(int element) { size++; ensureCapacityForInsert(); placeElement(size, element); upHeap(size); } /** * @return the least element of the queue in constant time. */ public final int top() { if (isEmpty()) { throw new IndexOutOfBoundsException("Priority Queue is empty"); } return heap.get(1); } /** * Removes and returns the least element of the queue in log(size) time. * * @return the least element of the queue in log(size) time while removing it. */ public final int pop() { if (size > 0) { int result = heap.get(1); // save first value mapElementToIndex.remove(result); placeElement(1, heap.get(size));// move last to first size--; downHeap(1); // adjust heap removeCost(result); return result; } else { return -1; } } /** * @return the number of elements currently stored in the queue. */ public final long size() { return size; } /** * @return true iff there are currently no elements stored in the queue. */ public final boolean isEmpty() { return size == 0; } /** * Removes all entries from the queue. */ public void clear() { size = 0; } /** * Updates the heap because the cost of an element has changed, possibly from the outside. * Cost is linear with the size of the queue. */ public final void update(int element) { long pos = findElementPosition(element); if (pos != 0) { if (!upHeap(pos) && pos < size) { downHeap(pos); } } } public final void set(int element, double cost) { if (addCost(element, cost)) { update(element); } else { add(element); } } long findElementPosition(int element) { return mapElementToIndex.get(element); } /** * Removes all entries from the queue, releases all buffers. * The queue can no longer be used afterwards. */ public void release() { size = 0; heap = null; } private boolean upHeap(long origPos) { long i = origPos; int node = heap.get(i); // save bottom node long j = i >>> 1; while (j > 0 && lessThan(node, heap.get(j))) { placeElement(i, heap.get(j));// shift parents down i = j; j = j >>> 1; } placeElement(i, node); // install saved node return i != origPos; } private void downHeap(long i) { int node = heap.get(i); // save top node long j = i << 1; // find smaller child long k = j + 1; if (k <= size && lessThan(heap.get(k), heap.get(j))) { j = k; } while (j <= size && lessThan(heap.get(j), node)) { placeElement(i, heap.get(j)); // shift up child i = j; j = i << 1; k = j + 1; if (k <= size && lessThan(heap.get(k), heap.get(j))) { j = k; } } placeElement(i, node); // install saved node } private void ensureCapacityForInsert() { if (size >= heap.size()) { long oversize = ArrayUtil.oversizeHuge(size + 1, Integer.BYTES); heap = heap.copyOf(oversize); } } public static IntPriorityQueue min(int capacity) { return new AbstractPriorityQueue(capacity) { @Override protected boolean lessThan(int a, int b) { return costs.get(a) < costs.get(b); } }; } public static IntPriorityQueue max(int capacity) { return new AbstractPriorityQueue(capacity) { @Override protected boolean lessThan(int a, int b) { return costs.get(a) > costs.get(b); } }; } public static IntPriorityQueue min() { return min(DEFAULT_CAPACITY); } public static IntPriorityQueue max() { return max(DEFAULT_CAPACITY); } private abstract static class AbstractPriorityQueue extends IntPriorityQueue { protected final IntDoubleScatterMap costs; AbstractPriorityQueue(int initialCapacity) { super(initialCapacity); this.costs = new IntDoubleScatterMap(initialCapacity); } @Override protected boolean addCost(int element, double cost) { var contained = costs.containsKey(element); costs.put(element, cost); return contained; } @Override protected void removeCost(final int element) { costs.remove(element); } @Override protected double cost(int element) { return costs.get(element); } @Override public void clear() { super.clear(); costs.clear(); } @Override public void release() { super.release(); costs.keys = EMPTY_INT; costs.clear(); costs.keys = null; costs.values = null; } } private void placeElement(long position, int element) { heap.set(position, element); mapElementToIndex.put(element, position); } @TestOnly long getIth(int i) { return heap.get(i + 1); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy