/* * 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 { /** * 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, * <code>x</code> must be a <code>SparseVector</code> */ 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 = Arrays.binarySearch(this.index, index, 0, used); 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<VectorEntry> 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 class SparseVectorIterator implements Iterator<VectorEntry> { 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 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; } } }