2424from itertools import product
2525from collections import defaultdict
2626
27- from pgmpy .models import BayesianNetwork , NaiveBayes , DynamicBayesianNetwork , MarkovNetwork
27+ from pgmpy .models import BayesianNetwork , NaiveBayes , MarkovNetwork
28+ from pgmpy .models import DynamicBayesianNetwork as DBN
2829from pgmpy .factors .discrete import TabularCPD
2930from pgmpy .metrics import structure_score
3031
@@ -110,6 +111,14 @@ def make_DAG(DAG, CPD=None, methodtype='bayes', checkmodel=True, verbose=3):
110111 >>> DAG = bn.make_DAG(edges, methodtype='naivebayes')
111112 >>> fig = bn.plot(DAG)
112113
114+ Examples
115+ --------
116+ >>> import bnlearn as bn
117+ >>> edges = [('A', 'B'), ('A', 'C'), ('A', 'D')]
118+ >>> CPD = build_cpts_from_structure(DAG, variable_card=3)
119+ >>> DAG = bn.make_DAG(edges, CPD=CPD, methodtype='naivebayes')
120+ >>> fig = bn.plot(DAG)
121+
113122 Examples
114123 --------
115124 >>> import bnlearn as bn
@@ -130,11 +139,15 @@ def make_DAG(DAG, CPD=None, methodtype='bayes', checkmodel=True, verbose=3):
130139 >>> fig = bn.plot(DAG)
131140
132141 """
133- if (CPD is not None ) and (not isinstance (CPD , list )):
134- CPD = [CPD ]
135-
142+ # Set names to lower
136143 if methodtype == 'nb' : methodtype = 'naivebayes'
137144 if methodtype == 'dbn' : methodtype = 'DBN'
145+ # Automatically generate placeholder values for the CPTs
146+ if CPD is None and isinstance (DAG , list ):
147+ CPD = build_cpts_from_structure (DAG , variable_card = 2 , methodtype = methodtype )
148+ # Make list if required
149+ if (CPD is not None ) and (not isinstance (CPD , list )):
150+ CPD = [CPD ]
138151
139152 if isinstance (DAG , dict ):
140153 DAG = DAG .get ('model' , None )
@@ -153,30 +166,42 @@ def make_DAG(DAG, CPD=None, methodtype='bayes', checkmodel=True, verbose=3):
153166 edges = DAG
154167 DAG = NaiveBayes ()
155168 DAG .add_edges_from (edges )
156- # modeel.add_nodes_from(DAG)
169+ DAG .add_nodes_from (CPD )
170+ for cpd in CPD : DAG .add_cpds (cpd )
157171 elif isinstance (DAG , list ) and methodtype == 'bayes' :
158172 if verbose >= 3 : print ('[bnlearn] >%s DAG created.' % (methodtype ))
159- DAG = BayesianNetwork (DAG )
173+ edges = DAG
174+ DAG = BayesianNetwork ()
175+ DAG .add_edges_from (edges )
176+ DAG .add_nodes_from (CPD )
177+ for cpd in CPD : DAG .add_cpds (cpd )
160178 elif isinstance (DAG , list ) and methodtype == 'markov' :
161179 if verbose >= 3 : print ('[bnlearn] >%s DAG created.' % (methodtype ))
162- DAG = MarkovNetwork (DAG )
180+ edges = DAG
181+ DAG = MarkovNetwork ()
182+ DAG .add_edges_from (edges )
183+ DAG .add_nodes_from (CPD )
184+ # for cpd in CPD: DAG.add_cpds(cpd)
163185 elif isinstance (DAG , list ) and methodtype == 'DBN' :
164- if verbose >= 3 : print ('[bnlearn] >DynamicBayesianNetwork DAG created.' )
186+ if verbose >= 3 : print ('[bnlearn] >DynamicBayesianNetwork (DBN) DAG created.' )
165187 # Make edges with time slice
166188 if not has_valid_time_slice (DAG ):
167- edges = convert_edges_with_time_slice (DAG )
189+ edges = convert_edges_with_time_slice (DAG , verbose = verbose )
168190 else :
169191 edges = DAG
170192
171- DAG = DynamicBayesianNetwork ()
172- # dbn.add_nodes_from(edges)
193+ DAG = DBN ()
173194 DAG .add_edges_from (edges )
195+ # DAG.add_nodes_from(CPD)
196+ # DAG.add_cpds(CPD)
197+ for cpd in CPD : DAG .add_cpds (cpd )
198+
174199 # Print edges
175200 # print("Edges in the DBN:", DAG.edges())
176201
177202 if CPD is not None :
178203 for cpd in CPD :
179- DAG .add_cpds (cpd )
204+ # DAG.add_cpds(cpd)
180205 if verbose >= 3 : print (f'[bnlearn] >[Conditional Probability Table (CPT)] >[Update Probabilities] >[Node { cpd .variable } ]' )
181206 # Check model
182207 if checkmodel :
@@ -192,14 +217,15 @@ def make_DAG(DAG, CPD=None, methodtype='bayes', checkmodel=True, verbose=3):
192217
193218
194219# %%
195- def convert_edges_with_time_slice (edges , time_slice = 0 ):
220+ def convert_edges_with_time_slice (edges , time_slice = 0 , verbose = 3 ):
221+ if verbose >= 3 : print (f'[bnlearn]> Converting edges to time slice.' )
196222 return [( (u , time_slice ), (v , time_slice ) ) for u , v in edges ]
197223
198224def has_valid_time_slice (edges ):
199- """ Example usage
225+ """ Example usage.
200226 edges = [('A', 'B'), ('A', 'C'), ('A', 'D')]
201227 edgest = [(('A', 0), ('B', 0)), (('A', 0), ('C', 0)), (('A', 0), ('D', 0))]
202-
228+
203229 # Check edges
204230 has_valid_time_slice(edges)
205231
@@ -320,14 +346,21 @@ def check_model(DAG, verbose=3):
320346 None.
321347
322348 """
323- if isinstance (DAG , dict ): DAG = DAG .get ('model' , None )
324- if DAG is not None :
349+ # Get the model
350+ if isinstance (DAG , dict ):
351+ DAG = DAG .get ('model' , None )
352+
353+ # Not all models do have get_cpds function
354+ if DAG is not None and hasattr (DAG , 'get_cpds' ):
325355 for cpd in DAG .get_cpds ():
326356 if not np .all (cpd .values .astype (Decimal ).sum (axis = 0 )== 1 ):
327357 if verbose >= 3 : print (f'[bnlearn] >[Conditional Probability Table (CPT)] >[Check Probabilities] >[Node { cpd .variable } ] >Table Error: Does not sum to 1 but is [{ cpd .values .sum (axis = 0 )} ]' )
328358 else :
329359 if verbose >= 3 : print (f'[bnlearn] >[Conditional Probability Table (CPT)] >[Check Probabilities] >[Node { cpd .variable } ] >OK' )
330360 # if verbose>=3: print('[bnlearn] >Check whether CPDs associated with the nodes are consistent: %s' %(DAG.check_model()))
361+ elif 'markovnetwork' in str (type (DAG )).lower ():
362+ pass
363+ # if verbose>=3: print(f'[bnlearn] >[Conditional Probability Table (CPT)] >[Check Probabilities] >Unknown')
331364 else :
332365 if verbose >= 2 : print ('[bnlearn] >No model found containing CPDs.' )
333366
@@ -2254,7 +2287,7 @@ def probs_rulebook(node, rulebook, variable_card, all_combos):
22542287 elif isinstance (out , (list , tuple )) and len (out ) == variable_card :
22552288 row = list (out )
22562289 else :
2257- raise ValueError (f"Rule for '{ node } ' must return float or list of len { variable_card } " )
2290+ raise ValueError (f"[bnlearn] > Rule for '{ node } ' must return float or list of len { variable_card } " )
22582291 raw_probs .append (row )
22592292 probs = list (map (list , zip (* raw_probs )))
22602293 return probs
@@ -2299,7 +2332,30 @@ def generate_cpt(node, parents, variable_card=2, rulebook=None, verbose=3):
22992332 >>> DAG = bn.make_DAG(edges, CPD=[cpt_Rain, cpt_Sprinkler])
23002333 >>> bn.plot(DAG)
23012334
2335+ Examples
2336+ --------
2337+ >>> # Example with DBN
2338+ >>> #
2339+ >>> # Import library
2340+ >>> import bnlearn as bn
2341+ >>> #
2342+ >>> edges = [('Cloudy', 'Rain'), ('Cloudy', 'Sprinkler')]
2343+ >>> edges = convert_edges_with_time_slice(edges)
2344+ >>> #
2345+ >>> # Get parents
2346+ >>> parents = bn.get_parents(edges)
2347+ >>> # {('Rain', 0): [('Cloudy', 0)], ('Sprinkler', 0): [('Cloudy', 0)], ('Cloudy', 0): []}
2348+ >>> #
2349+ >>> # Generate the CPTs
2350+ >>> cpt_Rain = bn.generate_cpt(('Rain', 0), parents.get(('Rain', 0)), variable_card=2)
2351+ >>> cpt_Rain = bn.generate_cpt(('Sprinkler', 0), parents.get(('Sprinkler', 0)), variable_card=2)
2352+ >>> #
2353+ >>> # Create DAG with default CPD values
2354+ >>> DAG = bn.make_DAG(edges, CPD=[cpt_Rain, cpt_Sprinkler])
2355+ >>> bn.plot(DAG)
2356+
23022357 """
2358+ parents = parents or []
23032359 n_parents = len (parents )
23042360 parent_card = [variable_card ] * n_parents
23052361 all_combos = list (product (range (variable_card ), repeat = n_parents ))
@@ -2317,12 +2373,33 @@ def generate_cpt(node, parents, variable_card=2, rulebook=None, verbose=3):
23172373 evidence_card = parent_card if parents else None )
23182374
23192375 if verbose >= 3 :
2320- print (f'CPT for { node } :' )
2376+ print (f'[bnlearn] > CPT for { node } :' )
23212377 print (cpt )
2378+
23222379 return cpt
2380+ # n_parents = len(parents)
2381+ # parent_card = [variable_card] * n_parents
2382+ # all_combos = list(product(range(variable_card), repeat=n_parents))
2383+ # n_combos = len(all_combos)
2384+
2385+ # if rulebook and node in rulebook:
2386+ # probs = probs_rulebook(node, rulebook, variable_card, all_combos)
2387+ # else:
2388+ # probs = [[1 / variable_card] * n_combos for _ in range(variable_card)]
2389+
2390+ # cpt = TabularCPD(variable=node,
2391+ # variable_card=variable_card,
2392+ # values=probs,
2393+ # evidence=parents if parents else None,
2394+ # evidence_card=parent_card if parents else None)
23232395
2396+ # if verbose >= 3:
2397+ # print(f'[bnlearn] >CPT for {node}:')
2398+ # print(f'{cpt}')
2399+ # return cpt
23242400
2325- def build_cpts_from_structure (edges , variable_card = 2 , rulebook = None , verbose = 3 ):
2401+
2402+ def build_cpts_from_structure (edges , variable_card = 2 , rulebook = None , methodtype = None , verbose = 3 ):
23262403 """
23272404 Automatically generates placeholder CPTs for all nodes in a network structure.
23282405
@@ -2354,7 +2431,12 @@ def build_cpts_from_structure(edges, variable_card=2, rulebook=None, verbose=3):
23542431 >>> # Create DAG with default CPD values
23552432 >>> DAG = bn.make_DAG(edges, CPD=CPD)
23562433 >>> bn.plot(DAG)
2434+
23572435 """
2436+ if verbose >= 3 : print ('[bnlearn]> Auto generate placeholders for the CPTs.' )
2437+ # Convert edges with time for DBN
2438+ if methodtype == 'DBN' and not has_valid_time_slice (edges ):
2439+ edges = convert_edges_with_time_slice (edges , verbose = verbose )
23582440
23592441 cpts = []
23602442 parents_map = get_parents (edges )
0 commit comments