package mikera.vectorz.impl; import java.util.Arrays; import java.util.List; import mikera.arrayz.ISparse; import mikera.indexz.Index; import mikera.matrixx.AMatrix; import mikera.vectorz.AVector; import mikera.vectorz.util.VectorzException; import mikera.vectorz.util.ErrorMessages; import mikera.vectorz.Vectorz; /** * Abstract base class for Sparse vector implementations * @author Mike * */ @SuppressWarnings("serial") public abstract class ASparseVector extends ASizedVector implements ISparse { protected ASparseVector(int length) { super(length); } /** * Returns the number of non-sparse elements in the sparse vector. * @return */ public abstract int nonSparseElementCount(); /** * Returns the non-sparse values as a compacted vector view * @return */ public abstract AVector nonSparseValues(); @Override public abstract Index nonSparseIndex(); /** * Returns true iff the sparse vector contains the index i * @param i * @return */ public abstract boolean includesIndex(int i); /** * Replaces all values less-than-or-equal to precision with zeros. * @param precision * @return */ public ASparseVector roundToZero(double precision) { throw new VectorzException(ErrorMessages.notYetImplemented()); } // ================================================ // Superclass methods that must be overridden // (superclass implementation is bad for sparse arrays) @Override public void copyTo(int offset, double[] destData, int destOffset, int length) { Arrays.fill(destData, destOffset, destOffset+length, 0.0); addToArray(offset, destData, destOffset, length); } @Override public boolean isZero() { return nonZeroCount()==0L; } @Override public boolean isView() { return false; } // ================================================ // standard implementations @Override public double dotProduct(AVector v) { checkSameLength(v); double result=0.0; Index ni=nonSparseIndex(); for (int i=0; i<ni.length(); i++) { int ii=ni.get(i); result+=unsafeGet(ii)*v.unsafeGet(ii); } return result; } @Override public final double dotProduct(ADenseArrayVector v) { checkSameLength(v); double[] array=v.getArray(); int offset=v.getArrayOffset(); return dotProduct(array,offset); } @Override public AVector innerProduct(AMatrix m) { int cc=m.columnCount(); int rc=m.rowCount(); checkLength(rc); AVector r=Vectorz.createSparseMutable(cc); Index ni=nonSparseIndex(); for (int i=0; i<ni.length(); i++) { int ti=ni.get(i); double v=unsafeGet(ti); if (v!=0.0) r.addMultiple(m.getRow(ti),v); } return r; } @Override public final boolean isSparse() { return true; } @Override public void add(AVector v) { if (v instanceof ASparseVector) { add((ASparseVector)v); return; } super.add(v); } @Override public void addMultiple(AVector src, double factor) { add(src.multiplyCopy(factor)); } public abstract void add(ASparseVector v); // public abstract void sub(ASparseVector v); @Override public List<Double> getSlices() { // we prefer a ListWrapper for sparse vectors since it is O(1) to create. // downside: causes boxing on individual element accesses return new ListWrapper(this); } @Override public double elementProduct() { int n=nonSparseElementCount(); if (n<length) return 0.0; return nonSparseValues().elementProduct(); } @Override public ASparseVector sparse() { return this; } @Override public AVector clone() { // TODO: figure out a better heuristic? if ((length<20)||(nonSparseElementCount()>(elementCount()*0.25))) return super.clone(); return SparseIndexedVector.create(this); } public boolean equals(ASparseVector v) { if (v==this) return true; if (v.length!=length) return false; Index ni=nonSparseIndex(); for (int i=0; i<ni.length(); i++) { int ii=ni.get(i); if (unsafeGet(ii)!=v.unsafeGet(ii)) return false; } Index ri=v.nonSparseIndex(); for (int i=0; i<ri.length(); i++) { int ii=ri.get(i); if (unsafeGet(ii)!=v.unsafeGet(ii)) return false; } return true; } @Override public double[] toDoubleArray() { double[] data=new double[length]; addToArray(data,0); return data; } @Override public long nonZeroCount() { return nonSparseValues().nonZeroCount(); } @Override public boolean equals(AVector v) { if (v instanceof ASparseVector) { return equals((ASparseVector)v); } if (v.length()!=length) return false; Index ni=nonSparseIndex(); int n=ni.length(); AVector nv=nonSparseValues(); int offset=0; for (int i=0; i<n; i++) { int ii=ni.get(i); if (!v.isRangeZero(offset, ii-offset)) return false; if (nv.unsafeGet(i)!=v.unsafeGet(ii)) return false; offset=ii+1; } return v.isRangeZero(offset,length-offset); } @Override public boolean hasUncountable() { return nonSparseValues().hasUncountable(); } /** * Returns the sum of all the elements raised to a specified power * @return */ @Override public double elementPowSum(double p) { return nonSparseValues().elementPowSum(p); } /** * Returns the sum of the absolute values of all the elements raised to a specified power * @return */ @Override public double elementAbsPowSum(double p) { return nonSparseValues().elementAbsPowSum(p); } }