com.linkedin.dagli.math.vector.SparseDoubleMapVector Maven / Gradle / Ivy
// AUTOGENERATED CODE. DO NOT MODIFY DIRECTLY! Instead, please modify the math/vector/SparseXMapVector.ftl file.
// See the README in the module's src/template directory for details.
package com.linkedin.dagli.math.vector;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.longs.Long2DoubleMap;
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.io.Serializable;
/**
* Implementation of a (sparse) {@link Vector} using double values and backed by a hashtable.
*
* This implementation is efficient when modification and iteration are not interleaved: the elements must be resorted
* when {@link #iterator()} or {@link #reverseIterator()} is called if the vector has changed since the previous call.
* Note that caching the sorted values means that this class is also relatively memory inefficient compared to a
* {@link SparseDoubleArrayVector}.
*/
public class SparseDoubleMapVector extends AbstractVector implements MutableVector, Serializable {
private static final long serialVersionUID = 1;
private final Long2DoubleOpenHashMap _vectorMap;
// used to cache a SparseArrayVector that provides sorted element iterators when needed; this must be recalculated
// whenever an entry is added or removed to the _vectorMap. Using a self-balancing tree as our primary data
// structure would avoid the need for this extra cached data, but we operate under the premise that iteration and
// modification are almost never interleaved.
private transient SparseDoubleArrayVector _cachedSparseArrayVector = null;
/**
* Create a new vector with the default initial capacity.
*/
public SparseDoubleMapVector() {
this(Hash.DEFAULT_INITIAL_SIZE);
}
/**
* Create a new vector with the specified initial capacity.
*
* @param initialCapacity the number of vector components to pre-allocate space for. The vector can still have more
* entries than this, of course--it will automatically grow as needed.
*/
public SparseDoubleMapVector(int initialCapacity) {
_vectorMap = new Long2DoubleOpenHashMap(initialCapacity);
}
@Override
public Class extends Number> valueType() {
return double.class;
}
@Override
public long size64() {
return _vectorMap.size();
}
@Override
public double increase(long index, double amount) {
// addTo returns the *previous* value
double result = _vectorMap.addTo(index, amount);
if (result == 0) { // adding new entry
_cachedSparseArrayVector = null;
}
if ((result + (amount)) == 0) {
_cachedSparseArrayVector = null;
_vectorMap.remove(index);
}
return result;
}
@Override
public double get(long index) {
return _vectorMap.get(index);
}
/**
* Sets the value of a particular index in the vector. Values of 0 are not stored explicitly; putting a value of 0
* thus removes the element from this instance's backing hashtable.
*
* @param index the index of the value
* @param value the value to be put
* @return the value previously associated with the index
*/
public double getAndSet(long index, double value) {
final double result;
if (value == 0) {
result = _vectorMap.remove(index);
if (result != 0) { // deleted an entry
_cachedSparseArrayVector = null;
}
} else {
result = _vectorMap.put(index, value);
if (result == 0) { // added an entry
_cachedSparseArrayVector = null;
}
}
return result;
}
@Override
public void put(long index, double value) {
getAndSet(index, value);
}
@Override
public void transformInPlace(VectorElementTransformer transformer) {
LongArrayList lal = new LongArrayList(4);
_vectorMap.long2DoubleEntrySet().fastForEach(keyAndValuePair -> {
long key = keyAndValuePair.getLongKey();
double res = transformer.transform(key, keyAndValuePair.getDoubleValue());
if (res != 0) {
// can safely modify entries while iterating
_vectorMap.put(key, res);
} else {
// cannot safely delete here, because the map enforces a minimum load factor and might resize
lal.add(key);
}
});
lal.forEach((long v) -> _vectorMap.remove(v));
}
private class UnorderedIterator implements VectorElementIterator {
private ObjectIterator _iterator = _vectorMap.long2DoubleEntrySet().fastIterator();
@Override
public T mapNext(VectorElementFunction mapper) {
Long2DoubleOpenHashMap.Entry nextVal = _iterator.next();
return mapper.apply(nextVal.getLongKey(), nextVal.getDoubleValue());
}
@Override
public void next(VectorElementConsumer consumer) {
Long2DoubleOpenHashMap.Entry nextVal = _iterator.next();
consumer.consume(nextVal.getLongKey(), nextVal.getDoubleValue());
}
@Override
public boolean hasNext() {
return _iterator.hasNext();
}
}
private void ensureCachedSparseArrayVector() {
if (_cachedSparseArrayVector == null) {
long[] indices = new long[_vectorMap.size()];
double[] values = new double[_vectorMap.size()];
ObjectIterator iterator = _vectorMap.long2DoubleEntrySet().fastIterator();
for (int offset = 0; offset < indices.length; offset++) {
Long2DoubleMap.Entry entry = iterator.next();
indices[offset] = entry.getLongKey();
values[offset] = entry.getDoubleValue();
}
_cachedSparseArrayVector = SparseDoubleArrayVector.wrap(indices, values);
}
}
@Override
public VectorElementIterator unorderedIterator() {
return new UnorderedIterator();
}
@Override
public VectorElementIterator iterator() {
ensureCachedSparseArrayVector();
return _cachedSparseArrayVector.iterator();
}
@Override
public VectorElementIterator reverseIterator() {
ensureCachedSparseArrayVector();
return _cachedSparseArrayVector.reverseIterator();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy