package mikera.vectorz.impl;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import mikera.indexz.Index;
import mikera.matrixx.AMatrix;
import mikera.matrixx.impl.AVectorMatrix;
import mikera.matrixx.impl.SparseColumnMatrix;
import mikera.matrixx.impl.SparseRowMatrix;
import mikera.vectorz.AVector;
import mikera.vectorz.Op;
import mikera.vectorz.Op2;
import mikera.vectorz.Tools;
import mikera.vectorz.Vector;
import mikera.vectorz.Vectorz;
import mikera.vectorz.util.DoubleArrays;
import mikera.vectorz.util.ErrorMessages;
import mikera.vectorz.util.IntArrays;
import mikera.vectorz.util.VectorzException;
/**
* Indexed sparse vector. Efficient for mutable, mostly sparse vectors.
*
* Maintains a indexed array of elements which may be non-zero. These values
* *can* also be zero.
*
* WARNING: individual updates of non-indexed elements are O(n) in the number of
* non-sparse elements. You should not normally perform element-wise mutation on
* a SparseIndexedVector if performance is a concern
*
* Index must be distinct and sorted.
*
* @author Mike
*
*/
public class SparseIndexedVector extends ASparseIndexedVector {
private static final long serialVersionUID = 750093598603613879L;
/**
* double[] array containing the non-sparse values for this indexed vector
* index contains the indexes of the vector that are non-sparse
* Both can be modified, enabling arbitrary mutation of this vector
*/
private double[] data;
private Index index;
private SparseIndexedVector(int length, Index index) {
this(length, index, new double[index.length()]);
}
private SparseIndexedVector(int length, Index index, double[] data) {
super(length);
this.index = index;
this.data = data;
}
private SparseIndexedVector(int length, Index index, AVector source) {
this(length, index, source.toDoubleArray());
}
/**
* Creates a SparseIndexedVector with the specified index and data values.
* Performs no checking - Index must be distinct and sorted.
*/
public static SparseIndexedVector wrap(int length, Index index, double[] data) {
assert (index.length() == data.length);
assert (index.isDistinctSorted());
return new SparseIndexedVector(length, index, data);
}
/**
* Creates a SparseIndexedVector with the specified index and data values.
* Performs no checking - Index must be distinct and sorted.
*/
public static SparseIndexedVector wrap(int length, int[] indices, double[] data) {
Index index = Index.wrap(indices);
assert (index.length() == data.length);
assert (index.isDistinctSorted());
return new SparseIndexedVector(length, index, data);
}
/**
* Creates a SparseIndexedVector using the given sorted Index to identify
* the indexes of non-zero values, and a double[] array to specify all the
* non-zero element values
*/
public static SparseIndexedVector create(int length, Index index, double[] data) {
if (!index.isDistinctSorted()) {
throw new VectorzException("Index must be sorted and distinct");
}
if (!(index.length() == data.length)) {
throw new VectorzException("Length of index: mismatch woth data");
}
return new SparseIndexedVector(length, index.clone(), DoubleArrays.copyOf(data));
}
/**
* Creates a sparse indexed vector using the specified indexed values from
* the source vector
*
* All non-indexed vales will be zero.
*
* @param v
* @param ixs
* @return
*/
public static AVector createWithIndices(AVector v, int[] ixs) {
int length = v.length();
int n = ixs.length;
double[] data = new double[n];
v.getElements(data, 0, ixs);
return wrap(length, ixs, data);
}
public static SparseIndexedVector createLength(int length) {
return new SparseIndexedVector(length, Index.EMPTY, DoubleArrays.EMPTY);
}
/**
* Creates a SparseIndexedVector using the given sorted Index to identify
* the indexes of non-zero values, and a dense vector to specify all the
* non-zero element values
*/
public static SparseIndexedVector create(int length, Index index, AVector data) {
SparseIndexedVector sv = create(length, index, new double[index.length()]);
data.getElements(sv.data, 0);
return sv;
}
/**
* Creates a SparseIndexedVector from the source object
* @param o
* @return
*/
@SuppressWarnings("unchecked")
public static SparseIndexedVector create(Object o) {
if (o instanceof AVector) {
return create((AVector)o);
} else if (o instanceof List) {
return create((Iterable>)o);
} else if (o.getClass().isArray()) {
return create(Arrays.asList((Object[])o));
} else if (o instanceof Iterable){
return create((Iterable)o);
} else if (o instanceof Iterator){
return create((Iterator)o);
}
Class> klass=o.getClass();
if (klass.isArray()) {
Class> ec=klass.getComponentType();
if (ec!=null) {
if (ec.isPrimitive()) {
if (ec==Double.TYPE) return create((double[])o);
}
}
}
throw new IllegalArgumentException(ErrorMessages.cantCreateVector(o));
}
/**
* Creates a SparseIndexedVector from an array of double values
* @param o
* @return
*/
public static SparseIndexedVector create(double[] values) {
int n=values.length;
GrowableIndexedVector gv=GrowableIndexedVector.createLength(n);
for (int i=0; i iterable) {
GrowableIndexedVector v=GrowableIndexedVector.create(iterable);
return v.toSparseIndexedVector();
}
/**
* Creates a SparseIndexedVector from the specified Iterator object
* @param iterator An Iterator over java.lang.Number instances
* @return
*/
public static SparseIndexedVector create(Iterator> iterator) {
GrowableIndexedVector v=GrowableIndexedVector.create(iterator);
return v.toSparseIndexedVector();
}
/** Creates a SparseIndexedVector from a row of an existing matrix */
public static AVector createFromRow(AMatrix m, int row) {
if (m instanceof AVectorMatrix)
return create(m.getRow(row));
return create(m.getRow(row));
}
@Override
public int nonSparseElementCount() {
return data.length;
}
/**
* Overloaded implementation for SparseIndexedVector x SparseRowMatrix
*
* This is basically a weighted sum of rows!
*
* @param m
* @return
*/
public AVector innerProduct(SparseRowMatrix m) {
int cc = m.columnCount();
int rc = m.rowCount();
checkLength(rc);
int n = nonSparseElementCount();
AVector result= SparseIndexedVector.createLength(cc); // inistially use sparse result
for (int ii = 0; ii < n; ii++) {
double value = data[ii];
if (value == 0.0)
continue; // skip zero values
int i = index.get(ii);
AVector row = m.unsafeGetVector(i);
if (row == null)
continue; // skip zero rows
result.addMultiple(row, value);
if ((result instanceof SparseIndexedVector)&&
((SparseIndexedVector)result).nonSparseElementCount()>(cc/4)) {
result=result.toVector();
}
}
return result;
}
public AVector innerProduct(SparseColumnMatrix m) {
int cc = m.columnCount();
int rc = m.rowCount();
int nzc = 0;
checkLength(rc);
AVector result = SparseIndexedVector.createLength(cc);
for (int i = 0; i < rc; i++) {
double v=this.dotProduct(m.getColumn(i));
if (v==0.0) continue;
nzc++;
if ((result instanceof SparseIndexedVector)&&(i>20)&&(nzc>(i/4))) {
// switch to dense vector if result becoming near-dense
result=result.toVector();
}
result.unsafeSet(i, v);
}
return result;
}
@Override
public AVector innerProduct(AMatrix m) {
if (m instanceof SparseRowMatrix) {
return this.innerProduct((SparseRowMatrix) m);
}
if (m instanceof SparseColumnMatrix) {
return this.innerProduct((SparseColumnMatrix) m);
}
int cc = m.columnCount();
int rc = m.rowCount();
checkLength(rc);
AVector result = SparseIndexedVector.createLength(cc);
int nzc = 0;
for (int i = 0; i < cc; i++) {
double v=this.dotProduct(m.getColumn(i));
if (v==0.0) continue;
nzc++;
if ((i>20)&&(nzc>(i/4))) {
// switch to dense vector
result=result.toVector();
}
result.unsafeSet(i, v);
}
return result;
}
@Override
public void add(AVector v) {
if (v instanceof ASparseVector) {
add((ASparseVector) v);
return;
}
includeIndices(v);
for (int i = 0; i < data.length; i++) {
data[i] += v.unsafeGet(index.get(i));
}
}
@Override
public void addMultiple(AVector v, double factor) {
if (factor == 0.0)
return;
if (v instanceof ASparseVector) {
addMultiple((ASparseVector) v, factor);
return;
}
super.addMultiple(v, factor);
}
@Override
public void addMultipleSparse(AVector v, double factor) {
if (v instanceof ADenseArrayVector) {
addMultipleSparse((ADenseArrayVector)v,factor);
} else if (v instanceof SparseIndexedVector) {
addMultipleSparse((SparseIndexedVector)v,factor);
} else {
checkSameLength(v);
int n=data.length;
double[] ds=new double[n];
v.getElements(ds, 0, index.data);
DoubleArrays.addMultiple(data, ds, factor);
}
}
/**
* Adds a multiple of the source array to the non-sparse elements of this array
*/
public void addMultipleSparse(ADenseArrayVector v, double factor) {
checkSameLength(v);
int n=data.length;
int[] ix=index.data;
int voffset=v.getArrayOffset();
double[] vdata=v.getArray();
for (int i=0; i result)
result = d;
}
return result;
}
@Override
public int maxElementIndex() {
double[] data = this.data;
if (data.length == 0)
return 0;
double result = data[0];
int di = 0;
for (int i = 1; i < data.length; i++) {
double d = data[i];
if (d > result) {
result = d;
di = i;
}
}
if (result < 0.0) { // see if we can find a zero element
int ind = index.findMissing();
if (ind > 0)
return ind;
}
return index.get(di);
}
@Override
public int maxAbsElementIndex() {
double[] data = this.data;
if (data.length == 0)
return 0;
double result = data[0];
int di = 0;
for (int i = 1; i < data.length; i++) {
double d = Math.abs(data[i]);
if (d > result) {
result = d;
di = i;
}
}
return index.get(di);
}
@Override
public int minElementIndex() {
double[] data = this.data;
if (data.length == 0)
return 0;
double result = data[0];
int di = 0;
for (int i = 1; i < data.length; i++) {
double d = data[i];
if (d < result) {
result = d;
di = i;
}
}
if (result > 0.0) { // see if we can find a zero element
int ind = index.findMissing();
if (ind >= 0)
return ind;
}
return index.get(di);
}
@Override
public void negate() {
DoubleArrays.negate(data);
}
@Override
public void applyOp(Op op) {
int dlen = data.length;
if ((dlen < length()) && (op.isStochastic() || (op.apply(0.0) != 0.0))) {
super.applyOp(op);
} else {
op.applyTo(data);
}
}
@Override
public void applyOp(Op2 op, double d) {
int dlen=data.length;
if ((dlen 0;
}
@Override
public void setElements(double[] array, int offset) {
int nz = DoubleArrays.nonZeroCount(array, offset, length);
int[] ixs = new int[nz];
double[] data = new double[nz];
this.data = data;
int di = 0;
for (int i = 0; i < length; i++) {
double x = array[offset + i];
if (x == 0.0)
continue;
ixs[di] = i;
data[di] = x;
di++;
}
index = Index.wrap(ixs);
}
@Override
public void setElements(int pos, double[] array, int offset, int length) {
if (length >= this.length) {
setElements(array, offset);
return;
}
int nz = DoubleArrays.nonZeroCount(array, offset, length);
int[] ixs = new int[nz];
double[] data = new double[nz];
this.data = data;
int di = pos;
for (int i = 0; i < length; i++) {
double x = array[offset + i];
if (x == 0.0)
continue;
ixs[di] = i;
data[di] = x;
di++;
}
index = Index.wrap(ixs);
}
@Override
public void set(AVector v) {
checkSameLength(v);
if (v instanceof ADenseArrayVector) {
set((ADenseArrayVector) v);
return;
} else if (v instanceof ASparseVector) {
int[] nzi = v.nonZeroIndices();
index = Index.wrap(nzi);
if (nzi.length != data.length) {
data = new double[nzi.length];
}
for (int i = 0; i < index.length(); i++) {
double val = v.unsafeGet(index.get(i));
data[i] = val;
}
return;
} else {
double[] data = this.data;
int nz = (int) v.nonZeroCount();
if (nz != data.length) {
data = new double[nz];
this.data = data;
index = Index.createLength(nz);
}
int di = 0;
for (int i = 0; i < nz; i++) {
double val = v.unsafeGet(i);
if (val != 0) {
data[di] = val;
index.set(di, i);
di++;
}
}
}
}
@Override
public void set(ADenseArrayVector v) {
checkSameLength(v);
setElements(v.getArray(), v.getArrayOffset());
}
@Override
public void set(int i, double value) {
checkIndex(i);
unsafeSet(i, value);
}
@Override
public void unsafeSet(int i, double value) {
int ip = index.indexPosition(i);
if (ip < 0) {
if (value == 0.0)
return;
int npos = index.seekPosition(i);
data = DoubleArrays.insert(data, npos, value);
index = index.insert(npos, i);
} else {
data[ip] = value;
}
}
@Override
public void addAt(int i, double value) {
if (value==0.0) return; // no change required
int ip=index.indexPosition(i);
if (ip<0) {
if (value==0.0) return;
int npos=index.seekPosition(i);
data=DoubleArrays.insert(data,npos,value);
index=index.insert(npos,i);
} else {
data[ip]+=value;
}
}
// TODO: consider a generic sparseApplyOp instead.
// keep in mind efficiency when randomly
// modifying index.
@Override
public ASparseVector roundToZero(double precision) {
int[] aboveInds = new int[data.length];
double[] aboveData = new double[data.length];
int ai = 0;
for (int i = 0; i < index.length(); i++) {
if (data[i] > precision) {
aboveInds[ai] = index.get(i);
aboveData[ai] = data[i];
ai++;
}
}
int[] newInds = new int[ai];
double[] newData = new double[ai];
System.arraycopy(aboveInds, 0, newInds, 0, ai);
System.arraycopy(aboveData, 0, newData, 0, ai);
return SparseIndexedVector.wrap(this.length, newInds, newData);
}
@Override
public Vector nonSparseValues() {
return Vector.wrap(data);
}
@Override
public Index nonSparseIndex() {
return index;
}
@Override
public double visitNonZero(IndexedElementVisitor elementVisitor) {
Index ix=index;
int cnt=ix.length();
if (cnt==0) return 0.0;
for (int k=0; k= data.length)
break;
int z = index.data[si];
if (z == nixs[i]) {
ndata[i] = data[si];
si++;
}
}
this.data = ndata;
index = Index.wrap(nixs);
}
/**
* Include additional indices in the non-sparse index set of this vector.
*
* Useful to improve performance if subsequent operations will access these
* indices.
*
* @param ixs
*/
public void includeIndices(Index ixs) {
includeIndices(ixs.data);
}
/**
* Include additional indices in the non-sparse index set of this vector.
*
* Useful to improve performance if subsequent operations will access these
* indices.
*
* @param ixs
* @return the newly included indices from the vector v
*/
public void includeIndices(AVector v) {
if (v instanceof ASparseIndexedVector) {
includeIndices((ASparseIndexedVector) v);
} else {
includeIndices(v.nonSparseIndex());
}
}
public void includeIndices(ASparseIndexedVector v) {
includeIndices(v.internalIndex());
}
@Override
public SparseIndexedVector sparseClone() {
return exactClone();
}
@Override
public SparseIndexedVector exactClone() {
return new SparseIndexedVector(length, index.clone(), data.clone());
}
@Override
public void validate() {
if (index.length() != data.length)
throw new VectorzException("Inconsistent data and index!");
if (!index.isDistinctSorted())
throw new VectorzException("Invalid index: " + index);
super.validate();
}
@Override
public AVector immutable() {
return SparseImmutableVector.create(this);
}
@Override
double[] internalData() {
return data;
}
@Override
Index internalIndex() {
return index;
}
}
Ads help maintain this website.