In [1]:
classpath.addPath("../jsci-1.2.jar")
classpath.addPath("../figaro_2.11-4.1.0.0.jar")



1. 离散分布(离散原子元素)


In [2]:
import com.cra.figaro.language._
import com.cra.figaro.algorithm.factored.VariableElimination


import com.cra.figaro.language._
import com.cra.figaro.algorithm.factored.VariableElimination

In [3]:
// Flip 伯努利分布
val sunnyToday = Flip(0.2)
println(VariableElimination.probability(sunnyToday, true))
println(VariableElimination.probability(sunnyToday, false))


0.2
0.8
sunnyToday: AtomicFlip = Flip(0.2)

In [4]:
// Select 离散分布
val greeting = Select(0.6 -> "Hello, world!", 0.3 -> "Howdy, universe!", 0.1 -> "Oh no, not again")
println(VariableElimination.probability(greeting, "Howdy, universe!"))


0.30000000000000004
greeting: AtomicSelect[String] = Select(0.6 -> Hello, world!, 0.3 -> Howdy, universe!, 0.1 -> Oh no, not again)

In [5]:
// Binomial 二项分布
import com.cra.figaro.library.atomic.discrete.Binomial

val numSunnyDaysInWeek = Binomial(7, 0.2)


import com.cra.figaro.library.atomic.discrete.Binomial
numSunnyDaysInWeek: com.cra.figaro.library.atomic.discrete.AtomicBinomial = Binomial(7, 0.2)

In [6]:
println(VariableElimination.probability(numSunnyDaysInWeek, 1))
//java.lang.NoClassDefFoundError: JSci/maths/ExtraMath


0.3670016

2. 连续分布(连续原子元素)


In [7]:
// 正态分布
import com.cra.figaro.library.atomic.continuous.Normal
val temperature = Normal(40, 100)


import com.cra.figaro.library.atomic.continuous.Normal
temperature: com.cra.figaro.library.atomic.continuous.AtomicNormal = Normal(40.0, 100.0)

In [8]:
import com.cra.figaro.algorithm.sampling.Importance
def greaterThan50(d: Double) = d > 50
println(Importance.probability(temperature, greaterThan50 _))


0.1607999999999988
import com.cra.figaro.algorithm.sampling.Importance
defined function greaterThan50

In [9]:
// 均匀分布
import com.cra.figaro.library.atomic.continuous.Uniform
val temperature2 = Uniform(10, 70)


import com.cra.figaro.library.atomic.continuous.Uniform
temperature2: com.cra.figaro.library.atomic.continuous.AtomicUniform = Uniform(10.0, 70.0)

In [10]:
Importance.probability(temperature2, greaterThan50 _)


res9: Double = 0.33569999999997957

3. 复合元素

3.1 If


In [11]:
// If随机过程,首先检查测试结果,如果测试值是true则生成then子句的值,否则生成else子句的值
import com.cra.figaro.library.compound.If

val sunnyToday = Flip(0.2)
val greetingToday = If(sunnyToday, 
                    Select(0.6 -> "Hello, world!", 0.4 -> "Howdy, universe!"),
                    Select(0.2 -> "Hello, world!", 0.8 -> "Oh no, not again"))


import com.cra.figaro.library.compound.If
sunnyToday: AtomicFlip = Flip(0.2)
greetingToday: com.cra.figaro.library.compound.If[String] = If(Flip(0.2), Select(0.6 -> Hello, world!, 0.4 -> Howdy, universe!), Select(0.2 -> Hello, world!, 0.8 -> Oh no, not again))

In [12]:
println(VariableElimination.probability(greetingToday, "Hello, world!"))


0.27999999999999997


In [13]:
sunnyToday.observe(true)
println(VariableElimination.probability(greetingToday, "Hello, world!"))


0.6


In [14]:
sunnyToday.observe(false)
println(VariableElimination.probability(greetingToday, "Hello, world!"))


0.2

3.2 Dist


In [15]:
// Dist, 它选择一组元素中的一个而不是选择一组值中的一个
val goodMood = Dist(0.2 -> Flip(0.6), 0.8 -> Flip(0.2))


goodMood: AtomicDist[Boolean] = Dist(0.2 -> Flip(0.6), 0.8 -> Flip(0.2))

In [16]:
// print 0.28 = 0.2*0.6 + 0.8*0.2
println(VariableElimination.probability(goodMood, true))


0.28

3.3 原子元素的复合版本


In [17]:
// 晴天的概率不确定
val sunnyTodayProbability = Uniform(0, 0.5)
val sunnyToday = Flip(sunnyTodayProbability)
println(Importance.probability(sunnyToday, true))


0.2602000000000055
sunnyTodayProbability: com.cra.figaro.library.atomic.continuous.AtomicUniform = Uniform(0.0, 0.5)
sunnyToday: Flip = Flip(Uniform(0.0, 0.5))

In [18]:
// 均值和方差都不确定
val tempMean = Normal(40, 9)
val tempVariance = Select(0.5 -> 80.0, 0.5 -> 105.0)
val temperature = Normal(tempMean, tempVariance)
println(Importance.probability(temperature, (d: Double) => d > 50))


