package de.lmu.ifi.dbs.elki.data;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
Copyright (C) 2011
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.List;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
/**
* A DoubleVector is to store real values approximately as double values.
*
* @author Arthur Zimek
*
* @apiviz.landmark
*/
public class DoubleVector extends AbstractNumberVector implements ByteBufferSerializer {
/**
* Keeps the values of the real vector
*/
private double[] values;
/**
* Private constructor. NOT for public use.
*/
private DoubleVector(double[] values, boolean nocopy) {
if(nocopy) {
this.values = values;
}
else {
this.values = new double[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
}
/**
* Provides a feature vector consisting of double values according to the
* given Double values.
*
* @param values the values to be set as values of the real vector
*/
public DoubleVector(List values) {
int i = 0;
this.values = new double[values.size()];
for(Iterator iter = values.iterator(); iter.hasNext(); i++) {
this.values[i] = (iter.next());
}
}
/**
* Provides a DoubleVector consisting of the given double values.
*
* @param values the values to be set as values of the DoubleVector
*/
public DoubleVector(double[] values) {
this.values = new double[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
/**
* Provides a DoubleVector consisting of the given double values.
*
* @param values the values to be set as values of the DoubleVector
*/
public DoubleVector(Double[] values) {
this.values = new double[values.length];
for(int i = 0; i < values.length; i++) {
this.values[i] = values[i];
}
}
/**
* Expects a matrix of one column.
*
* @param columnMatrix a matrix of one column
*/
public DoubleVector(Vector columnMatrix) {
values = new double[columnMatrix.getRowDimensionality()];
for(int i = 0; i < values.length; i++) {
values[i] = columnMatrix.get(i);
}
}
@Override
public int getDimensionality() {
return values.length;
}
/**
* Returns the value of the specified attribute.
*
* @param dimension the selected attribute. Attributes are counted starting
* with 1.
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
*/
@Override
public Double getValue(int dimension) {
try {
return values[dimension - 1];
}
catch(IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
/**
* Returns the value of the specified attribute.
*
* @param dimension the selected attribute. Attributes are counted starting
* with 1.
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
*/
@Override
public double doubleValue(int dimension) {
try {
return values[dimension - 1];
}
catch(IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
/**
* Returns the value of the specified attribute as long.
*
* @param dimension the selected attribute. Attributes are counted starting
* with 1.
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
*/
@Override
public long longValue(int dimension) {
try {
return (long) values[dimension - 1];
}
catch(IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
/**
* Get a copy of the raw double[] array.
*
* @return copy of values array.
*/
public double[] getValues() {
double[] copy = new double[values.length];
System.arraycopy(values, 0, copy, 0, values.length);
return copy;
}
@Override
public Vector getColumnVector() {
// TODO: can we sometimes save this copy?
// Is this worth the more complex API?
return new Vector(values.clone());
}
@Override
public Matrix getRowVector() {
return new Matrix(new double[][] { values.clone() });
}
@Override
public DoubleVector plus(DoubleVector fv) {
if(fv.getDimensionality() != this.getDimensionality()) {
throw new IllegalArgumentException("Incompatible dimensionality: " + this.getDimensionality() + " - " + fv.getDimensionality() + ".");
}
double[] values = new double[this.values.length];
for(int i = 0; i < values.length; i++) {
values[i] = this.values[i] + fv.values[i];
}
return new DoubleVector(values, true);
}
@Override
public DoubleVector minus(DoubleVector fv) {
if(fv.getDimensionality() != this.getDimensionality()) {
throw new IllegalArgumentException("Incompatible dimensionality: " + this.getDimensionality() + " - " + fv.getDimensionality() + ".");
}
double[] values = new double[this.values.length];
for(int i = 0; i < values.length; i++) {
values[i] = this.values[i] - fv.values[i];
}
return new DoubleVector(values, true);
}
@Override
public DoubleVector nullVector() {
return new DoubleVector(new double[this.values.length], true);
}
@Override
public DoubleVector negativeVector() {
return multiplicate(-1);
}
@Override
public DoubleVector multiplicate(double k) {
double[] values = new double[this.values.length];
for(int i = 0; i < values.length; i++) {
values[i] = this.values[i] * k;
}
return new DoubleVector(values, true);
}
/**
* Provides the scalar product (inner product) of this and the given
* DoubleVector.
*
* @param d the DoubleVector to compute the scalar product for
* @return the scalar product (inner product) of this and the given
* DoubleVector
*/
@Override
public Double scalarProduct(DoubleVector d) {
if(this.getDimensionality() != d.getDimensionality()) {
throw new IllegalArgumentException("Incompatible dimensionality: " + this.getDimensionality() + " - " + d.getDimensionality() + ".");
}
double result = 0.0;
for(int i = 0; i < this.getDimensionality(); i++) {
result += this.values[i] * d.values[i];
}
return result;
}
@Override
public String toString() {
StringBuffer featureLine = new StringBuffer();
for(int i = 0; i < values.length; i++) {
featureLine.append(values[i]);
if(i + 1 < values.length) {
featureLine.append(ATTRIBUTE_SEPARATOR);
}
}
return featureLine.toString();
}
@Override
public DoubleVector newInstance(Vector values) {
return new DoubleVector(values);
}
@Override
public DoubleVector newInstance(Double[] values) {
return new DoubleVector(values);
}
@Override
public DoubleVector newInstance(double[] values) {
return new DoubleVector(values);
}
@Override
public DoubleVector newInstance(List values) {
return new DoubleVector(values);
}
@Override
public DoubleVector fromByteBuffer(ByteBuffer buffer) throws IOException {
final short dimensionality = buffer.getShort();
final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
if(buffer.remaining() < len) {
throw new IOException("Not enough data for a double vector!");
}
double[] values = new double[dimensionality];
buffer.asDoubleBuffer().get(values);
return new DoubleVector(values, false);
}
@Override
public void toByteBuffer(ByteBuffer buffer, DoubleVector vec) throws IOException {
final short dimensionality = buffer.getShort();
final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
if(buffer.remaining() < len) {
throw new IOException("Not enough space for the double vector!");
}
buffer.putShort(dimensionality);
buffer.asDoubleBuffer().put(vec.values);
}
@Override
public int getByteSize(DoubleVector vec) {
return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * vec.getDimensionality();
}
}