/* * 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 2 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * PolyKernel.java * Copyright (C) 1999 Eibe Frank * */ package weka.classifiers.sparse; import weka.core.*; /** * The polynomial kernel : * K(x, y) = <x, y>^p or K(x, y) = (<x, y>+1)^p * * @author Eibe Frank (eibe@cs.waikato.ac.nz) * @author Shane Legg (shane@intelligenesis.net) (sparse vector code) * @author Stuart Inglis (stuart@reeltwo.com) (sparse vector code) * @version $$ */ public class PolyKernel extends Kernel { /** The size of the cache (a prime number) */ private int m_cacheSize; /** Use lower-order terms? */ private boolean m_lowerOrder = false; /** The exponent for the polynomial kernel. */ private double m_exponent = 1.0; /** Kernel cache */ private double[] m_storage; private long[] m_keys; /** Counts the number of kernel evaluations. */ private int m_kernelEvals = 0; /** The number of instance in the dataset */ private int m_numInsts; /** * Creates a new <code>PolyKernel</code> instance. * * @param dataset the training dataset used. * @param cacheSize the size of the cache (a prime number) */ public PolyKernel(Instances dataset, int cacheSize, double exponent, boolean lowerOrder){ m_exponent = exponent; m_lowerOrder = lowerOrder; m_data = dataset; m_numInsts = m_data.numInstances(); m_cacheSize = cacheSize; m_storage = new double[m_cacheSize]; m_keys = new long[m_cacheSize]; } /** * Implements the abstract function of Kernel. */ public double eval(int id1, int id2, Instance inst1) throws Exception { double result = 0; long key = -1; int location = -1; // we can only cache if we know the indexes if ((id1 >= 0) && (m_keys != null)) { if (id1 > id2) { key = (long)id1 * m_numInsts + id2; } else { key = (long)id2 * m_numInsts + id1; } if (key < 0) { throw new Exception("Cache overflow detected!"); } location = (int)(key % m_keys.length); if (m_keys[location] == (key + 1)) { return m_storage[location]; } } if (id1 == id2) { result = dotProd(inst1, inst1); } else { result = dotProd(inst1, m_data.instance(id2)); } // Use lower order terms? if (m_lowerOrder) { result += 1.0; } if (m_exponent != 1.0) { result = Math.pow(result, m_exponent); } m_kernelEvals++; // store result in cache if (key != -1){ m_storage[location] = result; m_keys[location] = (key + 1); } return result; } /** * Calculates a dot product between two instances * * @param inst1 the first instance * @param inst2 the second instance * @return the dot product of the two instances. * @exception Exception if an error occurs */ private double dotProd(Instance inst1, Instance inst2) throws Exception { double result=0; // we can do a fast dot product int n1 = inst1.numValues(); int n2 = inst2.numValues(); int classIndex = m_data.classIndex(); for (int p1 = 0, p2 = 0; p1 < n1 && p2 < n2;) { int ind1 = inst1.index(p1); int ind2 = inst2.index(p2); if (ind1 == ind2) { if (ind1 != classIndex) { result += inst1.valueSparse(p1) * inst2.valueSparse(p2); } p1++; p2++; } else if (ind1 > ind2) { p2++; } else { p1++; } } return(result); } /** * Frees the cache used by the kernel. */ public void clean(){ m_storage = null; m_keys = null; } /** * Returns the number of time Eval has been called. * * @return the number of kernel evaluation. */ public int numEvals(){ return m_kernelEvals; } }