/* * Copyright 2011-2014, by Vladimir Kostyukov and Contributors. * * This file is part of la4j project (http://la4j.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributor(s): - * */ package org.la4j.vector; import java.text.NumberFormat; import java.util.Collection; import java.util.Map; import java.util.Random; import org.la4j.iterator.VectorIterator; import org.la4j.Matrix; import org.la4j.matrix.sparse.CRSMatrix; import org.la4j.matrix.ColumnMajorSparseMatrix; import org.la4j.matrix.RowMajorSparseMatrix; import org.la4j.Vector; import org.la4j.Vectors; import org.la4j.vector.functor.VectorAccumulator; import org.la4j.vector.functor.VectorProcedure; import org.la4j.operation.VectorMatrixOperation; import org.la4j.operation.VectorOperation; import org.la4j.operation.VectorVectorOperation; import org.la4j.vector.sparse.CompressedVector; /** * A sparse vector. * * A vector represents an array of elements. It can be re-sized. * * A sparse data structure does not store blank elements, and instead just stores * elements with values. A sparse data structure can be initialized with a large * length but take up no storage until the space is filled with non-zero elements. * * However, there is a performance cost. Fetch/store operations take O(log n) * time instead of the O(1) time of a dense data structure. * */ public abstract class SparseVector extends Vector { protected int cardinality; public SparseVector(int length) { this(length, 0); } public SparseVector(int length, int cardinality) { super(length); this.cardinality = cardinality; } /** * Creates a zero {@link SparseVector} of the given {@code length}. */ public static SparseVector zero(int length) { return CompressedVector.zero(length); } /** * Creates a zero {@link SparseVector} of the given {@code length} with * the given {@code capacity}. */ public static SparseVector zero(int length, int capacity) { return CompressedVector.zero(length); } /** * Creates a constant {@link SparseVector} of the given {@code length} with * the given {@code value}. */ public static SparseVector random(int length, double density, Random random) { return CompressedVector.random(length, density, random); } /** * Creates a new {@link SparseVector} from the given {@code array} with * compressing (copying) the underlying array. */ public static SparseVector fromArray(double[] array) { return CompressedVector.fromArray(array); } /** * Parses {@link SparseVector} from the given CSV string. * * @param csv the CSV string representing a vector * * @return a parsed vector */ public static SparseVector fromCSV(String csv) { return Vector.fromCSV(csv).to(Vectors.SPARSE); } /** * Parses {@link SparseVector} from the given Matrix Market string. * * @param mm the string in Matrix Market format * * @return a parsed vector */ public static SparseVector fromMatrixMarket(String mm) { return Vector.fromMatrixMarket(mm).to(Vectors.SPARSE); } /** * Creates new {@link SparseVector} from collection * * @param list value list * * @return created vector */ public static SparseVector fromCollection(Collection<? extends Number> list) { return Vector.fromCollection(list).to(Vectors.SPARSE); } /** * Creates new {@link SparseVector} from given index-value map * * @param map * * @return */ public static SparseVector fromMap(Map<Integer, ? extends Number> map, int length) { return CompressedVector.fromMap(map, length); } /** * Returns the cardinality (the number of non-zero elements) * of this sparse vector. * * @return the cardinality of this vector */ public int cardinality() { return cardinality; } /** * Returns the density (non-zero elements divided by total elements) * of this sparse vector. * * @return the density of this vector */ public double density() { return cardinality / (double) length; } @Override public double get(int i) { return getOrElse(i, 0.0); } /** * Gets the specified element, or a {@code defaultValue} if there * is no actual element at index {@code i} in this sparse vector. * * @param i the element's index * @param defaultValue the default value * * @return the element of this vector or a default value */ public abstract double getOrElse(int i, double defaultValue); /** * Whether or not the specified element is zero. * * @param i element's index * * @return {@code true} if specified element is zero, {@code false} otherwise */ public boolean isZeroAt(int i) { return !nonZeroAt(i); } /** * * Whether or not the specified element is not zero. * * @param i element's index * * @return {@code true} if specified element is zero, {@code false} otherwise */ public abstract boolean nonZeroAt(int i); /** * Folds non-zero elements of this vector with given {@code accumulator}. * * @param accumulator the vector accumulator * * @return the accumulated value */ public double foldNonZero(VectorAccumulator accumulator) { eachNonZero(Vectors.asAccumulatorProcedure(accumulator)); return accumulator.accumulate(); } /** * Applies given {@code procedure} to each non-zero element of this vector. * * @param procedure the vector procedure */ public void eachNonZero(VectorProcedure procedure) { VectorIterator it = nonZeroIterator(); while (it.hasNext()) { double x = it.next(); int i = it.index(); procedure.apply(i, x); } } @Override public Vector add(double value) { Vector result = DenseVector.constant(length, value); VectorIterator it = nonZeroIterator(); while (it.hasNext()) { double x = it.next(); int i = it.index(); result.set(i, x + value); } return result; } @Override public Vector multiply(double value) { Vector result = blank(); VectorIterator it = nonZeroIterator(); while (it.hasNext()) { double x = it.next(); int i = it.index(); result.set(i, x * value); } return result; } @Override public double max() { double max = foldNonZero(Vectors.mkMaxAccumulator()); return (max > 0.0) ? max : 0.0; } @Override public double min() { double min = foldNonZero(Vectors.mkMinAccumulator()); return (min < 0.0) ? min : 0.0; } @Override public double euclideanNorm() { return foldNonZero(Vectors.mkEuclideanNormAccumulator()); } @Override public double manhattanNorm() { return foldNonZero(Vectors.mkManhattanNormAccumulator()); } @Override public double infinityNorm() { double norm = foldNonZero(Vectors.mkInfinityNormAccumulator()); return (norm > 0.0) ? norm : 0.0; } /** * Returns a non-zero vector iterator. * * @return a non-zero vector iterator */ public abstract VectorIterator nonZeroIterator(); @Override public <T extends Vector> T to(VectorFactory<T> factory) { T result = factory.apply(length); VectorIterator it = nonZeroIterator(); while (it.hasNext()) { double x = it.next(); int i = it.index(); result.set(i, x); } return result; } @Override public int hashCode() { int result = 17; VectorIterator it = nonZeroIterator(); while (it.hasNext()) { long x = it.next().longValue(); long i = (long) it.index(); result = 37 * result + (int) (x ^ (x >>> 32)); result = 37 * result + (int) (i ^ (i >>> 32)); } return result; } @Override public <T> T apply(VectorOperation<T> operation) { operation.ensureApplicableTo(this); return operation.apply(this); } @Override public <T> T apply(VectorVectorOperation<T> operation, Vector that) { return that.apply(operation.partiallyApply(this)); } @Override public <T> T apply(VectorMatrixOperation<T> operation, Matrix that) { return that.apply(operation.partiallyApply(this)); } @Override public Matrix toRowMatrix() { VectorIterator it = nonZeroIterator(); Matrix result = CRSMatrix.zero(1, length); while (it.hasNext()) { double x = it.next(); int j = it.index(); result.set(0, j, x); } return result; } @Override public Matrix toColumnMatrix() { VectorIterator it = nonZeroIterator(); Matrix result = ColumnMajorSparseMatrix.zero(length, 1); while (it.hasNext()) { double x = it.next(); int i = it.index(); result.set(i, 0, x); } return result; } @Override public Matrix toDiagonalMatrix() { VectorIterator it = nonZeroIterator(); Matrix result = RowMajorSparseMatrix.zero(length, length); while (it.hasNext()) { double x = it.next(); int i = it.index(); result.set(i, i, x); } return result; } @Override public String toMatrixMarket(NumberFormat formatter) { StringBuilder out = new StringBuilder(); VectorIterator it = nonZeroIterator(); out.append("%%MatrixMarket vector coordinate real\n"); out.append(length).append(' ').append(cardinality).append('\n'); while (it.hasNext()) { double x = it.next(); int i = it.index(); out.append(i + 1).append(' ').append(formatter.format(x)).append('\n'); } return out.toString(); } /** * Ensures the provided index is in the bounds of this {@link SparseVector}. * * @param i The index to check. */ protected void ensureIndexIsInBounds(int i) { if (i < 0 || i >= length) { throw new IndexOutOfBoundsException("Index '" + i + "' is invalid."); } } }