View Javadoc
1   package eu.simuline.relana.model;
2   
3   import eu.simuline.relana.expressions.Type;
4   
5   import java.math.BigDecimal;
6   import java.math.MathContext;
7   
8   import java.util.Map;
9   import java.util.EnumMap;
10  import java.util.HashMap;
11  import java.util.Set;
12  import java.util.SortedSet;
13  import java.util.TreeSet;
14  import java.util.HashSet;
15  import java.util.Stack;
16  import java.util.Iterator;
17  
18  /**
19   * Represents a probability distribution. 
20   *
21   *
22   * Created: Mon Apr 25 00:33:44 2005
23   *
24   * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
25   * @version 1.0
26   */
27  public final class ProbDistr {
28  
29      /* -------------------------------------------------------------------- *
30       * inner classes.                                                       *
31       * -------------------------------------------------------------------- */
32  
33      /**
34       * Enumeration of inverters: the trivial inverter 
35       * and the canonical inverter. 
36       */
37      enum Inverter {
38  	Identity() {
39  	    BigDecimal filterProb(BigDecimal prob) {
40  		return prob;
41  	    }
42  	    Set<DeficiencyNode> getSuccsPreds(DeficiencyNode node) {
43  		return node.getSuccessors();
44  	    }
45  	    void addSuccsPreds(DeficiencyNode source,
46  			       DeficiencyNode target) {
47  		target.addSuccessors(source.getSuccessors());
48  	    }
49  	    String andOr() {
50  		return "AND";
51  	    }
52  	    String condEventName(String event, SortedSet<String> conds) {
53  		return neg() + event + "|" + andOr() + conds;
54  	    }
55  	    String neg() {
56  		return "";
57  	    }
58  	},
59  	Invert  () {
60  	    BigDecimal filterProb(BigDecimal prob) {
61  		return BigDecimal.ONE.subtract(prob);
62  	    }
63  	    Set<DeficiencyNode> getSuccsPreds(DeficiencyNode node) {
64  		return node.getPredecessors();
65  	    }
66  	    void addSuccsPreds(DeficiencyNode source,
67  			       DeficiencyNode target) {
68  		target.addPredecessors(source.getPredecessors());
69  	    }
70  	    String andOr() {
71  		return "OR";
72  	    }
73  	    String condEventName(String event, SortedSet<String> conds) {
74  		return andOr() + conds + "|" + neg() + event;
75  	    }
76  	    String neg() {
77  		return "~";
78  	    }
79  	};
80  
81  	abstract BigDecimal filterProb(BigDecimal prob);
82  	abstract Set<DeficiencyNode> getSuccsPreds(DeficiencyNode node);
83  	abstract void addSuccsPreds(DeficiencyNode source,
84  				    DeficiencyNode target);
85  	abstract String andOr();
86  	abstract String condEventName(String event, SortedSet<String> conds);
87  	abstract String neg();
88  
89      } // enum Inverter 
90  
91      /**
92       * Contains some information on validity and degeneracy 
93       * of a {@link ProbDistr} and also the fundamental figures 
94       * to compute probabilities in case of validity and non-degenracy. 
95       */
96      static class Validator {
97  
98  	/* ---------------------------------------------------------------- *
99  	 * attributes.                                                      *
100 	 * ---------------------------------------------------------------- */
101 
102 	/**
103 	 * Maps a deficiency to essentially a set of deficiencies 
104 	 * it depends on. 
105 	 */
106 	private final Map<Deficiency, DeficiencySetNode> extOrdering;
107 
108 	/**
109 	 * Maps a deficiency which is either minimal or conditional 
110 	 * to its probability. 
111 	 */
112 	private final Map<Deficiency, BigDecimal> def2prob;
113 
114 	/**
115 	 * The <code>Deficiency</code>s in the key set of {@link #def2prob} 
116 	 * which map to a value greater than <code>1</code>. 
117 	 */
118 	private final Set<Deficiency> invalids;
119 
120 	/**
121 	 * The <code>Deficiency</code>s in the key set of {@link #def2prob} 
122 	 * which map to exactly <code>1</code>. 
123 	 */
124 	private final Set<Deficiency> degenerates;
125 
126 	/* ---------------------------------------------------------------- *
127 	 * constructors.                                                    *
128 	 * ---------------------------------------------------------------- */
129 
130 	Validator(Map<Deficiency, BigDecimal> def2prob) {
131 	    this.extOrdering = new HashMap<Deficiency, DeficiencySetNode>();
132 	    this.def2prob = new HashMap<Deficiency, BigDecimal>(def2prob);
133 	    this.invalids = new HashSet<Deficiency>();
134 	    this.degenerates = new HashSet<Deficiency>();
135 	}
136 
137 	/* ---------------------------------------------------------------- *
138 	 * methods.                                                         *
139 	 * ---------------------------------------------------------------- */
140 
141 	void put(Deficiency def, BigDecimal prob) {
142 	    this.def2prob.put(def, prob);
143 	    int cmp = prob.compareTo(BigDecimal.ONE);
144 	    if (cmp == 0) {
145 		this.degenerates.add(def);
146 	    }
147 	    if (cmp > 0) {
148 		this.invalids.add(def);
149 	    }
150 	}
151 
152 	void put(Deficiency def, DeficiencySetNode dsn) {
153 	    this.extOrdering.put(def, dsn);
154 	}
155 
156 	DeficiencySetNode get(Deficiency def) {
157 	    return this.extOrdering.get(def);
158 	}
159 
160 	boolean isValid() {
161 	    return this.invalids.isEmpty();
162 	}
163 
164 	boolean isDegenerate() {
165 	    return !this.degenerates.isEmpty();
166 	}
167 
168 	Map<Deficiency, BigDecimal> error() {
169 	    return warningError(this.invalids);
170 	}
171 
172 	Map<Deficiency, BigDecimal> warning() {
173 	    return warningError(this.degenerates);
174 	}
175 
176 	Map<Deficiency, BigDecimal> warningError(Set<Deficiency> invOrDeg) {
177 	    Map<Deficiency, BigDecimal> def2prob = 
178 		new HashMap<Deficiency, BigDecimal>();
179 	    for (Deficiency def : invOrDeg) {
180 		def2prob.put(def, this.def2prob.get(def));
181 	    }
182 
183 	    return def2prob;
184 	}
185 
186 	BigDecimal getProb(Set<Deficiency> defs) {
187 	    Set<Deficiency> elDefs = new HashSet<Deficiency>();
188 	    for (Deficiency def: defs) {
189 		elDefs.addAll(this.extOrdering.get(def).getDeficiencySet());
190 	    }
191 	    BigDecimal result = BigDecimal.ONE;
192 	    for (Deficiency def: elDefs) {
193 		result = result.multiply(this.def2prob.get(def));
194 	    }
195 	    return result;
196 	}
197 
198 	public String toString() {
199 	    StringBuffer res = new StringBuffer();
200 	    res.append("<Validator>\nextOrdering: ");
201 	    res.append(extOrdering);
202 	    res.append("\ndef2prob: ");
203 	    res.append(def2prob);
204 	    res.append("\ninvalids: ");
205 	    res.append(invalids);
206 	    res.append("\ndegenerates: ");
207 	    res.append(degenerates);
208 	    res.append("</Validator>");
209 	    return res.toString();
210 	}
211     } // class Validator 
212 
213     /* ---------------------------------------------------------------- *
214      * attributes.                                                      *
215      * ---------------------------------------------------------------- */
216 
217     /**
218      * The type of this ProbDistr. 
219      * Note that this is not  updated by methods such as 
220      * {@link #remove} or {@link #add} but only set and used 
221      * in the initialization process. 
222      */
223     private Type type;
224 
225     private Map<Deficiency, BigDecimal> def2prob;
226 
227     private Map<Inverter, Validator> validators;
228 
229     /* ---------------------------------------------------------------- *
230      * constructors.                                                    *
231      * ---------------------------------------------------------------- */
232 
233     public ProbDistr(Type type,
234 		     Map<Deficiency, BigDecimal> def2prob) {
235 
236 	checkIn01(def2prob);
237 
238 	this.type = type;
239 	this.def2prob = def2prob;
240     }
241 
242     // already checked by parser: 
243     // old2ProbDistr 
244     // keys exactly the inner classes, 
245     // also distributions fit with inner classes. 
246     //
247     // also, keys of old2ProbDistr and of def2prob are disjoint 
248     // finally, union of keys are exactly deficiencies of type sClass. 
249     public ProbDistr(Type type,
250 		     Map<Deficiency, ProbDistr> old2ProbDistr,
251 		     Map<Deficiency, BigDecimal> def2prob) {
252 	this(type, def2prob);
253 	for (ProbDistr distr : old2ProbDistr.values()) {
254 	    this.def2prob.putAll(distr.def2prob);
255 	}
256     }
257 
258     private static void checkIn01(Map<Deficiency, BigDecimal> def2prob) {
259 	for (BigDecimal prob : def2prob.values()) {
260 	    if (prob.compareTo(BigDecimal.ONE ) >= 0 || 
261 		prob.compareTo(BigDecimal.ZERO) <= 0) {
262 		throw new IllegalArgumentException
263 		("Expected probability in (0, 1) but found " + prob + ". ");
264 	    }
265 	}
266     }
267 
268     /* ---------------------------------------------------------------- *
269      * methods.                                                         *
270      * ---------------------------------------------------------------- */
271 
272     // **** needed by getValidator 
273     /**
274      * Describe <code>init</code> method here.
275      *
276      * @param inv
277      *    determines whether analysis is bottom up or top down. 
278      *    Here: just exchanges min and max. 
279      * @param minDefs
280      *    Invoked with empty set; method collects the minimal Deficiencys. 
281      * @param nMinNodes
282      *    Invoked with empty set; 
283      *    method collects sort of deep copies 
284      *    of the non-minimal DeficiencyNodes here. 
285      * @return 
286      *    a raw <code>Validator</code> for this distribution. 
287      *    Here, raw means that nothing is done 
288      *    but invocation of the constructor. 
289      */
290     private Validator init(Inverter inv,
291 			   Stack<Deficiency> minDefs,
292 			   Set<DeficiencyNode> nMinNodes) {
293 	// look for minimum and maximum. 
294 	for (DeficiencyNode node
295 		 : getType()
296 		 .getDeficiency2ordering()
297 		 .values()) {
298 	    if (inv.getSuccsPreds(node).isEmpty()) {
299 		minDefs.push(node.getDeficiency());
300 	    } else {
301 		// deep copy 
302 		DeficiencyNode cpNode = 
303 		    new DeficiencyNode(node.getDeficiency());
304 		inv.addSuccsPreds(node, cpNode);
305 		//cpNode.addSuccessors(node.getSuccessors());
306 		nMinNodes.add(cpNode);
307 	    }
308 	}
309 	// Here, minDefs/nMinNodes contains the Deficiency(Node)s 
310 	// with(out) successors to be processed 
311 
312 	// copy the probabilities of the minimal deficiencies 
313 	Map<Deficiency, BigDecimal> minDef2prob = 
314 	    new HashMap<Deficiency, BigDecimal>();
315 	for (Deficiency minDef : minDefs) {
316 	    minDef2prob.put(minDef, inv.filterProb(getProb(minDef)));
317 	}
318 
319 	return new Validator(minDef2prob);
320     }
321 
322     // adds intermediate nodes of two classes: 
323     // and-nodes and conditional nodes 
324     // fill the resulting Map with DeficiencySetNodes 
325     // **** currently needed by validate only. 
326     /**
327      *
328      * @param inv
329      *    determines whether analysis is bottom up or top down. 
330      * @return
331      *    a <code>Validator</code>. 
332      */
333    Validator getValidator(Inverter inv) {
334 	// minDefs are the Deficiencies 
335 	// all successors of which are already processed. 
336 	// In the beginning this is just the minimal Deficiency. 
337 	Stack<Deficiency> minDefs = new Stack<Deficiency>();
338 	// The DeficiencyNodes with at least one unprocessed successor. 
339 	// the predeccessors are irrelevant, 
340 	// the successors are just the unprocessed ones. 
341 	// in the very beginning, this includes all but the "minimal" node. 
342 	Set<DeficiencyNode> nMinNodes = new HashSet<DeficiencyNode>();
343 	// the resulting map 
344 	Validator val = init(inv, minDefs, nMinNodes);
345 	// Here, all variables above are initialized. 
346 
347 	while (!minDefs.isEmpty()) {
348 	    // pop element from minDefs and create corresponding 
349 	    // DeficiencySetNode. 
350 
351 	    // pop current Deficiency and get proper node....
352 	    DeficiencyNode node = getType().getDeficiency2ordering()
353 		.get(minDefs.pop());
354 	    // ... unwrap its successors (1st iter.: predDefs.isEmpty()). 
355 	    Set<Deficiency> predDefs = DeficiencyNode.unwrap
356 		(inv.getSuccsPreds(node));
357 
358 	    // between node n and its successors s1, ..., sn 
359 	    // we have to insert &(s1, ..., sn) and n|&(s1, ..., sn) 
360 
361 	    // of the elements in predDefs get the names and 
362 	    // union subseqDefs of their representations as sets. 
363 	    SortedSet<String> namesBelow = new TreeSet<String>();
364 	    Set<Deficiency> subseqDefs = new HashSet<Deficiency>();
365 	    for (Deficiency def : predDefs) {
366 		namesBelow.add(inv.neg() + def.getName());
367 		// extOrdering.get(def) is defined, i.e. not null 
368 		subseqDefs.addAll(val.get(def).getDeficiencySet());
369 	    }
370 	    // Here, namesBelow are the names and subseqDefs 
371 	    // the union of the representatives of the entries of predDefs
372 
373 	    // create the and-node and its probability
374 	    DeficiencySetNode andDefN = 
375 		new DeficiencySetNode(new Deficiency(inv.andOr() + namesBelow),
376 				      subseqDefs);
377 
378 	    BigDecimal andProb = andDefN.getProb(val.def2prob);
379 	    // maybe in later implementations andDefN is also added, 
380 	    // but for now, andDefN is no longer needed. 
381 
382 	    // add cond-node 
383 	    String name = inv.condEventName(node.getDeficiency().getName(),
384 					    namesBelow);
385 	    Deficiency condDef = new Deficiency(name);
386 	    BigDecimal condProb = inv.filterProb(getProb(node.getDeficiency()))
387 		.divide(andProb, MathContext.DECIMAL128);
388 	    val.put(condDef, condProb);
389 
390 	    // add DeficiencySetNode for node 
391 	    Set<Deficiency> subseqCondDefs = 
392 		new HashSet<Deficiency>(subseqDefs);
393 	    subseqCondDefs.add(condDef);
394 	    DeficiencySetNode condDefN = 
395 		new DeficiencySetNode(node.getDeficiency(), subseqCondDefs);
396 	    val.put(node.getDeficiency(), condDefN);
397 
398 	    // update minDefs and nMinNode 
399 	    Iterator<DeficiencyNode> iter = nMinNodes.iterator();
400 	    Set<DeficiencyNode> succs;
401 	    while (iter.hasNext()) {
402 		DeficiencyNode nMinNode = iter.next();
403 		succs = inv.getSuccsPreds(nMinNode);
404 		// remove node
405 		if (succs.remove(node) && succs.isEmpty()) {
406 		    iter.remove();
407 		    minDefs.add(nMinNode.getDeficiency());
408 		} // else, succs is not empty 
409 		// Here, node is removed from succs if it was present. 
410 	    }
411 	    // Here, node does not occur as a successor 
412 	    // within a DeficiencyNode in nMinNodes 
413 
414 	    // also minDefs is the set of deficiencies for nodes 
415 	    // all successors of which are already processed. 
416 	} // stil something to be done 
417 
418 	return val;
419     }
420 
421     /* -------------------------------------------------------------------- *
422      * methods.                                                             *
423      * -------------------------------------------------------------------- */
424 
425     private Type getType() {
426 	return this.type;
427     }
428 
429     // **** what if not defined? 
430     public BigDecimal getProb(Deficiency def) {
431 	BigDecimal res = this.def2prob.get(def);
432 	if (res == null) {
433 	    throw new IllegalArgumentException
434 		("Deficiency \"" + def + "\" is unknown. "); 
435 	}
436 	return res;
437     }
438 
439     public void validate() {
440 	this.validators = new EnumMap<Inverter, Validator>(Inverter.class);
441 	validateUp(Inverter.Identity);
442 	validateUp(Inverter.Invert);
443     }
444 
445     private void validateUp(Inverter inv) {
446 	Validator val = getValidator(inv);
447 	this.validators.put(inv, getValidator(inv));
448 	if (!val.isValid()) {
449 	    throw new IllegalStateException("invalid: " + val.error());
450 	}
451 	if (val.isDegenerate()) {
452 	    System.out.println("degenerate: " + val.warning());
453 	}
454    }
455 
456     // ****
457     ProbDistr remove(Deficiency def) {
458 	return this;
459     }
460 
461     // ****
462     ProbDistr add(Deficiency def) {
463 	return this;
464     }
465 
466     public String toString() {
467 	StringBuffer res = new StringBuffer();
468 	res.append("\n<ProbDistr>");
469 	res.append(this.def2prob);
470 	res.append("\n</ProbDistr>");
471 	return res.toString();
472     }
473 } // ProbDistr