0.1654999999999983
tempMean: com.cra.figaro.library.atomic.continuous.AtomicNormal = Normal(40.0, 9.0)
tempVariance: AtomicSelect[Double] = Select(0.5 -> 80.0, 0.5 -> 105.0)
temperature: com.cra.figaro.library.atomic.continuous.CompoundNormal = Normal(Normal(40.0, 9.0), Select(0.5 -> 80.0, 0.5 -> 105.0))

4. Apply和Chain构建更复杂的模型

4.1 Apply

Apply以一个元素和一个Scala函数作为参数,它代表将Scala函数应用到该元素值以获得新值的过程。


In [19]:
val sunnyDaysInMonth = Binomial(30, 0.2)
def getQuality(i: Int): String =
    if(i > 10) "good"; else if (i > 5) "average"; else "poor"
val monthQuality = Apply(sunnyDaysInMonth, getQuality)


sunnyDaysInMonth: com.cra.figaro.library.atomic.discrete.AtomicBinomial = Binomial(30, 0.2)
defined function getQuality
monthQuality: Apply1[Int, String] = Apply(Binomial(30, 0.2), <function1>)

In [20]:
println(VariableElimination.probability(monthQuality, "good"))


0.025616255335326694

Apply元素定义随机过程,它首先生成第一个元素参数的值,然后,该过程取得第二个函数参数,并将其应用到生成的值。

4.2 Chain

Chain用于将元素链接为一个模型,模型中的元素依赖于另一个元素,那个元素又依赖其他的元素,以此类推。这和概率的链式法则相关。 Chain和Apply之间的差别是Apply中的函数返回常规的Scala值,而Chain中的函数返回一个元素。


In [21]:
val goodMood = Chain(monthQuality, (s: String) =>
                    if(s == "good") Flip(0.9)
                    else if (s == "average") Flip(0.6)
                    else Flip(0.1))


goodMood: Chain[String, Boolean] = Chain(Apply(Binomial(30, 0.2), <function1>), <function1>)

In [22]:
println(VariableElimination.probability(goodMood, true))


0.3939286578054374

5. 条件和约束指定证据

5.1 观测值


In [23]:
greetingToday.observe("Hello, world!")




In [24]:
println(VariableElimination.probability(sunnyToday, true))


Warning: you are using a factored algorithm with continuous or infinite elements. The element will be sampled 15 times
0.23561405398220533


In [25]:
greetingToday.unobserve()




In [26]:
println(VariableElimination.probability(sunnyToday, true))


0.23561405398220536

5.2 条件

observe指定元素的特定值,条件指定一个布尔函数。

val sunnyDaysInMonth = Binomial(30, 0.2)
def getQuality(i: Int): String =
    if(i > 10) "good"; else if (i > 5) "average"; else "poor"
val monthQuality = Apply(sunnyDaysInMonth, getQuality)
val goodMood = Chain(monthQuality, (s: String) =>
                    if(s == "good") Flip(0.9)
                    else if (s == "average") Flip(0.6)
                    else Flip(0.1))

In [27]:
println(VariableElimination.probability(goodMood, true))


0.3939286578054374


In [30]:
// 规定sunnyDaysInMonth必须大于8,好心情的概率明显上升
sunnyDaysInMonth.setCondition((i: Int) => i > 8)
println(VariableElimination.probability(goodMood, true))


0.6597344078195808


In [31]:
// sunnyDaysInMonth必须比3的倍数大2,排除9和10等等情况的可能
sunnyDaysInMonth.addCondition((i: Int) => i % 3 == 2)
println(VariableElimination.probability(goodMood, true))


0.9


In [32]:
sunnyDaysInMonth.removeConditions()
println(VariableElimination.probability(goodMood, true))


0.3939286578054374

5.3 约束

约束是从一个元素值到Double类型的函数,约束改变不同状态的概率。


In [33]:
// 表示为goodMood为true的可能性只是false的一半
goodMood.addConstraint((b: Boolean) => if(b) 0.5; else 1.0)




In [34]:
println(VariableElimination.probability(goodMood, true))


0.24527469450215497

作为连接元素的约束


In [35]:
// 定义三场比赛的结果
val result1 = Flip(0.4)
val result2 = Flip(0.4)
val result3 = Flip(0.4)


result1: AtomicFlip = Flip(0.4)
result2: AtomicFlip = Flip(0.4)
result3: AtomicFlip = Flip(0.4)

In [36]:
val allWins = Apply(result1, result2, result3,
                   (w1: Boolean, w2: Boolean, w3: Boolean) => w1 && w2 && w3)


allWins: Apply3[Boolean, Boolean, Boolean, Boolean] = Apply(Flip(0.4), Flip(0.4), Flip(0.4), <function3>)

In [37]:
println(VariableElimination.probability(allWins, true))


0.06400000000000002


In [38]:
def makeStreaky(r1: Element[Boolean], r2: Element[Boolean]) {
    val pair = Apply(r1, r2, (b1: Boolean, b2: Boolean) => (b1, b2))
    pair.setConstraint((bb: (Boolean, Boolean)) =>
                      if(bb._1 == bb._2) 1.0; else 0.5)
}


defined function makeStreaky

In [39]:
// 由于描述了相邻比赛的连续性,使得比赛全胜的概率增加
makeStreaky(result1, result2)
makeStreaky(result2, result3)
println(VariableElimination.probability(allWins, true))


0.11034482758620692