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
023package org.biojava.bio.dist;
024
025import org.biojava.bio.symbol.Alphabet;
026import org.biojava.bio.symbol.IllegalAlphabetException;
027import org.biojava.bio.symbol.IllegalSymbolException;
028import org.biojava.bio.symbol.Symbol;
029import org.biojava.utils.ChangeEvent;
030import org.biojava.utils.ChangeForwarder;
031import org.biojava.utils.ChangeSupport;
032import org.biojava.utils.ChangeType;
033import org.biojava.utils.ChangeVetoException;
034import org.biojava.utils.Changeable;
035
036/**
037 * <p>
038 * An encapsulation of a probability distribution over the Symbols within an
039 * alphabet.
040 * </p>
041 *
042 * <p>
043 * A distribution can be implemented as a map from symbol to probability. It is
044 * more correct to think of them as being integrals or sums over probability
045 * dencity funcitons. In this world view, getWeight should look at the
046 * getMatches of the symbol it is given and then perform the apropreate sum or
047 * integral to return the probability of something within that set of symbols
048 * being emitted.
049 * </p>
050 *
051 * <p>
052 * This interface should handle the case of emitting an ambiguity symbol.
053 * This should be just the sum of the probabiltiy of emitting each matching
054 * symbol. It is up to the code using the Distribution instance to divide out
055 * the null model appropreately.
056 * </p>
057 *
058 * @author Matthew Pocock
059 * @since 1.0
060 */
061public interface Distribution extends Changeable {
062  /**
063   * <p>
064   * Whenever a distribution changes the values that would be returned by
065   * getWeight, they should fire a ChangeEvent with this object as the type.
066   * </p>
067   *
068   * <p>
069   * If the whole distribution changes, then the change and previous fields of
070   * the ChangeEvent should be left null. If only a single weight is modified,
071   * then change should be of the form Object[] { symbol, new Double(newVal) }
072   * and previous should be of the form Object[] { symbol, new Double(oldVal) }
073   * </p>
074   */
075  public static final ChangeType WEIGHTS = new ChangeType(
076    "distribution weights changed",
077    "org.biojava.bio.dist.Distribution",
078    "WEIGHTS"
079  );
080
081  /**
082   * <p>
083   * Whenever the null model distribution changes the values that would be
084   * returned by getWeight, either by being edited or by being replaced, a
085   * ChangeEvent with this object as the type should be thrown.
086   * </p>
087   *
088   * <p>
089   * If the null model has changed its weights, then the ChangeEvent should
090   * refer back to the ChangeEvent from the null model.
091   * </p>
092   */
093  public static final ChangeType NULL_MODEL = new ChangeType(
094    "distribution null model changed",
095    "org.biojava.bio.dist.Distribution",
096    "NULL_MODEL"
097  );
098  
099  /**
100   * The alphabet from which this spectrum emits symbols.
101   *
102   * @return  the Alphabet associated with this spectrum
103   */
104  Alphabet getAlphabet();
105    
106  /**
107   * <p>
108   * Return the probability that Symbol s is emitted by this spectrum.
109   * </p>
110   *
111   * <p>
112   * If the symbol is  ambiguou, then it is the sum of the probability that
113   * each one of the matching symbols was emitted.
114   * </p>
115   *
116   * @param s the Symbol emitted
117   * @return  the probability of emitting that symbol
118   * @throws IllegalSymbolException if s is not from this state's alphabet
119   */
120  double getWeight(Symbol s) throws IllegalSymbolException;
121  
122  /**
123   * Set the probability or odds that Symbol s is emitted by this state.
124   *
125   * @param s the Symbol emitted
126   * @param w  the probability of emitting that symbol
127   * @throws IllegalSymbolException if s is not from this state's alphabet, or
128   *         if it is an ambiguity symbol and the implementation can't handle
129   *         this case
130   * @throws ChangeVetoException if this state does not allow weights
131   *         to be tampered with, or if one of the listeners vetoed this change
132   */
133  void setWeight(Symbol s, double w)
134  throws IllegalSymbolException, ChangeVetoException;
135
136  /**
137   * Sample a symbol from this state's probability distribution.
138   *
139   * @return the symbol sampled
140   */
141  Symbol sampleSymbol();
142  
143  /**
144   * Retrieve the null model Distribution that this Distribution recognizes.
145   *
146   * @return  the apropriate null model
147   */
148  Distribution getNullModel();
149  
150  /**
151   * Set the null model Distribution that this Distribution recognizes.
152   *
153   * @param nullDist  the new null model Distribution
154   * @throws IllegalAlphabetException if the null model has the wrong alphabet
155   * @throws ChangeVetoException  if this Distirbution doesn't support setting
156   *         the null model, or if one of its listeners objects
157   */
158  void setNullModel(Distribution nullDist)
159  throws IllegalAlphabetException, ChangeVetoException;
160  
161  /**
162   * <p>
163   * Register this distribution with a training context.
164   * </p>
165   *
166   * <p>
167   * This should be invoked from within dtc.addDistribution(). This method
168   * is responsible for constructing a suitable DistributionTrainer instance
169   * and registering it by calling
170   * dtc.registerDistributionTrainer(this, trainer). If the distribution is a
171   * view onto another distribution, it can force the other to be registered by
172   * calling dtc.addDistribution(other), and can then get on with registering
173   * it's own trainer.
174   * </p>
175   *
176   * @param dtc the DistributionTrainerContext with witch to register a trainer
177   */
178  void registerWithTrainer(DistributionTrainerContext dtc);
179  
180  /**
181   * This listens to the null model distribution events and converts them into
182   * NULL_MODEL events.
183   *
184   * @author Matthew Pocock
185   * @since 1.1
186   * @deprecated use
187   *    <code>new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY)</code>
188   *    instead
189   */
190  public class NullModelForwarder extends ChangeForwarder {
191    /**
192     * Create a new forwarder.
193     *
194     * @param source  the Object who events are forwarded on behalf of
195     * @param cs      the change support that manages the listeners
196     */
197    public NullModelForwarder(Object source, ChangeSupport cs) {
198      super(source, cs);
199    }
200    
201    protected ChangeEvent generateEvent(ChangeEvent ce) {
202      if(ce.getType() == WEIGHTS) {
203        return new ChangeEvent(
204          getSource(),
205          NULL_MODEL,
206          null, null,
207          ce
208        );
209      }
210      return null;
211    }
212  }
213}