/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * AlgVector.java * Copyright (C) 2002-2012 University of Waikato, Hamilton, New Zealand * */ package weka.core; import java.io.Serializable; import java.util.Random; /** * Class for performing operations on an algebraic vector * of floating-point values. * * @author Gabi Schmidberger (gabi@cs.waikato.ac.nz) * @version $Revision: 8034 $ */ public class AlgVector implements Cloneable, Serializable, RevisionHandler { /** for serialization */ private static final long serialVersionUID = -4023736016850256591L; /** The values of the matrix */ protected double[] m_Elements; /** * Constructs a vector and initializes it with default values. * * @param n the number of elements */ public AlgVector(int n) { m_Elements = new double[n]; initialize(); } /** * Constructs a vector using a given array. * * @param array the values of the matrix */ public AlgVector(double[] array) { m_Elements = new double[array.length]; for (int i = 0; i < array.length; i++) { m_Elements[i] = array[i]; } } /** * Constructs a vector using a given data format. * The vector has an element for each numerical attribute. * The other attributes (nominal, string) are ignored. * Random is used to initialize the attributes. * * @param format the data format to use * @param random for initializing the attributes * @throws Exception if something goes wrong */ public AlgVector(Instances format, Random random) throws Exception { int len = format.numAttributes(); for (int i = 0; i < format.numAttributes(); i++) { if (!format.attribute(i).isNumeric()) len--; } if (len > 0) { m_Elements = new double[len]; initialize(random); } } /** * Constructs a vector using an instance. * The vector has an element for each numerical attribute. * The other attributes (nominal, string) are ignored. * * @param instance with numeric attributes, that AlgVector gets build from * @throws Exception if instance doesn't have access to the data format or * no numeric attributes in the data */ public AlgVector(Instance instance) throws Exception { int len = instance.numAttributes(); for (int i = 0; i < instance.numAttributes(); i++) { if (!instance.attribute(i).isNumeric()) len--; } if (len > 0) { m_Elements = new double[len]; int n = 0; for (int i = 0; i < instance.numAttributes(); i++) { if (!instance.attribute(i).isNumeric()) continue; m_Elements[n] = instance.value(i); n++; } } else { throw new IllegalArgumentException("No numeric attributes in data!"); } } /** * Creates and returns a clone of this object. * * @return a clone of this instance. * @throws CloneNotSupportedException if an error occurs */ public Object clone() throws CloneNotSupportedException { AlgVector v = (AlgVector)super.clone(); v.m_Elements = new double[numElements()]; for (int i = 0; i < numElements(); i++) { v.m_Elements[i] = m_Elements[i]; } return v; } /** * Resets the elements to the default value which is 0.0. */ protected void initialize() { for (int i = 0; i < m_Elements.length; i++) { m_Elements[i] = 0.0; } } /** * Initializes the values with random numbers between 0 and 1. * * @param random the random number generator to use for initializing */ protected void initialize(Random random) { for (int i = 0; i < m_Elements.length; i++) { m_Elements[i] = random.nextDouble(); } } /** * Returns the value of a cell in the matrix. * * @param index the row's index * @return the value of the cell of the vector */ public final double getElement(int index) { return m_Elements[index]; } /** * Returns the number of elements in the vector. * * @return the number of rows */ public final int numElements() { return m_Elements.length; } /** * Sets an element of the matrix to the given value. * * @param index the elements index * @param value the new value */ public final void setElement(int index, double value) { m_Elements[index] = value; } /** * Sets the elements of the vector to values of the given array. * Performs a deep copy. * * @param elements an array of doubles */ public final void setElements(double[] elements) { for (int i = 0; i < elements.length; i++) { m_Elements[i] = elements[i]; } } /** * Gets the elements of the vector and returns them as double array. * * @return an array of doubles */ public double[] getElements() { double [] elements = new double[this.numElements()]; for (int i = 0; i < elements.length; i++) { elements[i] = m_Elements[i]; } return elements; } /** * Gets the elements of the vector as an instance. * !! NON-numeric data is ignored sofar * * @param model the dataset structure to fit the data to * @param random in case of nominal values a random label is taken * @return an array of doubles * @throws Exception if length of vector is not number of numerical attributes */ public Instance getAsInstance(Instances model, Random random) throws Exception { Instance newInst = null; if (m_Elements != null) { newInst = new DenseInstance(model.numAttributes()); newInst.setDataset(model); for (int i = 0, j = 0; i < model.numAttributes(); i++) { if (model.attribute(i).isNumeric()) { if (j >= m_Elements.length) throw new Exception("Datatypes are not compatible."); newInst.setValue(i, m_Elements[j++]); } if (model.attribute(i).isNominal()) { int newVal = (int) (random.nextDouble() * (double) (model.attribute(i).numValues())); if (newVal == (int) model.attribute(i).numValues()) newVal -= 1; newInst.setValue(i, newVal); } } } return newInst; } /** * Returns the sum of this vector with another. * * @param other the vector to add * @return a vector containing the sum. */ public final AlgVector add(AlgVector other) { AlgVector b = null; if (m_Elements != null) { int n = m_Elements.length; try { b = (AlgVector)clone(); } catch (CloneNotSupportedException ex) { b = new AlgVector(n); } for(int i = 0; i < n; i++) { b.m_Elements[i] = m_Elements[i] + other.m_Elements[i]; } } return b; } /** * Returns the difference of this vector minus another. * * @param other the vector to subtract * @return a vector containing the difference vector. */ public final AlgVector substract(AlgVector other) { int n = m_Elements.length; AlgVector b; try { b = (AlgVector)clone(); } catch (CloneNotSupportedException ex) { b = new AlgVector(n); } for(int i = 0; i < n; i++) { b.m_Elements[i] = m_Elements[i] - other.m_Elements[i]; } return b; } /** * Returns the inner (or dot) product of two vectors * * @param b the multiplication matrix * @return the double representing the dot product */ public final double dotMultiply(AlgVector b) { double sum = 0.0; if (m_Elements != null) { int n = m_Elements.length; for(int i = 0; i < n; i++) { sum += m_Elements[i] * b.m_Elements[i]; } } return sum; } /** * Computes the scalar product of this vector with a scalar * * @param s the scalar */ public final void scalarMultiply(double s) { if (m_Elements != null) { int n = m_Elements.length; for(int i = 0; i < n; i++) { m_Elements[i] = s * m_Elements[i]; } } } /** * Changes the length of a vector. * * @param len the new length of the vector */ public void changeLength(double len) { double factor = this.norm(); factor = len / factor; scalarMultiply(factor); } /** * Returns the norm of the vector * * @return the norm of the vector */ public double norm() { if (m_Elements != null) { int n = m_Elements.length; double sum = 0.0; for(int i = 0; i < n; i++) { sum += m_Elements[i] * m_Elements[i]; } return Math.pow(sum, 0.5); } else return 0.0; } /** * Norms this vector to length 1.0 */ public final void normVector() { double len = this.norm(); this.scalarMultiply(1 / len); } /** * Converts a vector to a string * * @return the converted string */ public String toString() { StringBuffer text = new StringBuffer(); for (int i = 0; i < m_Elements.length; i++) { if (i > 0) text.append(","); text.append(Utils.doubleToString(m_Elements[i],6)); } text.append("\n"); return text.toString(); } /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } /** * Main method for testing this class, can take an ARFF file as first argument. * * @param args commandline options * @throws Exception if something goes wrong in testing */ public static void main(String[] args) throws Exception { double[] first = {2.3, 1.2, 5.0}; try { AlgVector test = new AlgVector(first); System.out.println("test:\n " + test); } catch (Exception e) { e.printStackTrace(); } } }