MiniTuring (p46)
The ladder of causation
![]() | ![]() |
Authors: Aymen Merrouche and Pierre-Henri Wuillemin.
This notebook follows the example from “The Book Of Why” (Pearl, 2018) chapter 1 page 046
import pyagrum as gumimport pyagrum.lib.notebook as gnbimport pyagrum.causal as cslimport pyagrum.causal.notebook as cslnbIn order for a prisoner to be executed, a certain chain of events must take place. First, the court gives the execution order to the captain. Then, the captain gives the signal to the firing squad (two soldiers A and B) to shoot the prisoner. Finally, the soldiers obey the commandment and shoot at the prisoner.
We create the causal diagram:
Section titled “We create the causal diagram:”The corresponding causal diagram is the following:
mtt = gum.fastBN("court order->captain->soldier A->death<-soldier B<-captain")mtt## filling the CPTsdef smallPertubation(cpt, epsilon=0.0001): cpt.fillWith(cpt.translate(epsilon).normalizeAsCPT())
mtt.cpt("court order")[:] = [0.5, 0.5]
## The captain will give the signal only if the court gives the order.mtt.cpt("captain")[{"court order": 0}] = [1, 0]mtt.cpt("captain")[{"court order": 1}] = [0, 1]
## The soldiers only fire if the captain gives them the signal.mtt.cpt("soldier A")[{"captain": 0}] = [1, 0] # c=0mtt.cpt("soldier A")[{"captain": 1}] = [0, 1] # c=1smallPertubation(mtt.cpt("soldier A"))
mtt.cpt("soldier B")[{"captain": 0}] = [1, 0] # c=0mtt.cpt("soldier B")[{"captain": 1}] = [0, 1] # c=1smallPertubation(mtt.cpt("soldier B"))
## It only takes one of the two soldiers to shoot for the prisoner to die.mtt.cpt("death")[{"soldier A": 0, "soldier B": 0}] = [1, 0] # a=0,b=0mtt.cpt("death")[{"soldier A": 0, "soldier B": 1}] = [0, 1] # a=0,b=1mtt.cpt("death")[{"soldier A": 1, "soldier B": 0}] = [0, 1] # a=1,b=0mtt.cpt("death")[{"soldier A": 1, "soldier B": 1}] = [0, 1] # a=1,b=1smallPertubation(mtt.cpt("death"))gnb.sideBySide( mtt, mtt.cpt("court order"), mtt.cpt("captain"), mtt.cpt("soldier A"), mtt.cpt("soldier B"), mtt.cpt("death"), captions=[ "the BN", "the marginal for $court order$", "the CPT for $captain$", "the CPT for $soldier A$", "the CPT for $soldier B$", "the CPT for $death$", ],)## gum.saveBN(mtt,os.path.join("out","MiniTuringTest.o3prm"))1- Answering observational queries (rung one of the ladder of causation) :
Section titled “1- Answering observational queries (rung one of the ladder of causation) :”Consists of finding variables that are associated by collecting and analyzing raw data. It allows us to answer queries based on passive observation of data If I observe variable , what can I say about variable ? (i.e )
If the prisoner is dead, does that mean the court order was given?
Section titled “If the prisoner is dead, does that mean the court order was given?”i.e. We are interested in distribution:
## inference engine : LazyPropagationie = gum.LazyPropagation(mtt)
## Knowing that death = 1ie.setEvidence({"death": 1})ie.makeInference()gnb.sideBySide( ie.posterior("court order"), gnb.getInference(mtt, evs={"death": 1}), captions=["$P(court order|death=1)", "Complete inference with evidence={deah:1}"],)If the prisoner is dead, this means that both soldiers fired, the captain gave the signal to the firing squad and the court ordered the execution.
Suppose we find out that A fired. What does that tell us about B?
Section titled “Suppose we find out that A fired. What does that tell us about B?”i.e. We are interested in distribution:
gnb.showInference(mtt, evs={"soldier A": 1})Following the diagram, we can say that B fired too since A wouldn’t have fired if the captain didn’t give the signal.
2- Answering interventional queries (rung two of the ladder of causation) :
Section titled “2- Answering interventional queries (rung two of the ladder of causation) :”Consists of predicting the effect of a deliberate intervention What would be, if I do ? (i.e. ) Interventional queries can not be answered using only passively collected data.
mttModele = csl.CausalModel(mtt)gum.config.push()gum.config["notebook", "graph_format"] = "png"gnb.show(mttModele)gum.config.pop()
What if Soldier A decides on his own initiative to fire, without waiting for the captain’s command? Will the prisoner be dead or alive?
Section titled “What if Soldier A decides on his own initiative to fire, without waiting for the captain’s command? Will the prisoner be dead or alive?”## We do(soldier A = 1) (a deliberate intervention)cslnb.showCausalImpact(mttModele, "death", doing="soldier A", values={"soldier A": 1})|
|
|
|---|---|
| 0.0001 | 0.9999 |
Even if the captain didn’t give the command to the firing squad, the prisoner still died we soldier A decided to shoot him.
Subset {captain} meets the back-door criterion relative to (soldier A, death) because:
- 1- no node in {captain} is a descendant of “soldier A”
- 2- {captain} blocks every path between “soldier A” and “death” that contains an arrow into “death”
So {captain} satisfies the back-door criterion relative to (soldier A, death), the causal effect of “soldier A” over “death” is given by the formula
What if Soldier A decides on his own initiative to fire, without waiting for the captain’s command? What does that tell us about Soldier B?
Section titled “What if Soldier A decides on his own initiative to fire, without waiting for the captain’s command? What does that tell us about Soldier B?”## We do(soldier A = 1) (a deliberate intervention)cslnb.showCausalImpact(mttModele, on="soldier B", doing="soldier A", values={"soldier A": 1})|
|
|
|---|---|
| 0.5000 | 0.5000 |
A’s spontaneous decision shouldn’t affect variables in the model that are not descendants of A. If we see that A shot, we conclude that B shot too (he wouldn’t have shot without the captain’s order). But if we make A shoot spontaneously without waiting for the captain’s order, we have no information about B. This is the difference between seeing and doing.
What if the captain decides on his own initiative to give the firing order, without waiting for the court order’s command?
Section titled “What if the captain decides on his own initiative to give the firing order, without waiting for the court order’s command?”mttModele = csl.CausalModel(mtt)## We do(soldier A = 1) (a deliberate intervention)cslnb.showCausalImpact(mttModele, "death", doing="captain", values={"captain": 1})|
|
|
|---|---|
| 0.0001 | 0.9999 |
Subset {soldier A, soldier B} meets the front-door criterion relative to (captain, death) because:
- 1- {soldier A, soldier B} intercepts all directed paths from “captain” to “death”
- 2- There is no back-door path from “captain” to {soldier A, soldier B}.
- 3- All back-door paths from {soldier A, soldier B} to “death” are blocked by “captain.”
3- Answering counterfactual queries (rung three of the ladder of causation) :
Section titled “3- Answering counterfactual queries (rung three of the ladder of causation) :”Consists of reasoning about hypothetical situations What would have happened if?
Suppose the prisoner is lying dead on the ground. From this, we can conclude (using level one) that A shot, B shot, the captain gave the signal, and the court gave the order. But what if A had decided not to shoot? Would the prisoner be alive?
Section titled “Suppose the prisoner is lying dead on the ground. From this, we can conclude (using level one) that A shot, B shot, the captain gave the signal, and the court gave the order. But what if A had decided not to shoot? Would the prisoner be alive?”To answer this question we need to compare the real world where the prisoner is lying dead on the ground, A shot, B shot, the captain gave the signal to the firing squad and the court gave the order : With a counterfactual world where "", and where the arrow leading into A is erased (since he decided on his own not to shoot we emancipate it from the effect of the captain).
mttCounterfactual = gum.BayesNet(mtt)
## The court did give the ordermttCounterfactual.cpt("court order")[:] = [0, 1]
## Soldier A decides not to shootmttCounterfactual.cpt("soldier A")[0, :] = [1, 0] # c=0mttCounterfactual.cpt("soldier A")[1, :] = [1, 0] # c=1
## We emancipate soldier AmttCounterfactual.eraseArc("captain", "soldier A")gnb.showBNDiff(mtt, mttCounterfactual, size="4")gnb.sideBySide( mttCounterfactual, mttCounterfactual.cpt("court order"), mttCounterfactual.cpt("captain"), mttCounterfactual.cpt("soldier A"), mttCounterfactual.cpt("soldier B"), mttCounterfactual.cpt("death"), captions=[ "the BN", "the marginal for $court order$", "the CPT for $captain$", "the CPT for $soldier A$", "the CPT for $soldier B$", "the CPT for $death$", ],)gnb.showInference(mttCounterfactual)Even if A decides not to shoot in the counterfactual world, the court did give the order to the captain who gave the signal to the firing squad including A who decided not to shoot, however, B shot and killed the prisoner.
Mathematically, this counterfactual is the following conditional probability: where variables with a subscript C are unobserved (and unobservable) variables that live in the counterfactual world, while variables without subscript are observable.
