Sample code (Python preferred) for Dynamic Bayesian Network

The engine.
Post Reply
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Hello,

I have searched for sample code showing how to create a DBN and do some inferences with it (preferably but not necessarily in Python), and I
have found nothing. Do you know of any samples you can share with me?

Regards,
Rafael
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

I'll try to explain PySmile DBN usage based on GeNIe documentation: https://support.bayesfusion.com/docs/Ge ... ngdbn.html


1. Create CPT nodes, ex.

Code: Select all

net.add_node("Location")
net.add_node("Rain")
net.add_node("Umbrella")
2. Set node temporal types
plate_explained.png
plate_explained.png (21.17 KiB) Viewed 20212 times

Code: Select all

net.set_node_temporal_type("Rain", pysmile.NodeTemporalType.PLATE)
net.set_node_temporal_type("Umbrella", pysmile.NodeTemporalType.PLATE)
Default type is CONTEMPORAL, so you don't have to change Location node type.

3. Create arcs
temporal_arcs_explained.png
temporal_arcs_explained.png (13.91 KiB) Viewed 20212 times
4. Set node definitions

Code: Select all

net.set_node_definition("Location", [0.5, 0.5])
net.set_node_definition("Rain", [0.9,0.1,0.2,0.8])
net.set_node_temporal_definition("Rain", [0.7,0.3,0.3,0.7,0.001,0.999,0.01,0.99])
5. Update beliefs and check if everything is ok.
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Thank you! However, I got this error message:

Code: Select all

TypeError: add_node(): incompatible function arguments. The following argument types are supported:
    1. (self: pysmile.Network, node_type: int, node_id: unicode) -> int
    2. (self: pysmile.Network, node_type: int) -> int
    3. (self: pysmile.Network) -> int
I fixed it this way

Code: Select all

        handle = net.add_node()
        net.set_node_temporal_type(handle, pysmile.NodeTemporalType.PLATE)
        net.set_node_id(handle,id)
        net.set_node_name(handle, name)
Then I got the following error:

Code: Select all

TypeError: set_node_temporal_definition(): incompatible function arguments. The following argument types are supported:
    1. (self: pysmile.Network, node_handle: int, order: int, definition: List[float]) -> None
    2. (self: pysmile.Network, node_id: unicode, order: int, definition: List[float]) -> None
So I guessed order was the number of time steps in the temporal relation, so I set it to 1, and the network definition is working.

I let you know about the inference, but I have a question. Is it necessary to have some initial condition or a contemporal node.
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Well, net.update_beliefs() seems to go well, but then I have problems printing the posteriors:

Code: Select all

Network written to colaborar_din.xdsl
Starting inferencing...
Posteriors with no evidence set:
P(Colaborar_G = Bajo) = 0.5
P(Colaborar_G = Medio) = 0.5
P(Colaborar_G = Alto) = 0.0
Traceback (most recent call last):
  File "modest_dynamic.py", line 180, in <module>
    InferenceOnNetwork(cn.get_network())
  File "modest_dynamic.py", line 146, in __init__
    self.print_all_posteriors(net)
  File "modest_dynamic.py", line 167, in print_all_posteriors
    self.print_posteriors(net, h)
  File "modest_dynamic.py", line 160, in print_posteriors
    value = net.get_outcome_id(node_handle, i)
RuntimeError: basic_string::_M_construct null not valid
Please, find attached the code and the resulting network.
Attachments
modest_dynamic.txt
(5.48 KiB) Downloaded 648 times
colaborar_din.xdsl
(10.69 KiB) Downloaded 644 times
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

Code: Select all

    def print_posteriors(self, net, node_handle):
        node_id = net.get_node_id(node_handle)
        if net.is_evidence(node_handle):
            print(node_id + " has evidence set (" +
                  net.get_outcome_id(node_handle, 
                                     net.get_evidence(node_handle)) + ")")
        else :
            posteriors = net.get_node_value(node_handle)
            for i in range(0, len(posteriors)):
                value = net.get_outcome_id(node_handle, i)
                p = posteriors[i]
                print("P(" + node_id + " = " + value + ") = " + str(p))
