|
| 1 | +Load Rules and Facts From File |
| 2 | +================================== |
| 3 | + |
| 4 | +Introduction |
| 5 | +------------ |
| 6 | +Loading facts and rules from files is crucial for practical PyReason usage. |
| 7 | +It provides scalability for large rule sets, reusability across projects, |
| 8 | +and allows non-programmers to edit domain knowledge without touching Python code. |
| 9 | + |
| 10 | +In this tutorial, we will focus on four functions that load facts and rules |
| 11 | +from CSV or JSON files: ``add_fact_from_csv``, ``add_fact_from_json``, |
| 12 | +``add_rule_from_csv``, and ``add_rule_from_json``. |
| 13 | + |
| 14 | +.. note:: |
| 15 | + Find the full, executable code `here <https://github.com/lab-v2/pyreason/blob/main/examples/load_rules_facts_from_file/load_rules_facts_from_file_ex.py>`_ |
| 16 | + |
| 17 | +Graph |
| 18 | +---------------------- |
| 19 | +Let's build a simple student-major-department knowledge graph. Alice, Bob, |
| 20 | +and Mary are students — Alice and Bob enroll in the math major, while Mary |
| 21 | +enrolls in CS. Each major belongs to a department: math belongs to the math |
| 22 | +department, and CS belongs to the CS department. |
| 23 | + |
| 24 | +The enrollment relationships are defined as graph edges. The ``in_department`` |
| 25 | +and ``scholarship`` relationships are loaded from external files as facts instead. |
| 26 | + |
| 27 | +.. code:: python |
| 28 | +
|
| 29 | + import networkx as nx |
| 30 | +
|
| 31 | + g = nx.DiGraph() |
| 32 | + g.add_nodes_from(['alice', 'bob', 'mary']) # students |
| 33 | + g.add_nodes_from(['math', 'cs']) # majors |
| 34 | + g.add_nodes_from(['math_dept', 'cs_dept']) # departments |
| 35 | +
|
| 36 | + g.add_edge('alice', 'math', enroll=1) |
| 37 | + g.add_edge('bob', 'math', enroll=1) |
| 38 | + g.add_edge('mary', 'cs', enroll=1) |
| 39 | +
|
| 40 | +
|
| 41 | +Load Rules from CSV |
| 42 | +---------------------- |
| 43 | +Rules can be loaded from a CSV file. Each row has four columns: |
| 44 | +``rule_text``, ``name``, ``infer_edges``, ``set_static``. |
| 45 | + |
| 46 | +.. code:: text |
| 47 | +
|
| 48 | + rule_text,name,infer_edges,set_static |
| 49 | + "under_department(X,Y) <-0 enroll(X,Z), in_department(Z,Y)",under_department_rule,true,false |
| 50 | + "eligible(X) <-1 under_department(X,Y), scholarship(Y)",eligible_scholarship_rule,false,false |
| 51 | +
|
| 52 | +Note: when the rule text contains a comma, wrap the whole field in quotes. |
| 53 | + |
| 54 | +Then load the file using: |
| 55 | + |
| 56 | +.. code:: python |
| 57 | +
|
| 58 | + import pyreason as pr |
| 59 | + pr.add_rule_from_csv('examples/rules.csv') |
| 60 | +
|
| 61 | +Load Rules from JSON |
| 62 | +----------------------- |
| 63 | +Rules can also be loaded from a JSON file. The JSON should be array of objects. |
| 64 | +Example: |
| 65 | + |
| 66 | +.. code:: text |
| 67 | +
|
| 68 | + [ |
| 69 | + { |
| 70 | + "rule_text": "under_department(X,Y) <-0 enroll(X,Z), in_department(Z,Y)", |
| 71 | + "name": "under_department_rule", |
| 72 | + "infer_edges": true, |
| 73 | + "set_static": false |
| 74 | + }, |
| 75 | + { |
| 76 | + "rule_text": "eligible(X) <-1 under_department(X,Y), scholarship(Y)", |
| 77 | + "name": "eligible_scholarship_rule", |
| 78 | + "infer_edges": false, |
| 79 | + "set_static": false |
| 80 | + } |
| 81 | + ] |
| 82 | +
|
| 83 | +Then load the file using: |
| 84 | + |
| 85 | +.. code:: python |
| 86 | +
|
| 87 | + pr.add_rule_from_json('examples/rules.json') |
| 88 | +
|
| 89 | +Loading Facts from CSV |
| 90 | +---------------------- |
| 91 | +Facts can be loaded from a CSV file. Each row should have up to 5 comma-separated values in this order: ``fact_text, name, start_time, end_time, static``. |
| 92 | + |
| 93 | +.. code:: text |
| 94 | +
|
| 95 | + fact_text,name,start_time,end_time,static |
| 96 | + scholarship(math_dept),scholarship_math_dept,0,2,False |
| 97 | + "in_department(math,math_dept)",math_in_math_department,0,2,False |
| 98 | + "in_department(cs,cs_dept)",cs_in_cs_department,0,2,False |
| 99 | + |
| 100 | +Note: when the fact text contains a comma, wrap the whole field in quotes. |
| 101 | + |
| 102 | +Then load the file using: |
| 103 | + |
| 104 | +.. code:: python |
| 105 | +
|
| 106 | + pr.add_fact_from_csv('examples/facts.csv') |
| 107 | +
|
| 108 | +Loading Facts from JSON |
| 109 | +----------------------- |
| 110 | +Facts can also be loaded from a JSON file. The JSON should be an array of objects. |
| 111 | +Example: |
| 112 | + |
| 113 | +.. code:: text |
| 114 | +
|
| 115 | + [ |
| 116 | + { |
| 117 | + "fact_text": "scholarship(math_dept)", |
| 118 | + "name": "scholarship_math_dept", |
| 119 | + "start_time": 0, |
| 120 | + "end_time": 2, |
| 121 | + "static": false |
| 122 | + }, |
| 123 | +
|
| 124 | + { |
| 125 | + "fact_text": "in_department(math,math_dept)", |
| 126 | + "name": "math_in_math_department", |
| 127 | + "start_time": 0, |
| 128 | + "end_time": 2, |
| 129 | + "static": false |
| 130 | + }, |
| 131 | +
|
| 132 | + { |
| 133 | + "fact_text": "in_department(cs,cs_dept)", |
| 134 | + "name": "cs_in_cs_department", |
| 135 | + "start_time": 0, |
| 136 | + "end_time": 2, |
| 137 | + "static": false |
| 138 | + } |
| 139 | + ] |
| 140 | +
|
| 141 | +Then load the file using: |
| 142 | + |
| 143 | +.. code:: python |
| 144 | +
|
| 145 | + pr.add_fact_from_json('examples/facts.json') |
| 146 | +
|
| 147 | +
|
| 148 | +Running PyReason |
| 149 | +---------------- |
| 150 | + |
| 151 | +After loading the graph, rules, and facts using any combination of the |
| 152 | +four loading functions above, run the reasoning: |
| 153 | + |
| 154 | +.. code:: python |
| 155 | +
|
| 156 | + interpretation = pr.reason(timesteps=2) |
| 157 | + dataframes = pr.filter_and_sort_nodes(interpretation, ['eligible']) |
| 158 | + for t, df in enumerate(dataframes): |
| 159 | + print(f'TIMESTEP - {t}') |
| 160 | + print(df) |
| 161 | + print() |
| 162 | +
|
| 163 | +Expected Output |
| 164 | +--------------- |
| 165 | +.. code:: |
| 166 | +
|
| 167 | +
|
| 168 | + TIMESTEP - 0 |
| 169 | + Empty DataFrame |
| 170 | + Columns: [component, eligible] |
| 171 | + Index: [] |
| 172 | +
|
| 173 | + TIMESTEP - 1 |
| 174 | + component eligible |
| 175 | + 0 alice [1.0, 1.0] |
| 176 | + 1 bob [1.0, 1.0] |
| 177 | +
|
| 178 | + TIMESTEP - 2 |
| 179 | + component eligible |
| 180 | + 0 alice [1.0, 1.0] |
| 181 | + 1 bob [1.0, 1.0] |
| 182 | +
|
| 183 | +At timestep 1, ``alice`` and ``bob`` become eligible because they are in |
| 184 | +``math_dept`` and ``math_dept`` has a scholarship. ``mary`` is not eligible |
| 185 | +because ``cs_dept`` has no scholarship fact. |
| 186 | + |
| 187 | + |
| 188 | +Further Details |
| 189 | +--------------- |
| 190 | + |
| 191 | +For a complete description of parameters and advanced features, see the full API reference in `pyreason.py |
| 192 | +<https://github.com/lab-v2/pyreason/blob/main/pyreason/pyreason.py#L868>`_. |
0 commit comments