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 022package org.biojavax.ga.util; 023 024import java.util.AbstractSet; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.Iterator; 030import java.util.Map; 031import java.util.NoSuchElementException; 032 033 034/** 035 * <p>Inspred by the BioJava Distribution objects the WeightedSet is a map from 036 * a Key to a Weight. Unlike Distributions the Keys do not have to be Symbols. 037 * In the GA package the WeightedMap is useful for sampling Organisms according 038 * to their fitness.</p> 039 * 040 * <p>When Symbols are added or their weights are set then the weights are internally 041 * normalized to 1</p> 042 * 043 * @author Mark Schreiber 044 * @version 1.0 045 * @since 1.5 046 */ 047 048public class WeightedSet extends AbstractSet implements java.io.Serializable{ 049 private HashMap key2Weight; 050 double totalWeight; 051 052 public WeightedSet() { 053 key2Weight = new HashMap(); 054 } 055 056 /** 057 * Converts the Set to a map from key <code>Objects</code> to <code>Double</code> 058 * weights. 059 * @return a Map with all the key-weight mappings. Weights are not normalized in this map. 060 */ 061 public Map asMap(){ 062 return key2Weight; 063 } 064 065 /** 066 * Randomly samples an <code>Object</code> from the <code>Set</code> according 067 * to its weight. 068 * @return the Object sampled. 069 */ 070 public Object sample(){ 071 double p = Math.random(); 072 for (Iterator i = this.iterator(); i.hasNext(); ) { 073 Object o = i.next(); 074 double weight = getWeight(o); 075 076 p -= weight; 077 if(p <= 0.0){ 078 return o; 079 } 080 } 081 throw new org.biojava.bio.BioError("Cannot sample an object, does this set contain any objects?"); 082 } 083 084 /** 085 * Determines the normalized weight for <code>o</code> 086 * @param o the <code>Object</code> you want to know the weight of 087 * @return the normalized weight 088 * @throws NoSuchElementException if <code>o</code> is not found in this set 089 */ 090 public double getWeight(Object o) throws NoSuchElementException{ 091 if(!( key2Weight.containsKey(o))) 092 throw new NoSuchElementException(o+" not found in this WeightedSet"); 093 094 Double d = (Double)key2Weight.get(o); 095 if(totalWeight == 0.0) 096 return 0.0; 097 098 099 return d.doubleValue() / totalWeight; 100 } 101 102 /** 103 * The total weight that has been added to this Set. 104 * @return the total weight (the value that can be used for normalizing) 105 */ 106 protected double getTotalWeight(){ 107 return totalWeight; 108 } 109 110 /** 111 * Sets the weight of an <code>Object</code>. If the <code>Object</code> is 112 * not in this <code>Set</code> then it is added. 113 * @param o the <code>Object</code> 114 * @param w the weight. 115 * @throws IllegalArgumentException if <code>w</code> is < 0.0 116 */ 117 public void setWeight(Object o, double w){ 118 if(w < 0.0){ 119 throw new IllegalArgumentException("Weight must be >= 0.0"); 120 } 121 if(key2Weight.containsKey(o)){ 122 remove(o); 123 } 124 totalWeight += w; 125 key2Weight.put(o, Double.valueOf(w)); 126 } 127 128 public boolean contains(Object o) { 129 return key2Weight.containsKey(o); 130 } 131 132 public boolean remove(Object o) { 133 if(key2Weight.containsKey(o)){ 134 totalWeight -= ((Double)key2Weight.get(o)).doubleValue(); 135 key2Weight.remove(o); 136 return true; 137 } 138 return false; 139 } 140 141 public boolean isEmpty() { 142 return key2Weight.isEmpty(); 143 } 144 public boolean retainAll(Collection c) { 145 boolean b = false; 146 Collection toRemove = new ArrayList(); 147 148 for (Iterator i = iterator(); i.hasNext(); ) { 149 Object item = i.next(); 150 if(c.contains(item) == false){ 151 b = true; 152 toRemove.add(item); 153 } 154 } 155 156 removeAll(toRemove); 157 158 return b; 159 } 160 161 /** 162 * Adds a new <code>Object</code> with a weight of zero. Equivalent to 163 * setWeight(o, 0.0); 164 * @param o the object to add. 165 * @return true if this Object has not been added before. 166 */ 167 public boolean add(Object o) { 168 boolean b = !(key2Weight.containsKey(o)); 169 setWeight(o, 0.0); 170 return b; 171 } 172 public int size() { 173 return key2Weight.size(); 174 } 175 176 public boolean containsAll(Collection c) { 177 if(size() == 0) 178 return false; 179 180 for (Iterator i = iterator(); i.hasNext(); ) { 181 Object item = i.next(); 182 if(!(key2Weight.containsKey(item))){ 183 return false; 184 } 185 } 186 return true; 187 } 188 public Object[] toArray() { 189 Object[] o = new Object[size()]; 190 int j = 0; 191 for (Iterator i = iterator(); i.hasNext(); ) { 192 o[j++] = i.next(); 193 } 194 195 return o; 196 } 197 198 public void clear() { 199 key2Weight = new HashMap(); 200 totalWeight = 0.0; 201 } 202 203 /** 204 * Returns an unmodifiable iterator over the keys of the set. 205 * @return an Iterator 206 */ 207 public Iterator iterator() { 208 return Collections.unmodifiableSet(key2Weight.keySet()).iterator(); 209 } 210 211 public boolean addAll(Collection c) { 212 boolean b = false; 213 214 for (Iterator i = c.iterator(); i.hasNext(); ) { 215 216 Object item = i.next(); 217 if(!(key2Weight.containsKey(item))) 218 b = true; 219 220 add(item); 221 } 222 return b; 223 } 224}