When i reaches 3, Python trying to get element that not exists. You should change

Code: Select all

value = net.get_outcome_id(node_handle, i)
to

Code: Select all

value = net.get_outcome_id(node_handle, i%net.get_outcome_count(node_handle))
temporal_values.png
temporal_values.png (42.53 KiB) Viewed 20183 times
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

I did the changes and it started working! Then I discovered all variables in the initial state of the net have the same probability distribution (0.5, 0.5, 0), so I initialized the top variable (CG) adding the following to my code

Code: Select all

        t = self.stream_table(cpd.flatDist(3))
        net.set_node_definition(cg, t)
Then I found that there was not propagation on the initial state of the net, so I added a full definition for the initial state of the net,

Code: Select all

        t = self.stream_table(cpd.incsub(2))    
        net.set_node_definition(pg, t)
        net.set_node_definition(ag, t)

        t = self.stream_table(cpd.genesp())
        net.set_node_definition(ce, t)

        t = self.stream_table(cpd.join(cpd.genesp(),cpd.incsub(2)))
        net.set_node_definition(pe, t)
        net.set_node_definition(ae, t)

        t = self.stream_table(cpd.compevi())        
        net.set_node_definition(ec, t)
        net.set_node_definition(ep, t)
        net.set_node_definition(ea, t)
and everything is fine now!

Some questions:
  1. Why beliefs initialization goes through 10 steps in time?
  2. How do I add evidences along time? (I have a guess on this one, but I ask it just in case I am wrong)
  3. Is there a way to recover the net at time t (all nodes probability distributions at time t)?
Regards.
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

Why beliefs initialization goes through 10 steps in time?
It depends on Slice (Step) Count variable. You can change it by calling ex. net.set_slice_count(4) (four steps). Remember to update beliefs.
How do I add evidences along time? (I have a guess on this one, but I ask it just in case I am wrong)
net.set_temporal_evidence(handle, slice, outcome)
Is there a way to recover the net at time t (all nodes probability distributions at time t)?
If you want to get network definition, you should call get_node_temporal_definition method for each node. If your intention is to read the node values, use get_node_value method and read values corresponding to searched 't' value.

I hope it helps,
Piotr
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Dear Piotr,

Thank you very much for your help. I can get the 16 time slices I wanted. However, I guess I am doing something wrong because the behaviour is not as expected. Given the temporal evidence

Competencia Tiempo Nivel
'e_proponer' 1 0
'e_aportar' 2 0
'e_proponer' 4 1
'e_aportar' 5 1
'e_colaborar' 7 1
'e_proponer' 10 2
'e_aportar' 11 1
'e_colaborar' 14 1

I get the following results with SMILE:
bm16-smile.png
bm16-smile.png (29.66 KiB) Viewed 20107 times
They are too flat, as if the network is almost no reacting to the evidence. Consider, for example, the same evidence on PGMPY
bm16-pgmpy.png
bm16-pgmpy.png (39.88 KiB) Viewed 20107 times
These results are more credible.

So, I am attaching the code, hoping you have time to have a look at it.
Attachments
modest_dynamic.py.txt
(6.93 KiB) Downloaded 655 times
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

Code: Select all

def print_posteriors(net, node_handle):
        node_id = net.get_node_id(node_handle)
        if net.is_evidence(node_handle):
            print(node_id + " has evidence set (" +
                  net.get_outcome_id(node_handle, 
                                     net.get_evidence(node_handle)) + ")")
        else :
            posteriors = net.get_node_value(node_handle)
            n = net.get_outcome_count(node_handle)
            for i in range(0, len(posteriors)):
                value = value = net.get_outcome_id(node_handle, i%n)
                p = posteriors[i]
                print("P(" + node_id + " = " + value + ") = " + str(p))
You should consider usage of has_temporal_evidence/is_temporal_evidence methods instead of is_evidence method. Slice count should be changed before adding evidence. Apart from these two things, the rest of code seems to be fine.

