Repository: pgmpy/pgmpy

Issue:
Bug in BIFReader function
### Subject of the issue
There is a bug in the BIFReader function. I think while reading the CPD only the order  in which the states are specified is taken into account , not the actual values of the state.

e.g. variable Pollution {low high} **high - 1 low - 0**
variable Smoker {True False} **True - 0, False-1**

probability ( Cancer | Pollution, Smoker ) {
  (low, True) 0.03, 0.97; 
  (high, True) 0.05, 0.95; **should be 1,0 instead of 0,1**
  (low, False) 0.001, 0.999;**should be 0,1 instead of 1,0**
  (high, False) 0.02, 0.98;
}

probability ( Cancer | Pollution, Smoker ) {
  (low, True) 0.03, 0.97; 
 (low, False) 0.001, 0.999;
  (high, True) 0.05, 0.95;
  (high, False) 0.02, 0.98;
}
The two CPDs are equivalent but read differently by the function.
### Your environment
* pgmpy version 0.1.7
* Python version 3.5
* Operating System Unbuntu16.04

### Steps to reproduce
Attached are two benchmark files cancer1.bif and cancer2.bif with the same data but the order of specifying the states of CPD is changed for one of the tables. Inference on both models gives different results even though the model is the same. 
Sample run file is also attached.

### Expected behaviour
The result of the query should be same in both cases since models are equivalent.

### Actual behaviour
Results with model file1:
```
+----------+---------------+
| Cancer   |   phi(Cancer) |
+==========+===============+
| Cancer_0 |        0.0116 |
+----------+---------------+
| Cancer_1 |        0.9884 |
+----------+---------------+
Results with model file2:
+----------+---------------+
| Cancer   |   phi(Cancer) |
+==========+===============+
| Cancer_0 |        0.0410 |
+----------+---------------+
| Cancer_1 |        0.9590 |
+----------+---------------+
```
[test.zip](https://github.com/pgmpy/pgmpy/files/2775737/test.zip)


Discussion:

Patch:
diff --git a/pgmpy/readwrite/BIF.py b/pgmpy/readwrite/BIF.py
index affdcf8d..af0bbbf2 100644
--- a/pgmpy/readwrite/BIF.py
+++ b/pgmpy/readwrite/BIF.py
@@ -95,7 +95,7 @@ class BIFReader(object):
         # 1.00 or 1 or 1.00. 0.00 or 9.8e-5 etc
         num_expr = Word(nums + '-' + '+' + 'e' + 'E' + '.') + Suppress(Optional(","))
         probability_expr = Suppress('probability') + Suppress('(') + OneOrMore(word_expr) + Suppress(')')
-        optional_expr = Suppress('(') + Suppress(OneOrMore(word_expr2)) + Suppress(')')
+        optional_expr = Suppress('(') + OneOrMore(word_expr2) + Suppress(')')
         probab_attributes = optional_expr | Suppress('table')
         cpd_expr = probab_attributes + OneOrMore(num_expr)
 
@@ -237,21 +237,27 @@ class BIFReader(object):
          """
         variable_cpds = {}
         for block in self.probability_block():
-            name = self.probability_expr.searchString(block)[0][0]
+            names = self.probability_expr.searchString(block)
+            var_name, parents = names[0][0], names[0][1:]
             cpds = self.cpd_expr.searchString(block)
-            arr = [float(j) for i in cpds for j in i]
             if 'table' in block:
+                arr = [float(j) for i in cpds for j in i]
                 arr = numpy.array(arr)
-                arr = arr.reshape((len(self.variable_states[name]),
-                                   arr.size // len(self.variable_states[name])))
+                arr = arr.reshape((len(self.variable_states[var_name]),
+                                   arr.size // len(self.variable_states[var_name])))
             else:
-                length = len(self.variable_states[name])
-                reshape_arr = [[] for i in range(length)]
-                for i, val in enumerate(arr):
-                    reshape_arr[i % length].append(val)
-                arr = reshape_arr
+                length = sum(len(self.variable_states[var]) for var in parents)
+                arr = [[0 for j in range(length)] for i in self.variable_states[var_name]]
+                length = len(self.variable_states[var_name])
+                for prob_line in cpds:
+                    states = prob_line[:len(parents)]
+                    vals = [float(i) for i in prob_line[len(parents):]]
+                    offset = sum((len(parents)-i)*self.variable_states[parents[i]].index(states[i])
+                                 for i in range(len(states)))
+                    for i, val in enumerate(vals):
+                        arr[i][offset] = val
                 arr = numpy.array(arr)
-            variable_cpds[name] = arr
+            variable_cpds[var_name] = arr
 
         return variable_cpds
 


Test patch:
diff --git a/pgmpy/tests/test_readwrite/test_BIF.py b/pgmpy/tests/test_readwrite/test_BIF.py
index 3142f057..e45a9e32 100644
--- a/pgmpy/tests/test_readwrite/test_BIF.py
+++ b/pgmpy/tests/test_readwrite/test_BIF.py
@@ -111,6 +111,51 @@ probability (  "family-out" ) { //1 variable(s) and 2 values
             np_test.assert_array_equal(cpd_expected[variable],
                                        cpd[variable])
 
+    def test_get_values_reordered(self):
+
+        cancer_values1 = BIFReader(string="""
+network unknown {
+}
+variable Pollution {
+  type discrete [ 2 ] { low, high };
+}
+variable Smoker {
+  type discrete [ 2 ] { True, False };
+}
+variable Cancer {
+  type discrete [ 2 ] { True, False };
+}
+probability ( Cancer | Pollution, Smoker ) {
+  (low, True) 0.03, 0.97;
+  (low, False) 0.001, 0.999;
+  (high, True) 0.05, 0.95;
+  (high, False) 0.02, 0.98;
+}
+        """).get_values()
+
+        cancer_values2 = BIFReader(string="""
+network unknown {
+}
+variable Pollution {
+  type discrete [ 2 ] { low, high };
+}
+variable Smoker {
+  type discrete [ 2 ] { True, False };
+}
+variable Cancer {
+  type discrete [ 2 ] { True, False };
+}
+probability ( Cancer | Pollution, Smoker ) {
+  (low, True) 0.03, 0.97;
+  (high, True) 0.05, 0.95;
+  (low, False) 0.001, 0.999;
+  (high, False) 0.02, 0.98;
+}
+        """).get_values()
+
+        for var in cancer_values1:
+            np_test.assert_array_equal(cancer_values1[var], cancer_values2[var])
+
     def test_get_parents(self):
 
         parents_expected = {'bowel-problem': [],