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.bio.dist; 022 023import java.io.Serializable; 024import java.util.Iterator; 025import java.util.List; 026 027import org.biojava.bio.symbol.Alphabet; 028import org.biojava.bio.symbol.AlphabetManager; 029import org.biojava.bio.symbol.AtomicSymbol; 030import org.biojava.bio.symbol.BasisSymbol; 031import org.biojava.bio.symbol.FiniteAlphabet; 032import org.biojava.bio.symbol.IllegalAlphabetException; 033import org.biojava.bio.symbol.IllegalSymbolException; 034import org.biojava.bio.symbol.Symbol; 035import org.biojava.utils.ChangeForwarder; 036import org.biojava.utils.ChangeSupport; 037import org.biojava.utils.ChangeType; 038import org.biojava.utils.ChangeVetoException; 039 040/** 041 * Simple base class for OrderNDistributions. 042 * 043 * @author Samiul Hasan 044 * @author Matthew Pocock 045 * @author Thomas Down 046 * @since 1.2 047 */ 048public abstract class AbstractOrderNDistribution extends AbstractDistribution 049 implements OrderNDistribution, Serializable 050{ 051 static final long serialVersionUID = 1406135308618188893L; 052 053 private Alphabet alphabet; 054 private Alphabet firstA; 055 private Alphabet lastA; 056 private Distribution nullModel; 057 058 /** 059 * The listener that will forward events from the underlying distributions to 060 * listeners for this distribution. 061 */ 062 protected transient ChangeForwarder weightForwarder = null; 063 064 protected ChangeSupport getChangeSupport(ChangeType ct) { 065 ChangeSupport changeSupport = super.getChangeSupport(ct); 066 067 if( 068 ( (Distribution.WEIGHTS.isMatchingType(ct)) || (ct.isMatchingType(Distribution.WEIGHTS)) ) && 069 weightForwarder == null 070 ) { 071 weightForwarder = 072 new ChangeForwarder.Retyper(this, changeSupport, Distribution.WEIGHTS); 073 for(Iterator i = conditionedDistributions().iterator(); i.hasNext(); ) { 074 Distribution dist = (Distribution) i.next(); 075 dist.addChangeListener(weightForwarder, Distribution.WEIGHTS); 076 } 077 } 078 079 return changeSupport; 080 } 081 082 /** 083 * Construct a new NthOrderDistribution. 084 * 085 * @param alpha the Alpahbet this is over 086 */ 087 088 protected AbstractOrderNDistribution(Alphabet alpha) 089 throws IllegalAlphabetException { 090 this.alphabet = alpha; 091 List aList = alpha.getAlphabets(); 092 int lb1 = aList.size() - 1; 093 if(aList.size() == 2) { 094 this.firstA = (Alphabet) aList.get(0); 095 } else { 096 this.firstA = AlphabetManager.getCrossProductAlphabet(aList.subList(0, lb1)); 097 } 098 this.lastA = (Alphabet) aList.get(lb1); 099 this.nullModel = new UniformNullModel(); 100 } 101 102 /** 103 * Get the conditioning alphabet of this distribution. If the `overall' 104 * alphabet is a cross-product of two alphabets, this will be the first 105 * of those alphabets. If it is a cross-product of more than two alphabets, 106 * the conditioning alphabet is the cross-product of all but the last 107 * alphabet. 108 * 109 * @return the conditioning Alphabet 110 */ 111 112 public Alphabet getConditioningAlphabet() { 113 return firstA; 114 } 115 116 /** 117 * Get the conditioned alphabet. This is the last alphabet in the 118 * distribution's overall cross-product. It will be the alphabet of 119 * all the sub-distributions contained within this OrderNDistribution. 120 */ 121 122 public Alphabet getConditionedAlphabet() { 123 return lastA; 124 } 125 126 public Alphabet getAlphabet() { 127 return alphabet; 128 } 129 130 /** 131 * Get a weight from one of the sub-distributions, conditioned 132 * on the first part of the symbol. 133 * 134 * @param sym the symbol to look up 135 * @return the weight 136 * @throws IllegalSymbolException if sym is not recognised 137 */ 138 139 protected double getWeightImpl(AtomicSymbol sym) throws IllegalSymbolException { 140 List symL = sym.getSymbols(); 141 int lb1 = symL.size() - 1; 142 BasisSymbol firstS; 143 if(symL.size() == 2) { 144 firstS = (AtomicSymbol) symL.get(0); 145 } else { 146 firstS = (AtomicSymbol) firstA.getSymbol(symL.subList(0, lb1)); 147 } 148 Distribution dist = getDistribution(firstS); 149 return dist.getWeight((AtomicSymbol) symL.get(lb1)); 150 } 151 152 /** 153 * Set a weight in one of the conditioned distributions. It is the callers 154 * responsibility to ensure that all the conditioned distributions have total 155 * weights which sum to 1.0. 156 * 157 * @param sym the symbol to set the weight for 158 * @param w the new weight 159 */ 160 161 public void setWeightImpl(AtomicSymbol sym, double w) 162 throws IllegalSymbolException, ChangeVetoException { 163 List symL = sym.getSymbols(); 164 int lb1 = symL.size() - 1; 165 Symbol firstS; 166 if(symL.size() == 2) { 167 firstS = (Symbol) symL.get(0); 168 } else { 169 firstS = firstA.getSymbol(symL.subList(0, lb1)); 170 } 171 Distribution dist = getDistribution(firstS); 172 dist.setWeight((Symbol) symL.get(lb1), w); 173 } 174 175 public void setNullModelImpl(Distribution nullModel) { 176 this.nullModel = nullModel; 177 } 178 179 public Distribution getNullModel() { 180 return this.nullModel; 181 } 182 183 public void registerWithTrainer(DistributionTrainerContext dtc) { 184 for(Iterator i = conditionedDistributions().iterator(); i.hasNext(); ) { 185 dtc.registerDistribution((Distribution) i.next()); 186 } 187 dtc.registerTrainer(this, new IgnoreCountsTrainer() { 188 public void addCount( 189 DistributionTrainerContext dtc, 190 AtomicSymbol sym, 191 double count 192 ) throws IllegalSymbolException { 193 List symL = sym.getSymbols(); 194 int lb1 = symL.size() - 1; 195 Symbol firstS; 196 if(lb1 == 1) { 197 firstS = (Symbol) symL.get(0); 198 } else { 199 firstS = firstA.getSymbol(symL.subList(0, lb1)); 200 } 201 Distribution dist = getDistribution(firstS); 202 dtc.addCount(dist, (Symbol) symL.get(lb1), count); 203 } 204 205 public double getCount( 206 DistributionTrainerContext dtc, 207 AtomicSymbol sym 208 ) throws IllegalSymbolException { 209 List symL = sym.getSymbols(); 210 int lb1 = symL.size() - 1; 211 Symbol firstS; 212 if(lb1 == 1) { 213 firstS = (Symbol) symL.get(0); 214 } else { 215 firstS = firstA.getSymbol(symL.subList(0, lb1)); 216 } 217 Distribution dist = getDistribution(firstS); 218 return dtc.getCount(dist, (AtomicSymbol) symL.get(lb1)); 219 } 220 }); 221 } 222 223 private class UniformNullModel 224 extends AbstractDistribution implements Serializable 225 { 226 private static final long serialVersionUID = -3357793043843515032L; 227 private Distribution nullModel = new UniformDistribution( 228 (FiniteAlphabet) lastA 229 ); 230 231 public Alphabet getAlphabet() { 232 return AbstractOrderNDistribution.this.getAlphabet(); 233 } 234 235 protected double getWeightImpl(AtomicSymbol sym) 236 throws IllegalSymbolException { 237 List symL = sym.getSymbols(); 238 int lb1 = symL.size() - 1; 239 return nullModel.getWeight((AtomicSymbol) symL.get(lb1)); 240 } 241 242 protected void setWeightImpl(AtomicSymbol sym, double weight) 243 throws ChangeVetoException { 244 throw new ChangeVetoException( 245 "Can't change the weight of this null model" 246 ); 247 } 248 249 public Distribution getNullModel() { 250 return this; 251 } 252 253 protected void setNullModelImpl(Distribution nullModel) 254 throws IllegalAlphabetException, ChangeVetoException { 255 throw new ChangeVetoException( 256 "Can't set the null model for NthOrderDistribution.UniformNullModel" 257 ); 258 } 259 } 260}