Could you tell me, how to reproduce these charts?
genie_chart.PNG
genie_chart.PNG (18.16 KiB) Viewed 20082 times
Genie shows, that node values change in time (Aportar_E at image).
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Thank you! I would fix my is_evidence call. The charts are created using a spreadsheets (attached). And yes, the probabilities move, but too little. Consider the first pair or evidences on value 0 for e_proponer and e_aportar, they should have moved the probabilities much more than they are, as it happens with pgmpy. Unless in the SMILE case the temporal conditional are too strong.
Attachments
bajoMedio16-smile.zip
(45.44 KiB) Downloaded 680 times
bajoMedio16-pgmpy.zip
(33.33 KiB) Downloaded 644 times
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

We have noticed that something is not quite right with network definition. You probably have not set nodes definition for t=0 (ex. Proponer_E)
def_ex.PNG
def_ex.PNG (12.31 KiB) Viewed 20043 times
It should be set with method set_node_definition (not like other slices - set_node_temporal_definition). Also notice, that in SMILE 'time' variable starts with 0 - if pgmpy starts with 1, you can get different results.
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

I have the opportunity to install Genie on a Windows machine, and I get a nice picture of my map:
genie.png
genie.png (80.71 KiB) Viewed 19928 times
I have followed the manual and revised the tables, and everything seems to be fine, bot the t = 0 definitions,

Code: Select all

        net.add_arc(cg, pg)
        net.add_arc(cg, ag)
        net.add_arc(cg, ce)
        net.add_arc(pg, pe)
        net.add_arc(ce, pe)
        net.add_arc(ag, ae)
        net.add_arc(ce, ae)
        net.add_arc(ce, ec)
        net.add_arc(pe, ep)
        net.add_arc(ae, ea)

        # Non temporal conditionals. Time 0
        t = self.stream_table(cpd.flatDist(3))
        net.set_node_definition(cg,t)

        t = self.stream_table(cpd.incsub(2))    
        net.set_node_definition(pg, t)
        net.set_node_definition(ag, t)

        t = self.stream_table(cpd.genesp())
        net.set_node_definition(ce, t)

        t = self.stream_table(cpd.join(cpd.genesp(),cpd.incsub(2)))
        net.set_node_definition(pe, t)
        net.set_node_definition(ae, t)

        t = self.stream_table(cpd.compevi())
        net.set_node_definition(ec, t)
        net.set_node_definition(ep, t)
        net.set_node_definition(ea, t)
as the temporal ones,

Code: Select all

        # Temporal arcs
        net.add_temporal_arc(cg,cg,1)
        net.add_temporal_arc(pg,pg,1)
        net.add_temporal_arc(ag,ag,1)
        net.add_temporal_arc(ce,ce,1)
        net.add_temporal_arc(pe,pe,1)
        net.add_temporal_arc(ae,ae,1)


        # Non temporal conditionals. Time 0
        t = self.stream_table(cpd.flatDist(3))
        net.set_node_definition(cg,t)

        t = self.stream_table(cpd.incsub(2))    
        net.set_node_definition(pg, t)
        net.set_node_definition(ag, t)

        t = self.stream_table(cpd.genesp())
        net.set_node_definition(ce, t)

        t = self.stream_table(cpd.join(cpd.genesp(),cpd.incsub(2)))
        net.set_node_definition(pe, t)
        net.set_node_definition(ae, t)

        t = self.stream_table(cpd.compevi())
        net.set_node_definition(ec, t)
        net.set_node_definition(ep, t)
        net.set_node_definition(ea, t)

        # Conditionals that include temporal relations

        tpp = cpd.paspre()
        t = self.stream_table(tpp)
        net.set_node_temporal_definition(cg, 1, t)
        
        t = self.stream_table(cpd.join(cpd.incsub(2),tpp))    
        net.set_node_temporal_definition(pg, 1, t)
        net.set_node_temporal_definition(ag, 1, t)

        t = self.stream_table(cpd.join(cpd.genesp(),tpp))
        net.set_node_temporal_definition(ce, 1, t)

        t = self.stream_table(cpd.join(cpd.genesp(),cpd.incsub(2),tpp))
        net.set_node_temporal_definition(pe, 1, t)
        net.set_node_temporal_definition(ae, 1, t)
