/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.commons.math.linear;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.commons.math.exception.MathUnsupportedOperationException;
import org.apache.commons.math.exception.DimensionMismatchException;
import org.apache.commons.math.exception.OutOfRangeException;
import org.apache.commons.math.exception.MathArithmeticException;
import org.apache.commons.math.analysis.FunctionUtils;
import org.apache.commons.math.analysis.function.Add;
import org.apache.commons.math.analysis.function.Multiply;
import org.apache.commons.math.analysis.function.Divide;
import org.apache.commons.math.analysis.UnivariateRealFunction;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.util.FastMath;
/**
* This class provides default basic implementations for many methods in the
* {@link RealVector} interface.
*
* @version $Id: AbstractRealVector.java 1131229 2011-06-03 20:49:25Z luc $
* @since 2.1
*/
public abstract class AbstractRealVector implements RealVector {
/**
* Check if instance and specified vectors have the same dimension.
*
* @param v Vector to compare instance with.
* @throws DimensionMismatchException if the vectors do not
* have the same dimension.
*/
protected void checkVectorDimensions(RealVector v) {
checkVectorDimensions(v.getDimension());
}
/**
* Check if instance dimension is equal to some expected value.
*
* @param n Expected dimension.
* @throws DimensionMismatchException if the dimension is
* inconsistent with the vector size.
*/
protected void checkVectorDimensions(int n) {
int d = getDimension();
if (d != n) {
throw new DimensionMismatchException(d, n);
}
}
/**
* Check if an index is valid.
*
* @param index Index to check.
* @exception OutOfRangeException if {@code index} is not valid.
*/
protected void checkIndex(final int index) {
if (index < 0 ||
index >= getDimension()) {
throw new OutOfRangeException(LocalizedFormats.INDEX,
index, 0, getDimension() - 1);
}
}
/** {@inheritDoc} */
public void setSubVector(int index, RealVector v) {
checkIndex(index);
checkIndex(index + v.getDimension() - 1);
setSubVector(index, v.getData());
}
/** {@inheritDoc} */
public void setSubVector(int index, double[] v) {
checkIndex(index);
checkIndex(index + v.length - 1);
for (int i = 0; i < v.length; i++) {
setEntry(i + index, v[i]);
}
}
/** {@inheritDoc} */
public RealVector add(double[] v) {
double[] result = v.clone();
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
result[e.getIndex()] += e.getValue();
}
return new ArrayRealVector(result, false);
}
/** {@inheritDoc} */
public RealVector add(RealVector v) {
if (v instanceof ArrayRealVector) {
double[] values = ((ArrayRealVector)v).getDataRef();
return add(values);
}
RealVector result = v.copy();
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
final int index = e.getIndex();
result.setEntry(index, e.getValue() + result.getEntry(index));
}
return result;
}
/** {@inheritDoc} */
public RealVector subtract(double[] v) {
double[] result = v.clone();
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
final int index = e.getIndex();
result[index] = e.getValue() - result[index];
}
return new ArrayRealVector(result, false);
}
/** {@inheritDoc} */
public RealVector subtract(RealVector v) {
if (v instanceof ArrayRealVector) {
double[] values = ((ArrayRealVector)v).getDataRef();
return add(values);
}
RealVector result = v.copy();
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
final int index = e.getIndex();
v.setEntry(index, e.getValue() - result.getEntry(index));
}
return result;
}
/** {@inheritDoc} */
public RealVector mapAdd(double d) {
return copy().mapAddToSelf(d);
}
/** {@inheritDoc} */
public RealVector mapAddToSelf(double d) {
if (d != 0) {
return mapToSelf(FunctionUtils.fix2ndArgument(new Add(), d));
}
return this;
}
/** {@inheritDoc} */
public abstract AbstractRealVector copy();
/** {@inheritDoc} */
public double dotProduct(double[] v) {
return dotProduct(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public double dotProduct(RealVector v) {
checkVectorDimensions(v);
double d = 0;
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
d += e.getValue() * v.getEntry(e.getIndex());
}
return d;
}
/** {@inheritDoc} */
public double cosine(RealVector v) {
final double norm = getNorm();
final double vNorm = v.getNorm();
if (norm == 0 ||
vNorm == 0) {
throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
}
return dotProduct(v) / (norm * vNorm);
}
/** {@inheritDoc} */
public double cosine(double[] v) {
return cosine(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public RealVector ebeDivide(double[] v) {
return ebeDivide(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public RealVector ebeMultiply(double[] v) {
return ebeMultiply(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public double getDistance(RealVector v) {
checkVectorDimensions(v);
double d = 0;
Iterator<Entry> it = iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
final double diff = e.getValue() - v.getEntry(e.getIndex());
d += diff * diff;
}
return FastMath.sqrt(d);
}
/** {@inheritDoc} */
public double getNorm() {
double sum = 0;
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
final double value = e.getValue();
sum += value * value;
}
return FastMath.sqrt(sum);
}
/** {@inheritDoc} */
public double getL1Norm() {
double norm = 0;
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
norm += FastMath.abs(e.getValue());
}
return norm;
}
/** {@inheritDoc} */
public double getLInfNorm() {
double norm = 0;
Iterator<Entry> it = sparseIterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
norm = FastMath.max(norm, FastMath.abs(e.getValue()));
}
return norm;
}
/** {@inheritDoc} */
public double getDistance(double[] v) {
return getDistance(new ArrayRealVector(v,false));
}
/** {@inheritDoc} */
public double getL1Distance(RealVector v) {
checkVectorDimensions(v);
double d = 0;
Iterator<Entry> it = iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
d += FastMath.abs(e.getValue() - v.getEntry(e.getIndex()));
}
return d;
}
/** {@inheritDoc} */
public double getL1Distance(double[] v) {
checkVectorDimensions(v.length);
double d = 0;
Iterator<Entry> it = iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
d += FastMath.abs(e.getValue() - v[e.getIndex()]);
}
return d;
}
/** {@inheritDoc} */
public double getLInfDistance(RealVector v) {
checkVectorDimensions(v);
double d = 0;
Iterator<Entry> it = iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
d = FastMath.max(FastMath.abs(e.getValue() - v.getEntry(e.getIndex())), d);
}
return d;
}
/** {@inheritDoc} */
public double getLInfDistance(double[] v) {
checkVectorDimensions(v.length);
double d = 0;
Iterator<Entry> it = iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
d = FastMath.max(FastMath.abs(e.getValue() - v[e.getIndex()]), d);
}
return d;
}
/** Get the index of the minimum entry.
* @return index of the minimum entry or -1 if vector length is 0
* or all entries are NaN
*/
public int getMinIndex() {
int minIndex = -1;
double minValue = Double.POSITIVE_INFINITY;
Iterator<Entry> iterator = iterator();
while (iterator.hasNext()) {
final Entry entry = iterator.next();
if (entry.getValue() <= minValue) {
minIndex = entry.getIndex();
minValue = entry.getValue();
}
}
return minIndex;
}
/** Get the value of the minimum entry.
* @return value of the minimum entry or NaN if all entries are NaN
*/
public double getMinValue() {
final int minIndex = getMinIndex();
return minIndex < 0 ? Double.NaN : getEntry(minIndex);
}
/** Get the index of the maximum entry.
* @return index of the maximum entry or -1 if vector length is 0
* or all entries are NaN
*/
public int getMaxIndex() {
int maxIndex = -1;
double maxValue = Double.NEGATIVE_INFINITY;
Iterator<Entry> iterator = iterator();
while (iterator.hasNext()) {
final Entry entry = iterator.next();
if (entry.getValue() >= maxValue) {
maxIndex = entry.getIndex();
maxValue = entry.getValue();
}
}
return maxIndex;
}
/** Get the value of the maximum entry.
* @return value of the maximum entry or NaN if all entries are NaN
*/
public double getMaxValue() {
final int maxIndex = getMaxIndex();
return maxIndex < 0 ? Double.NaN : getEntry(maxIndex);
}
/** {@inheritDoc} */
public RealVector mapMultiply(double d) {
return copy().mapMultiplyToSelf(d);
}
/** {@inheritDoc} */
public RealVector mapMultiplyToSelf(double d){
return mapToSelf(FunctionUtils.fix2ndArgument(new Multiply(), d));
}
/** {@inheritDoc} */
public RealVector mapSubtract(double d) {
return copy().mapSubtractToSelf(d);
}
/** {@inheritDoc} */
public RealVector mapSubtractToSelf(double d){
return mapAddToSelf(-d);
}
/** {@inheritDoc} */
public RealVector mapDivide(double d) {
return copy().mapDivideToSelf(d);
}
/** {@inheritDoc} */
public RealVector mapDivideToSelf(double d){
return mapToSelf(FunctionUtils.fix2ndArgument(new Divide(), d));
}
/** {@inheritDoc} */
public RealMatrix outerProduct(RealVector v) {
RealMatrix product;
if (v instanceof SparseRealVector || this instanceof SparseRealVector) {
product = new OpenMapRealMatrix(this.getDimension(),
v.getDimension());
} else {
product = new Array2DRowRealMatrix(this.getDimension(),
v.getDimension());
}
Iterator<Entry> thisIt = sparseIterator();
Entry thisE = null;
while (thisIt.hasNext() && (thisE = thisIt.next()) != null) {
Iterator<Entry> otherIt = v.sparseIterator();
Entry otherE = null;
while (otherIt.hasNext() && (otherE = otherIt.next()) != null) {
product.setEntry(thisE.getIndex(), otherE.getIndex(),
thisE.getValue() * otherE.getValue());
}
}
return product;
}
/** {@inheritDoc} */
public RealMatrix outerProduct(double[] v) {
return outerProduct(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public RealVector projection(double[] v) {
return projection(new ArrayRealVector(v, false));
}
/** {@inheritDoc} */
public void set(double value) {
Iterator<Entry> it = iterator();
Entry e = null;
while (it.hasNext() && (e = it.next()) != null) {
e.setValue(value);
}
}
/** {@inheritDoc} */
public double[] toArray() {
int dim = getDimension();
double[] values = new double[dim];
for (int i = 0; i < dim; i++) {
values[i] = getEntry(i);
}
return values;
}
/** {@inheritDoc} */
public double[] getData() {
return toArray();
}
/** {@inheritDoc} */
public RealVector unitVector() {
RealVector copy = copy();
copy.unitize();
return copy;
}
/** {@inheritDoc} */
public void unitize() {
mapDivideToSelf(getNorm());
}
/** {@inheritDoc} */
public Iterator<Entry> sparseIterator() {
return new SparseEntryIterator();
}
/** {@inheritDoc} */
public Iterator<Entry> iterator() {
final int dim = getDimension();
return new Iterator<Entry>() {
/** Current index. */
private int i = 0;
/** Current entry. */
private EntryImpl e = new EntryImpl();
/** {@inheritDoc} */
public boolean hasNext() {
return i < dim;
}
/** {@inheritDoc} */
public Entry next() {
e.setIndex(i++);
return e;
}
/** {@inheritDoc} */
public void remove() {
throw new MathUnsupportedOperationException();
}
};
}
/** {@inheritDoc} */
public RealVector map(UnivariateRealFunction function) {
return copy().mapToSelf(function);
}
/** {@inheritDoc} */
public RealVector mapToSelf(UnivariateRealFunction function) {
Iterator<Entry> it = (function.value(0) == 0) ? sparseIterator() : iterator();
Entry e;
while (it.hasNext() && (e = it.next()) != null) {
e.setValue(function.value(e.getValue()));
}
return this;
}
/** An entry in the vector. */
protected class EntryImpl extends Entry {
/** Simple constructor. */
public EntryImpl() {
setIndex(0);
}
/** {@inheritDoc} */
@Override
public double getValue() {
return getEntry(getIndex());
}
/** {@inheritDoc} */
@Override
public void setValue(double newValue) {
setEntry(getIndex(), newValue);
}
}
/**
* This class should rare be used, but is here to provide
* a default implementation of sparseIterator(), which is implemented
* by walking over the entries, skipping those whose values are the default one.
*
* Concrete subclasses which are SparseVector implementations should
* make their own sparse iterator, not use this one.
*
* This implementation might be useful for ArrayRealVector, when expensive
* operations which preserve the default value are to be done on the entries,
* and the fraction of non-default values is small (i.e. someone took a
* SparseVector, and passed it into the copy-constructor of ArrayRealVector)
*/
protected class SparseEntryIterator implements Iterator<Entry> {
/** Dimension of the vector. */
private final int dim;
/** last entry returned by {@link #next()} */
private EntryImpl current;
/** Next entry for {@link #next()} to return. */
private EntryImpl next;
/** Simple constructor. */
protected SparseEntryIterator() {
dim = getDimension();
current = new EntryImpl();
next = new EntryImpl();
if (next.getValue() == 0) {
advance(next);
}
}
/** Advance an entry up to the next nonzero one.
* @param e entry to advance
*/
protected void advance(EntryImpl e) {
if (e == null) {
return;
}
do {
e.setIndex(e.getIndex() + 1);
} while (e.getIndex() < dim && e.getValue() == 0);
if (e.getIndex() >= dim) {
e.setIndex(-1);
}
}
/** {@inheritDoc} */
public boolean hasNext() {
return next.getIndex() >= 0;
}
/** {@inheritDoc} */
public Entry next() {
int index = next.getIndex();
if (index < 0) {
throw new NoSuchElementException();
}
current.setIndex(index);
advance(next);
return current;
}
/** {@inheritDoc} */
public void remove() {
throw new MathUnsupportedOperationException();
}
}
}