/**
* 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.Iterator;
import com.google.common.collect.AbstractIterator;
import org.apache.mahout.math.list.IntArrayList;
import org.apache.mahout.math.map.OpenIntDoubleHashMap;
/** Implements vector that only stores non-zero doubles */
public class RandomAccessSparseVector extends AbstractVector {
private static final int INITIAL_CAPACITY = 11;
private OpenIntDoubleHashMap values;
/** For serialization purposes only. */
public RandomAccessSparseVector() {
super(0);
}
public RandomAccessSparseVector(int cardinality) {
this(cardinality, Math.min(cardinality, INITIAL_CAPACITY)); // arbitrary estimate of 'sparseness'
}
public RandomAccessSparseVector(int cardinality, int initialCapacity) {
super(cardinality);
values = new OpenIntDoubleHashMap(initialCapacity);
}
public RandomAccessSparseVector(Vector other) {
this(other.size(), other.getNumNondefaultElements());
Iterator<Element> it = other.iterateNonZero();
Element e;
while (it.hasNext() && (e = it.next()) != null) {
values.put(e.index(), e.get());
}
}
private RandomAccessSparseVector(int cardinality, OpenIntDoubleHashMap values) {
super(cardinality);
this.values = values;
}
public RandomAccessSparseVector(RandomAccessSparseVector other, boolean shallowCopy) {
super(other.size());
values = shallowCopy ? other.values : (OpenIntDoubleHashMap)other.values.clone();
}
@Override
protected Matrix matrixLike(int rows, int columns) {
return new SparseRowMatrix(rows, columns);
}
@Override
public RandomAccessSparseVector clone() {
return new RandomAccessSparseVector(size(), (OpenIntDoubleHashMap) values.clone());
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append('{');
Iterator<Element> it = iterateNonZero();
boolean first = true;
while (it.hasNext()) {
if (first) {
first = false;
} else {
result.append(',');
}
Element e = it.next();
result.append(e.index());
result.append(':');
result.append(e.get());
}
result.append('}');
return result.toString();
}
@Override
public Vector assign(Vector other) {
if (size() != other.size()) {
throw new CardinalityException(size(), other.size());
}
values.clear();
Iterator<Element> it = other.iterateNonZero();
Element e;
while (it.hasNext() && (e = it.next()) != null) {
setQuick(e.index(), e.get());
}
return this;
}
/**
* @return false
*/
@Override
public boolean isDense() {
return false;
}
/**
* @return false
*/
@Override
public boolean isSequentialAccess() {
return false;
}
@Override
public double getQuick(int index) {
return values.get(index);
}
@Override
public void setQuick(int index, double value) {
lengthSquared = -1.0;
if (value == 0.0) {
values.removeKey(index);
} else {
values.put(index, value);
}
}
@Override
public int getNumNondefaultElements() {
return values.size();
}
@Override
public RandomAccessSparseVector like() {
return new RandomAccessSparseVector(size(), values.size());
}
/**
* NOTE: this implementation reuses the Vector.Element instance for each call of next(). If you need to preserve the
* instance, you need to make a copy of it
*
* @return an {@link Iterator} over the Elements.
* @see #getElement(int)
*/
@Override
public Iterator<Element> iterateNonZero() {
return new NonDefaultIterator();
}
@Override
public Iterator<Element> iterator() {
return new AllIterator();
}
private final class NonDefaultIterator extends AbstractIterator<Element> {
private final RandomAccessElement element = new RandomAccessElement();
private final IntArrayList indices = new IntArrayList();
private int offset;
private NonDefaultIterator() {
values.keys(indices);
}
@Override
protected Element computeNext() {
if (offset >= indices.size()) {
return endOfData();
}
element.index = indices.get(offset);
offset++;
return element;
}
}
private final class AllIterator extends AbstractIterator<Element> {
private final RandomAccessElement element = new RandomAccessElement();
private AllIterator() {
element.index = -1;
}
@Override
protected Element computeNext() {
if (element.index + 1 < size()) {
element.index++;
return element;
} else {
return endOfData();
}
}
}
private final class RandomAccessElement implements Element {
int index;
@Override
public double get() {
return values.get(index);
}
@Override
public int index() {
return index;
}
@Override
public void set(double value) {
lengthSquared = -1;
if (value == 0.0) {
values.removeKey(index);
} else {
values.put(index, value);
}
}
}
}