Then I add the evidence, and update the beliefs.

Code: Select all

	self.change_evidence_and_update(net, 'e_proponer', 1, 'Bajo')    
        self.change_evidence_and_update(net, 'e_aportar', 2, 'Bajo')    
        self.change_evidence_and_update(net, 'e_proponer', 4, 'Medio')    
        self.change_evidence_and_update(net, 'e_aportar', 5, 'Medio')    
        self.change_evidence_and_update(net, 'e_colaborar', 7, 'Medio')    
        self.change_evidence_and_update(net, 'e_proponer', 10, 'Alto')    
        self.change_evidence_and_update(net, 'e_aportar', 11, 'Medio')    
        self.change_evidence_and_update(net, 'e_colaborar', 14, 'Medio')    
        net.set_slice_count(17)
        net.update_beliefs()
with

Code: Select all

    def change_evidence_and_update(self, net, node_id, n_time, outcome_id):
        if outcome_id is not None:
            net.set_temporal_evidence(node_id, n_time, outcome_id)
        else:
            net.clear_temporal_evidence(node_id, n_time)
Yet, the model seems to be insensitive to to evidence. So I am wondering whether what I miss are initial conditions of some kind, or there is something deep wrong in the model.
Attachments
colaborar_din.xdsl
(11.99 KiB) Downloaded 674 times
piotr [BayesFusion]
Site Admin
Posts: 60
Joined: Mon Nov 06, 2017 6:41 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by piotr [BayesFusion] »

Code: Select all

self.change_evidence_and_update(net, 'e_proponer', 1, 'Bajo')    
self.change_evidence_and_update(net, 'e_aportar', 2, 'Bajo')    
self.change_evidence_and_update(net, 'e_proponer', 4, 'Medio')    
self.change_evidence_and_update(net, 'e_aportar', 5, 'Medio')    
self.change_evidence_and_update(net, 'e_colaborar', 7, 'Medio')    
self.change_evidence_and_update(net, 'e_proponer', 10, 'Alto')    
self.change_evidence_and_update(net, 'e_aportar', 11, 'Medio')    
self.change_evidence_and_update(net, 'e_colaborar', 14, 'Medio')    
net.set_slice_count(17)
net.update_beliefs()
Perhaps you should start setting evidence from time = 0 (decrease each t by one). I have done every step that you mentioned and calculations seems to be fine.
eg. e_colaboral values:
e_colaboral_values.png
e_colaboral_values.png (130.02 KiB) Viewed 19925 times
If you need more informations about DBNs, you can check GeNIe documentation: https://support.bayesfusion.com/docs/Ge ... ction.html
rmorales
Posts: 14
Joined: Mon Oct 29, 2018 11:21 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by rmorales »

Well, it looks like there is some differences in understanding what a DBN is. I was thinking on something that evolves in time; i.e. conditional probabilities in nodes in slice t+1 depend only on conditional probabilities in nodes in the same slice and slice t. So evidences for e_proponer = 0 and e_aportar = 0 in slices 1 and 2 would make conditional probabilities for nodes 'Proponer_E' and 'Aportar_E' to "move" towards 0. But it seems to me now that what the system tries to do is to consider all the slices and evidences all together, as a large static BN, and considering all evidence together.

Is that the case?
shooltz[BayesFusion]
Site Admin
Posts: 1417
Joined: Mon Nov 26, 2007 5:51 pm

Re: Sample code (Python preferred) for Dynamic Bayesian Network

Post by shooltz[BayesFusion] »

conditional probabilities in nodes in slice t+1 depend only on conditional probabilities in nodes in the same slice and slice t.
Your temporal arcs have order 1, therefore your CPTs in t+1 are conditioned on nodes in t+1 and t. However, the calculated posterior probabilities depend on the entire unrolled network and the evidence set. To calculate the posteriors, SMILE unrolls the network into a static BN containing the specified number of slices, performs inference and copies the results into original DBN. You can use the 'Unroll' command in GeNIe to visualize the process.

For more info, see Using GeNIe/Dynamic Bayesian Networks chapter in GeNIe manual.
Post Reply