001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.stats.svm;
022
023import java.util.HashMap;
024import java.util.Map;
025
026/**
027 * Caches the leading diagonal of a kernel matrix.
028 * <p>
029 * Several kernels need to repeatedly access k(x,x) to do things like
030 * normalization, or to calculate distances. This kernel wraps k so that these
031 * leading diagonal elements do not need to be calculated each time.
032 * <p>
033 * This kernel is thread-safe. However, care must be taken when setting the
034 * nested kernel that no other thread is retrieving values at the same time.
035 * This would cause a race condition in which the newly flushed cache may
036 * contain a value from the previous kernel.
037 *
038 * @author Matthew Pocock
039 * @author Thomas Down
040 */
041public class DiagonalCachingKernel extends NestedKernel {
042  /**
043   * The cache of values.
044   */
045  transient private Map cache;
046
047  {
048    cache = new HashMap();
049  }
050  
051  /**
052   * Create a new CachingKernel.
053   */
054  public DiagonalCachingKernel() {
055  }
056
057  /**
058   * Creates a new DiagonalCachingKernel that nests k.
059   */
060  public DiagonalCachingKernel(SVMKernel k) {
061    setNestedKernel(k);
062  }
063  
064  /**
065   * <p>
066   * Set the kernel to nest.
067   * </p>
068   *
069   * <p>
070   * This will flush the cache.
071   * </p>
072   *
073   * @param k  the kernel to nest.
074   */
075  public void setNestedKernel(SVMKernel k) {
076    super.setNestedKernel(k);
077    synchronized(cache) {
078      cache.clear();
079    }
080  }
081
082  /**
083   * <p>
084   * Returns the kernel product of two Objects.
085   * </p>
086   *
087   * <p>
088   * This returns <code>getNestedKernel.evaluate(x, y)</code>. If
089   * <code>x.equals(y)</code> then it will cache the result first time, and do
090   * a hash table look up to retrieve the value in subsequent calls.
091   * </p>
092   */
093  public double evaluate(Object x, Object y) {
094    if(x.equals(y)) {
095      Double d = null;
096      synchronized(cache) {
097        d = (Double) cache.get(x);
098      }
099      if (d == null) {
100        d = new Double(getNestedKernel().evaluate(x, x));
101        synchronized(cache) {
102          cache.put(x, d);
103        }
104      }
105      return d.doubleValue();
106    } else {
107      return getNestedKernel().evaluate(x, y);
108    }
109  }
110  
111  public String toString() {
112    return getNestedKernel().toString();
113  }
114}