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.dp;
024
025import java.io.Serializable;
026import java.util.HashMap;
027import java.util.Map;
028
029import org.biojava.bio.Annotation;
030import org.biojava.bio.BioError;
031import org.biojava.bio.BioException;
032import org.biojava.bio.dist.Distribution;
033import org.biojava.bio.dist.UniformDistribution;
034import org.biojava.bio.symbol.Alphabet;
035import org.biojava.bio.symbol.FiniteAlphabet;
036import org.biojava.bio.symbol.IllegalSymbolException;
037import org.biojava.bio.symbol.SimpleAlphabet;
038import org.biojava.utils.AbstractChangeable;
039import org.biojava.utils.ChangeForwarder;
040import org.biojava.utils.ChangeSupport;
041import org.biojava.utils.ChangeType;
042import org.biojava.utils.ChangeVetoException;
043
044/**
045 * Wraps a weight matrix up so that it appears to be a very simple HMM.
046 *
047 * @author Matthew Pocock
048 */
049
050public class WMAsMM
051  extends
052    AbstractChangeable
053  implements
054    MarkovModel,
055    Serializable
056{
057  private static final int [] advance = {1};
058
059  private final WeightMatrix wm;
060  private final FiniteAlphabet stateAlpha;
061  private final MagicalState magicalState;
062  private final EmissionState [] states;
063
064  private final Map transFrom;
065  private final Map transTo;
066  private final Map transWeights;
067
068  private final transient ChangeForwarder distForwarder;
069
070  public int[] advance() {
071    return new int[] { 1 }; // fixme: this should be cleverer:x
072  }
073
074  public Alphabet emissionAlphabet() {
075    return wm.getAlphabet();
076  }
077
078  public FiniteAlphabet stateAlphabet() {
079    return stateAlpha;
080  }
081
082  public int heads() {
083    return 1;
084  }
085
086  public MagicalState magicalState() {
087    return magicalState;
088  }
089
090  public Distribution getWeights(State source)
091  throws IllegalSymbolException {
092    stateAlpha.validate(source);
093    return (Distribution) transWeights.get(source);
094  }
095
096  public void setWeights(State source, Distribution dist)
097  throws ChangeVetoException {
098    throw new ChangeVetoException(
099      "Can't replace distribution in immutable model"
100    );
101  }
102
103  public FiniteAlphabet transitionsFrom(State from)
104  throws IllegalSymbolException {
105    Alphabet sAlpha = stateAlphabet();
106    sAlpha.validate(from);
107
108    return (FiniteAlphabet) transFrom.get(from);
109  }
110
111  public FiniteAlphabet transitionsTo(State to)
112  throws IllegalSymbolException {
113    Alphabet sAlpha = stateAlphabet();
114    sAlpha.validate(to);
115
116    return (FiniteAlphabet) transTo.get(to);
117  }
118
119  public void registerWithTrainer(ModelTrainer modelTrainer)
120  throws BioException {
121/*    for(Iterator i = stateAlphabet().iterator(); i.hasNext(); ) {
122      EmissionState s = (EmissionState) i.next();
123      s.registerWithTrainer(modelTrainer);
124    }*/
125  }
126
127  public void createTransition(State from, State to)
128  throws ChangeVetoException {
129    throw new ChangeVetoException(
130      "destroyTransition not supported by " + getClass());
131  }
132
133  public void destroyTransition(State from, State to)
134  throws ChangeVetoException {
135    throw new ChangeVetoException(
136      "destroyTransition not supported by " + getClass());
137  }
138
139  public void addState(State toAdd)
140  throws IllegalSymbolException, ChangeVetoException {
141    if(stateAlphabet().contains(toAdd)) {
142      throw new IllegalSymbolException(
143        toAdd,
144        "Can't add a state to a model that already contains it"
145      );
146    }
147
148    throw new ChangeVetoException("addState not supported by " + getClass());
149  }
150
151  public void removeState(State toAdd)
152  throws IllegalSymbolException, ChangeVetoException {
153    stateAlphabet().validate(toAdd);
154
155    throw new ChangeVetoException("removeState not supported by " + getClass());
156  }
157
158  public boolean containsTransition(State from, State to)
159  throws IllegalSymbolException {
160    Alphabet sAlpha = stateAlphabet();
161    sAlpha.validate(from);
162    sAlpha.validate(to);
163
164    return transitionsFrom(from).contains(to);
165  }
166
167  protected int index(State s) {
168    for(int i = 0; i < states.length; i++) {
169      if(s == states[i]) {
170        return i;
171      }
172    }
173
174    return -1;
175  }
176
177  public WMAsMM(WeightMatrix wm) throws IllegalSymbolException {
178    try {
179      ChangeSupport changeSupport = getChangeSupport(ChangeType.UNKNOWN);
180      distForwarder = new ChangeForwarder.Retyper(this, changeSupport, MarkovModel.PARAMETER);
181      transFrom = new HashMap();
182      transTo = new HashMap();
183      transWeights = new HashMap();
184      this.wm = wm;
185      this.magicalState = MagicalState.getMagicalState(wm.getAlphabet(), 1);
186      SimpleAlphabet sa = new SimpleAlphabet();
187      sa.addSymbol(magicalState);
188      this.stateAlpha = sa;
189      this.states = new EmissionState[wm.columns()];
190      for(int i = 0; i <= wm.columns(); i++) {
191        if(i < wm.columns()) {
192          sa.addSymbol(
193            this.states[i] = new SimpleEmissionState(
194              i + "",
195              Annotation.EMPTY_ANNOTATION,
196              WMAsMM.advance,
197              wm.getColumn(i)
198            )
199          );
200          wm.getColumn(i).addChangeListener(distForwarder);
201        }
202        State prev = (i == 0) ? magicalState : states[i-1];
203        State current = (i == wm.columns()) ? magicalState : states[i];
204        FiniteAlphabet fa = (FiniteAlphabet) prev.getMatches();
205        transFrom.put(prev, current.getMatches());
206        transTo.put(current, fa);
207        Distribution dist = new UniformDistribution(fa);
208        transWeights.put(prev, dist);
209      }
210      sa.setName("Weight Matrix columns");
211    } catch (ChangeVetoException cve) {
212      throw new BioError(
213
214        "Assertion Failure: Should be able to manipulate my state alphabet.", cve
215      );
216    } catch (IllegalSymbolException ise) {
217      throw new BioError(
218
219        "Assertion Failure: Should be able to manipulate my state alphabet.", ise
220      );
221    }
222  }
223}