no.uib.cipr.matrix.sparse.SparseVector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mt-java Show documentation
Show all versions of mt-java Show documentation
Matrix data structures, linear solvers, least squares methods, eigenvalue,
and singular value decompositions. For larger random dense matrices (above ~ 350 x 350)
matrix-matrix multiplication C = A.B is about 50% faster than MTJ.
The newest version!
/*
* Copyright (C) 2003-2006 Bjørn-Ove Heimsund
*
* This file is part of MTJ.
*
* 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 library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package no.uib.cipr.matrix.sparse;
import java.util.Iterator;
import no.uib.cipr.matrix.AbstractVector;
import no.uib.cipr.matrix.DenseVector;
import no.uib.cipr.matrix.Matrices;
import no.uib.cipr.matrix.Vector;
import no.uib.cipr.matrix.VectorEntry;
/**
* Sparse vector
*/
public class SparseVector extends AbstractVector implements ISparseVector {
private static final long serialVersionUID = 4094199314372334420L;
/**
* Data
*/
double[] data;
/**
* Indices to data
*/
int[] index;
/**
* How much has been used
*/
int used;
/**
* Constructor for SparseVector.
*
* @param size
* Size of the vector
* @param nz
* Initial number of non-zeros
*/
public SparseVector(int size, int nz) {
super(size);
data = new double[nz];
index = new int[nz];
}
/**
* Constructor for SparseVector, and copies the contents from the supplied
* vector.
*
* @param x
* Vector to copy from
* @param deep
* True if a deep copy is to be made. If the copy is shallow,
* x
must be a SparseVector
*/
public SparseVector(Vector x, boolean deep) {
super(x);
if (deep) {
int nz = Matrices.cardinality(x);
data = new double[nz];
index = new int[nz];
set(x);
} else {
SparseVector xs = (SparseVector) x;
data = xs.getData();
index = xs.getIndex();
used = xs.getUsed();
}
}
/**
* Constructor for SparseVector, and copies the contents from the supplied
* vector. Zero initial pre-allocation.
*
* @param x
* Vector to copy from. A deep copy is made
*/
public SparseVector(Vector x) {
this(x, true);
}
/**
* Constructor for SparseVector. Zero initial pre-allocation.
*
* @param size
* Size of the vector
*/
public SparseVector(int size) {
this(size, 0);
}
/**
* Constructor for SparseVector.
*
* @param size
* Size of the vector
* @param index
* Indices of the vector
* @param data
* Entries of the vector
* @param deep
* True for a deep copy. For shallow copies, the given indices
* will be used internally
*/
public SparseVector(int size, int[] index, double[] data, boolean deep) {
super(size);
if (index.length != data.length)
throw new IllegalArgumentException("index.length != data.length");
if (deep) {
used = index.length;
this.index = index.clone();
this.data = data.clone();
} else {
this.index = index;
this.data = data;
used = index.length;
}
}
/**
* Constructor for SparseVector.
*
* @param size
* Size of the vector
* @param index
* The vector indices are copies from this array
* @param data
* The vector entries are copies from this array
*/
public SparseVector(int size, int[] index, double[] data) {
this(size, index, data, true);
}
@Override
public void set(int index, double value) {
check(index);
// TODO: should we check against zero when setting zeros?
int i = getIndex(index);
data[i] = value;
}
@Override
public void add(int index, double value) {
check(index);
int i = getIndex(index);
data[i] += value;
}
@Override
public double get(int index) {
check(index);
int in = java.util.Arrays.binarySearch(this.index, 0, used, index);
if (in >= 0)
return data[in];
return 0;
}
/**
* Tries to find the index. If it is not found, a reallocation is done, and
* a new index is returned.
*/
private int getIndex(int ind) {
// Try to find column index
int i = Arrays.binarySearchGreater(index, ind, 0, used);
// Found
if (i < used && index[i] == ind)
return i;
int[] newIndex = index;
double[] newData = data;
// Check available memory
if (++used > data.length) {
// If zero-length, use new length of 1, else double the bandwidth
int newLength = data.length != 0 ? data.length << 1 : 1;
// Enforce the maximum size.
newLength = Math.min(newLength, this.size);
// Copy existing data into new arrays
newIndex = new int[newLength];
newData = new double[newLength];
System.arraycopy(index, 0, newIndex, 0, i);
System.arraycopy(data, 0, newData, 0, i);
}
// All ok, make room for insertion
System.arraycopy(index, i, newIndex, i + 1, used - i - 1);
System.arraycopy(data, i, newData, i + 1, used - i - 1);
// Put in new structure
newIndex[i] = ind;
newData[i] = 0.;
// Update pointers
index = newIndex;
data = newData;
// Return insertion index
return i;
}
@Override
public SparseVector copy() {
return new SparseVector(this);
}
@Override
public SparseVector zero() {
java.util.Arrays.fill(data, 0);
used = 0;
return this;
}
@Override
public SparseVector scale(double alpha) {
// Quick return if possible
if (alpha == 0)
return zero();
else if (alpha == 1)
return this;
for (int i = 0; i < used; ++i)
data[i] *= alpha;
return this;
}
@Override
public double dot(Vector y) {
if (!(y instanceof DenseVector))
return super.dot(y);
checkSize(y);
double[] yd = ((DenseVector) y).getData();
double ret = 0;
for (int i = 0; i < used; ++i)
ret += data[i] * yd[index[i]];
return ret;
}
@Override
protected double norm1() {
double sum = 0;
for (int i = 0; i < used; ++i)
sum += Math.abs(data[i]);
return sum;
}
@Override
protected double norm2() {
double norm = 0;
for (int i = 0; i < used; ++i)
norm += data[i] * data[i];
return Math.sqrt(norm);
}
@Override
protected double norm2_robust() {
double scale = 0, ssq = 1;
for (int i = 0; i < used; ++i) {
if (data[i] != 0) {
double absxi = Math.abs(data[i]);
if (scale < absxi) {
ssq = 1 + ssq * Math.pow(scale / absxi, 2);
scale = absxi;
} else
ssq = ssq + Math.pow(absxi / scale, 2);
}
}
return scale * Math.sqrt(ssq);
}
@Override
protected double normInf() {
double max = 0;
for (int i = 0; i < used; ++i)
max = Math.max(Math.abs(data[i]), max);
return max;
}
/**
* Returns the internal value array. This array may contain extra elements
* beyond the number that are used. If it is greater than the number used,
* the remaining values will be 0. Since this vector can resize its internal
* data, if it is modified, this array may no longer represent the internal
* state.
*
* @return The internal array of values.
*/
public double[] getData() {
return data;
}
/**
* Returns the used indices.
*/
public int[] getIndex() {
if (used == index.length)
return index;
// could run compact, or return subarray
// compact();
int[] indices = new int[used];
System.arraycopy(index, 0, indices, 0, used);
return indices;
}
/**
* Gets the raw internal index array. This array may contain extra elements
* beyond the number that are used. If it is greater than the number used,
* the remaining indices will be 0. Since this vector can resize its
* internal data, if it is modified, this array may no longer represent the
* internal state.
*
* @return The internal array of indices, whose length is greater than or
* equal to the number of used elements. Indices in the array beyond
* the used elements are not valid indices since they are unused.
*/
public int[] getRawIndex() {
return index;
}
/**
* Gets the raw internal data array. This array may contain extra elements
* beyond the number that are used. If it is greater than the number used,
* the remaining indices will be 0. Since this vector can resize its
* internal data, if it is modified, this array may no longer represent the
* internal state.
*
* @return The internal array of values, whose length is greater than or
* equal to the number of used elements. Values in the array beyond
* the used elements are not valid since they are unused.
*/
public double[] getRawData() {
return data;
}
/**
* Number of entries used in the sparse structure.
*/
public int getUsed() {
return used;
}
/**
* Compacts the vector.
*/
public void compact() {
int nz = Matrices.cardinality(this); // catches zero entries
if (nz < data.length) {
int[] newIndex = new int[nz];
double[] newData = new double[nz];
// Copy only non-zero entries
for (int i = 0, j = 0; i < data.length; ++i)
if (data[i] != 0.) {
newIndex[j] = index[i];
newData[j] = data[i];
j++;
}
data = newData;
index = newIndex;
used = data.length;
}
}
@Override
public Iterator iterator() {
return new SparseVectorIterator();
}
@Override
public Vector set(Vector y) {
if (!(y instanceof SparseVector))
return super.set(y);
checkSize(y);
SparseVector yc = (SparseVector) y;
if (yc.index.length != index.length) {
data = new double[yc.data.length];
index = new int[yc.data.length];
}
System.arraycopy(yc.data, 0, data, 0, data.length);
System.arraycopy(yc.index, 0, index, 0, index.length);
used = yc.used;
return this;
}
/**
* Iterator over a sparse vector
*/
private final class SparseVectorIterator implements Iterator {
private int cursor;
private final SparseVectorEntry entry = new SparseVectorEntry();
public boolean hasNext() {
return cursor < used;
}
public VectorEntry next() {
entry.update(cursor);
cursor++;
return entry;
}
public void remove() {
entry.set(0);
}
}
/**
* Entry of a sparse vector
*/
private final class SparseVectorEntry implements VectorEntry {
private int cursor;
public void update(int cursor) {
this.cursor = cursor;
}
public int index() {
return index[cursor];
}
public double get() {
return data[cursor];
}
public void set(double value) {
data[cursor] = value;
}
}
}