/** * 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.mahout.math; import java.util.Arrays; import java.util.Iterator; import com.google.common.collect.AbstractIterator; import org.apache.mahout.math.function.DoubleDoubleFunction; import org.apache.mahout.math.function.PlusMult; /** Implements vector as an array of doubles */ public class DenseVector extends AbstractVector { private double[] values; /** For serialization purposes only */ public DenseVector() { super(0); } /** Construct a new instance using provided values */ public DenseVector(double[] values) { this(values, false); } public DenseVector(double[] values, boolean shallowCopy) { super(values.length); this.values = shallowCopy ? values : values.clone(); } public DenseVector(DenseVector values, boolean shallowCopy) { this(values.values, shallowCopy); } /** Construct a new instance of the given cardinality */ public DenseVector(int cardinality) { super(cardinality); this.values = new double[cardinality]; } /** * Copy-constructor (for use in turning a sparse vector into a dense one, for example) * @param vector */ public DenseVector(Vector vector) { super(vector.size()); values = new double[vector.size()]; Iterator<Element> it = vector.iterateNonZero(); while (it.hasNext()) { Element e = it.next(); values[e.index()] = e.get(); } } @Override protected Matrix matrixLike(int rows, int columns) { return new DenseMatrix(rows, columns); } @Override public DenseVector clone() { return new DenseVector(values.clone()); } /** * @return true */ @Override public boolean isDense() { return true; } /** * @return true */ @Override public boolean isSequentialAccess() { return true; } @Override public double dotSelf() { double result = 0.0; int max = size(); for (int i = 0; i < max; i++) { double value = this.getQuick(i); result += value * value; } return result; } @Override public double getQuick(int index) { return values[index]; } @Override public DenseVector like() { return new DenseVector(size()); } @Override public void setQuick(int index, double value) { lengthSquared = -1.0; values[index] = value; } @Override public Vector assign(double value) { this.lengthSquared = -1; Arrays.fill(values, value); return this; } @Override public Vector assign(Vector other, DoubleDoubleFunction function) { if (size() != other.size()) { throw new CardinalityException(size(), other.size()); } // is there some other way to know if function.apply(0, x) = x for all x? if (function instanceof PlusMult) { Iterator<Element> it = other.iterateNonZero(); Element e; while (it.hasNext() && (e = it.next()) != null) { values[e.index()] = function.apply(values[e.index()], e.get()); } } else { for (int i = 0; i < size(); i++) { values[i] = function.apply(values[i], other.getQuick(i)); } } lengthSquared = -1; return this; } public Vector assign(DenseVector vector) { // make sure the data field has the correct length if (vector.values.length != this.values.length) { this.values = new double[vector.values.length]; } // now copy the values System.arraycopy(vector.values, 0, this.values, 0, this.values.length); return this; } @Override public int getNumNondefaultElements() { return values.length; } @Override public Vector viewPart(int offset, int length) { if (offset < 0) { throw new IndexException(offset, size()); } if (offset + length > size()) { throw new IndexException(offset + length, size()); } return new VectorView(this, offset, length); } /** * Returns an iterator that traverses this Vector from 0 to cardinality-1, in that order. */ @Override public Iterator<Element> iterateNonZero() { return new NonDefaultIterator(); } @Override public Iterator<Element> iterator() { return new AllIterator(); } @Override public boolean equals(Object o) { if (o instanceof DenseVector) { // Speedup for DenseVectors return Arrays.equals(values, ((DenseVector) o).values); } return super.equals(o); } @Override public double getLengthSquared() { if (lengthSquared >= 0.0) { return lengthSquared; } double result = 0.0; for (double value : values) { result += value * value; } lengthSquared = result; return result; } public void addAll(Vector v) { if (size() != v.size()) { throw new CardinalityException(size(), v.size()); } Iterator<Element> iter = v.iterateNonZero(); while (iter.hasNext()) { Element element = iter.next(); values[element.index()] += element.get(); } } private final class NonDefaultIterator extends AbstractIterator<Element> { private final DenseElement element = new DenseElement(); private int index = 0; @Override protected Element computeNext() { while (index < size() && values[index] == 0.0) { index++; } if (index < size()) { element.index = index; index++; return element; } else { return endOfData(); } } } private final class AllIterator extends AbstractIterator<Element> { private final DenseElement element = new DenseElement(); private AllIterator() { element.index = -1; } @Override protected Element computeNext() { if (element.index + 1 < size()) { element.index++; return element; } else { return endOfData(); } } } private final class DenseElement implements Element { int index; @Override public double get() { return values[index]; } @Override public int index() { return index; } @Override public void set(double value) { lengthSquared = -1; values[index] = value; } } }