1 /** 2 Authors: [Ilya Yaroshenko](http://9il.github.io) 3 4 Copyright: © 2014-2015 [Ilya Yaroshenko](http://9il.github.io) 5 6 License: MIT 7 */ 8 module atmosphere.finitemixture; 9 10 import std.range; 11 import std.traits; 12 13 /++ 14 Returns: Callable structure 15 Params: 16 funcs = input range of functions 17 weights = mixture weights 18 +/ 19 auto finiteMixture(FRange, T)(FRange funcs, const(T)[] weights) 20 if( 21 isForwardRange!FRange && 22 isCallable!(ElementType!FRange) && 23 isFloatingPoint!T) 24 in { 25 assert(weights.length); 26 static if(hasLength!FRange) 27 assert(funcs.length == weights.length); 28 } 29 body { 30 struct Result(T, FRange) 31 if( 32 isForwardRange!FRange && 33 isCallable!(ElementType!FRange) && 34 isFloatingPoint!T) 35 { 36 private FRange funcs; 37 private const(T)[] weights; 38 this(T)(FRange funcs, const(T)[] weights) 39 { 40 this.funcs = funcs; 41 this.weights = weights; 42 } 43 T opCall(T x) 44 { 45 T s = 0; 46 auto funcs = this.funcs.save; 47 foreach(w; weights) 48 { 49 assert(!funcs.empty); 50 s += funcs.front()(x); 51 funcs.popFront; 52 } 53 return s; 54 } 55 } 56 return Result!(T, FRange)(funcs, weights); 57 } 58 59 /// 60 unittest 61 { 62 import std.algorithm : map; 63 import std.range : sequence; 64 import atmosphere.pdf; 65 import atmosphere.utilities; 66 auto pdfs = sequence!"n+1"().map!(shape => GammaSPDF!real(shape, 1)); 67 double[] weights = [0.25, 0.5, 0.125, 0.125]; 68 auto pdf0 = finiteMixture(pdfs, weights); //struct 69 auto pdf1 = finiteMixture(pdfs[3..4].chain(pdfs[0..3]), [0.125, 0.25, 0.5, 0.125]); //struct 70 PDF!double pdf2 = toPDF(pdf0); //object 71 foreach(x; iota(1, 4)) 72 assert(approxEqual(pdf1(x), pdf2(x))); 73 }