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 */
021/*
022 * @(#)CachingKernel.java      0.1 00/01/20
023 *
024 * By Thomas Down <td2@sanger.ac.uk>
025 */
026
027package org.biojava.stats.svm;
028
029import java.util.HashMap;
030import java.util.Map;
031
032/**
033 * <p>
034 * Caches the results of a nested kernel so that k(a, b) need only be calculated
035 * once.
036 * </p>
037 *
038 * <p>
039 * This kernel is thread-safe. However, care must be taken when setting the
040 * nested kernel that no other thread is retrieving values at the same time.
041 * This would cause a race condition in which the newly flushed cache may
042 * contain a value from the previous kernel.
043 * </p>
044 *
045 * @author Thomas Down
046 * @author Matthew Pocock
047 */
048public class CachingKernel extends NestedKernel {
049    transient private Map cache;
050    
051    public CachingKernel() {
052    }
053    
054    public CachingKernel(SVMKernel k) {
055      super(k);
056    }
057
058    public void setNestedKernel(SVMKernel k) {
059      super.setNestedKernel(k);
060      if(cache == null) {
061        cache = new HashMap();
062      }
063      cache.clear();
064    }
065
066    public double evaluate(Object x, Object y) {
067      ObjectPair op = new ObjectPair(x, y);
068      Double d = null;
069      synchronized (cache) {
070        d = (Double) cache.get(op);
071      }
072      if (d == null) {
073        d = new Double(getNestedKernel().evaluate(x, y));
074        synchronized (cache) {
075          cache.put(op, d);
076        }
077      }
078      return d.doubleValue();
079    }
080
081    public String toString() {
082      return getNestedKernel().toString();
083    }
084    
085    private static class ObjectPair {
086      Object a;
087      Object b;
088
089      public ObjectPair(Object a, Object b) {
090        this.a = a;
091        this.b = b;
092      }
093
094      public boolean equals(Object x) {
095        if (! (x instanceof ObjectPair))
096          return false;
097        ObjectPair op = (ObjectPair) x;
098        return ((op.a == a && op.b == b) || 
099                            (op.a == b && op.b == a));
100      }
101
102      public int hashCode() {
103        return a.hashCode() + b.hashCode();
104      }
105    }
106}