package de.lmu.ifi.dbs.elki.data; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2014 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 de.lmu.ifi.dbs.elki.math.linearalgebra.Vector; import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter; import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil; import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter; import de.lmu.ifi.dbs.elki.utilities.io.ByteArrayUtil; import de.lmu.ifi.dbs.elki.utilities.io.ByteBufferSerializer; import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; /** * Vector type using {@code float[]} storage, thus needing approximately half as * much memory as {@link DoubleVector}. * * @author Elke Achtert */ public class FloatVector extends AbstractNumberVector { /** * Static factory instance. */ public static final FloatVector.Factory FACTORY = new FloatVector.Factory(); /** * Serializer for up to 127 dimensions. */ public static final ByteBufferSerializer BYTE_SERIALIZER = new SmallSerializer(); /** * Serializer for up to 2^15-1 dimensions. */ public static final ByteBufferSerializer SHORT_SERIALIZER = new ShortSerializer(); /** * Serializer using varint encoding. */ public static final ByteBufferSerializer VARIABLE_SERIALIZER = new VariableSerializer(); /** * Keeps the values of the float vector. */ private final float[] values; /** * Private constructor. NOT for public use. * * @param values Data values * @param nocopy Flag to re-use the values array */ private FloatVector(float[] values, boolean nocopy) { this.values = nocopy ? values : values.clone(); } /** * Create a FloatVector consisting of the given float values. * * @param values the values to be set as values of the float vector */ public FloatVector(float[] values) { this.values = values.clone(); } /** * Expects a matrix of one column. * * @param columnMatrix a matrix of one column */ public FloatVector(Vector columnMatrix) { final double[] src = columnMatrix.getArrayRef(); values = new float[src.length]; for(int i = 0; i < src.length; i++) { values[i] = (float) src[i]; } } @Override public int getDimensionality() { return values.length; } @Deprecated @Override public Float getValue(int dimension) { return values[dimension]; } @Override public double doubleValue(int dimension) { return values[dimension]; } @Override public long longValue(int dimension) { return (long) values[dimension]; } @Override public Vector getColumnVector() { return new Vector(ArrayLikeUtil.toPrimitiveDoubleArray(values, ArrayLikeUtil.FLOATARRAYADAPTER)); } @Override public String toString() { StringBuilder featureLine = new StringBuilder(); for(int i = 0; i < values.length; i++) { featureLine.append(values[i]); if(i + 1 < values.length) { featureLine.append(ATTRIBUTE_SEPARATOR); } } return featureLine.toString(); } /** * Factory for float vectors. * * @author Erich Schubert * * @apiviz.has FloatVector */ public static class Factory extends AbstractNumberVector.Factory { @Override public FloatVector newFeatureVector(A array, ArrayAdapter adapter) { int dim = adapter.size(array); float[] values = new float[dim]; for(int i = 0; i < dim; i++) { values[i] = adapter.get(array, i).floatValue(); } return new FloatVector(values, true); } @Override public FloatVector newNumberVector(A array, NumberArrayAdapter adapter) { int dim = adapter.size(array); float[] values = new float[dim]; for(int i = 0; i < dim; i++) { values[i] = adapter.getFloat(array, i); } return new FloatVector(values, true); } @Override public ByteBufferSerializer getDefaultSerializer() { return VARIABLE_SERIALIZER; } @Override public Class getRestrictionClass() { return FloatVector.class; } /** * Parameterization class. * * @author Erich Schubert * * @apiviz.exclude */ public static class Parameterizer extends AbstractParameterizer { @Override protected FloatVector.Factory makeInstance() { return FACTORY; } } } /** * Serialization class for dense float vectors with up to 127 dimensions, by * using a byte for storing the dimensionality. * * @author Erich Schubert * * @apiviz.uses FloatVector - - «serializes» */ public static class SmallSerializer implements ByteBufferSerializer { @Override public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException { final byte dimensionality = buffer.get(); assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality); final float[] values = new float[dimensionality]; for(int i = 0; i < dimensionality; i++) { values[i] = buffer.getFloat(); } return new FloatVector(values, true); } @Override public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException { assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!"; assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length); buffer.put((byte) vec.values.length); for(int i = 0; i < vec.values.length; i++) { buffer.putFloat(vec.values[i]); } } @Override public int getByteSize(FloatVector vec) { assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!"; return ByteArrayUtil.SIZE_BYTE + ByteArrayUtil.SIZE_FLOAT * vec.getDimensionality(); } } /** * Serialization class for dense float vectors with up to * {@link Short#MAX_VALUE} dimensions, by using a short for storing the * dimensionality. * * @author Erich Schubert * * @apiviz.uses FloatVector - - «serializes» */ public static class ShortSerializer implements ByteBufferSerializer { @Override public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException { final short dimensionality = buffer.getShort(); assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality); final float[] values = new float[dimensionality]; for(int i = 0; i < dimensionality; i++) { values[i] = buffer.getFloat(); } return new FloatVector(values, true); } @Override public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException { assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!"; assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length); buffer.putShort((short) vec.values.length); for(int i = 0; i < vec.values.length; i++) { buffer.putFloat(vec.values[i]); } } @Override public int getByteSize(FloatVector vec) { assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!"; return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_FLOAT * vec.getDimensionality(); } } /** * Serialization class for variable dimensionality by using VarInt encoding. * * @author Erich Schubert * * @apiviz.uses FloatVector - - «serializes» */ public static class VariableSerializer implements ByteBufferSerializer { @Override public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException { final int dimensionality = ByteArrayUtil.readUnsignedVarint(buffer); assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality); final float[] values = new float[dimensionality]; for(int i = 0; i < dimensionality; i++) { values[i] = buffer.getFloat(); } return new FloatVector(values, true); } @Override public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException { assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!"; assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length); ByteArrayUtil.writeUnsignedVarint(buffer, vec.values.length); for(int i = 0; i < vec.values.length; i++) { buffer.putFloat(vec.values[i]); } } @Override public int getByteSize(FloatVector vec) { assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!"; return ByteArrayUtil.getUnsignedVarintSize(vec.values.length) + ByteArrayUtil.SIZE_FLOAT * vec.values.length; } } }