-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/v3.0.0' into v3.0.0
- Loading branch information
Showing
2 changed files
with
213 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,137 @@ | ||
PyReason Graphs | ||
============== | ||
**PyReason Graphs ** (Brief Intro) | ||
PyReason supports direct reasoning over knowledge graphs. PyReason graphs have full explainability of the reasoning process. (add more) | ||
=============== | ||
|
||
-Notes: go more indepth about use cases of Graphs, connection to Nuero symbolic reasoning, other pyreason logic concepts etc. | ||
PyReason supports direct reasoning over knowledge graphs. PyReason graphs have full explainability of the reasoning process. Graphs serve as the knowledge base for PyReason, allowing users to create visual representations based on rules, relationships, and connections. | ||
|
||
Methods for Loading Graphs | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
In PyReason there are two Methods for loading graphs: Networkx and GraphMl | ||
Methods for Creating Graphs | ||
--------------------------- | ||
In PyReason there are two ways to create graphs: Networkx and GraphMl | ||
Networkx allows you to manually add nodes and edges, whereas GraphMl reads in a directed graph from a file. | ||
|
||
|
||
Networkx Example | ||
^^^^^^^^^^^^^^^^ | ||
You can also build a graph using Networkx. | ||
---------------- | ||
Using Networkx, you can create a **`directed <https://en.wikipedia.org/wiki/Directed_graph>`_** graph object. Users can add and remove nodes and edges from the graph. | ||
|
||
Read more about Networkx `here <https://networkx.org/documentation/stable/reference/classes/digraph.html>`_. | ||
|
||
The following graph represents a network of people and the pets that | ||
they own. | ||
|
||
1. Mary is friends with Justin | ||
2. Mary is friends with John | ||
3. Justin is friends with John | ||
|
||
And | ||
|
||
1. Mary owns a cat | ||
2. Justin owns a cat and a dog | ||
3. John owns a dog | ||
|
||
.. code:: python | ||
import networkx as nx | ||
# ================================ CREATE GRAPH==================================== | ||
# Create a Directed graph | ||
g = nx.DiGraph() | ||
# Add the nodes | ||
g.add_nodes_from(['John', 'Mary', 'Justin']) | ||
g.add_nodes_from(['Dog', 'Cat']) | ||
# Add the edges and their attributes. When an attribute = x which is <= 1, the annotation | ||
# associated with it will be [x,1]. NOTE: These attributes are immutable | ||
# Friend edges | ||
g.add_edge('Justin', 'Mary', Friends=1) | ||
g.add_edge('John', 'Mary', Friends=1) | ||
g.add_edge('John', 'Justin', Friends=1) | ||
# Pet edges | ||
g.add_edge('Mary', 'Cat', owns=1) | ||
g.add_edge('Justin', 'Cat', owns=1) | ||
g.add_edge('Justin', 'Dog', owns=1) | ||
g.add_edge('John', 'Dog', owns=1) | ||
import networkx as nx | ||
# ================================ CREATE GRAPH==================================== | ||
# Create a Directed graph | ||
g = nx.DiGraph() | ||
# Add the nodes | ||
g.add_nodes_from(['John', 'Mary', 'Justin']) | ||
g.add_nodes_from(['Dog', 'Cat']) | ||
# Add the edges and their attributes. When an attribute = x which is <= 1, the annotation | ||
# associated with it will be [x,1]. NOTE: These attributes are immutable | ||
# Friend edges | ||
g.add_edge('Justin', 'Mary', Friends=1) | ||
g.add_edge('John', 'Mary', Friends=1) | ||
g.add_edge('John', 'Justin', Friends=1) | ||
# Pet edges | ||
g.add_edge('Mary', 'Cat', owns=1) | ||
g.add_edge('Justin', 'Cat', owns=1) | ||
g.add_edge('Justin', 'Dog', owns=1) | ||
g.add_edge('John', 'Dog', owns=1) | ||
After the graph has been created, it can be loaded with: | ||
|
||
.. code:: python | ||
import pyreason as pr | ||
pr.load_graph(graph: nx.DiGraph) | ||
Additional Considerations: | ||
-------------------------- | ||
Attributes to Bounds: | ||
|
||
In Networkx, each graph, node, and edge can hold key/value attribute pairs in an associated attribute dictionary (the keys must be hashable). | ||
|
||
In PyReason, these attributes get transformed into "bounds". The attribute value in Networkx, is translated into the lower bound in PyReason. | ||
|
||
.. code:: python | ||
import networkx as nx | ||
g = nx.DiGraph() | ||
g.add_node("some_node", attribute1=1, attribute2="0,0") | ||
When the graph is loaded, "some_node" is given the attribute1: [1,1], and attribute2 : [0,0]. | ||
|
||
If the attribute is a simple value, it is treated as both the lower and upper bound in PyReason. If a specific pair of bounds is required (e.g., for coordinates or ranges), the value should be provided as a string in a specific format. | ||
|
||
|
||
|
||
GraphMl Example | ||
^^^^^^^^^^^^^^^ | ||
Using GraphMl, you can also read in from a file. | ||
--------------- | ||
Using `GraphMl <https://en.wikipedia.org/wiki/GraphML>`_, you can read a graph in from a file. | ||
|
||
.. code:: xml | ||
<?xml version='1.0' encoding='utf-8'?> | ||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> | ||
<key id="owns" for="edge" attr.name="owns" attr.type="long" /> | ||
<key id="Friends" for="edge" attr.name="Friends" attr.type="long" /> | ||
<graph edgedefault="directed"> | ||
<node id="John" /> | ||
<node id="Mary" /> | ||
<node id="Justin" /> | ||
<node id="Dog" /> | ||
<node id="Cat" /> | ||
<edge source="John" target="Mary"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="John" target="Justin"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="John" target="Dog"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Mary" target="Cat"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Justin" target="Mary"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="Justin" target="Cat"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Justin" target="Dog"> | ||
<data key="owns">1</data> | ||
</edge> | ||
</graph> | ||
</graphml> | ||
<?xml version='1.0' encoding='utf-8'?> | ||
<graphml | ||
xmlns="http://graphml.graphdrawing.org/xmlns" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> | ||
<key id="owns" for="edge" attr.name="owns" attr.type="long" /> | ||
<key id="Friends" for="edge" attr.name="Friends" attr.type="long" /> | ||
<graph edgedefault="directed"> | ||
<node id="John" /> | ||
<node id="Mary" /> | ||
<node id="Justin" /> | ||
<node id="Dog" /> | ||
<node id="Cat" /> | ||
<edge source="John" target="Mary"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="John" target="Justin"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="John" target="Dog"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Mary" target="Cat"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Justin" target="Mary"> | ||
<data key="Friends">1</data> | ||
</edge> | ||
<edge source="Justin" target="Cat"> | ||
<data key="owns">1</data> | ||
</edge> | ||
<edge source="Justin" target="Dog"> | ||
<data key="owns">1</data> | ||
</edge> | ||
</graph> | ||
</graphml> | ||
Then load the graph using the following: | ||
|
||
.. code:: python | ||
import pyreason as pr | ||
pr.load_graphml('path_to_file') | ||
import pyreason as pr | ||
pr.load_graphml('path_to_file') | ||
Graph Output: | ||
|
||
.. code:: python | ||
.. figure:: docs/source/tutorials/basic_graph.png | ||
:alt: image | ||
|
||
?? Add image output of graph possibly? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
PyReason Rules | ||
============== | ||
- A rule is a statement that establishes a relationship between | ||
premises and a conclusion, allowing for the derivation of the | ||
conclusion if the premises are true. Rules are foundational to | ||
logical systems, facilitating the inference process. | ||
|
||
.. figure:: docs/source/tutorials/rule_image.png | ||
:alt: image | ||
|
||
- Every rule has a head and a body. The head determines what will | ||
change in the graph if the body is true. | ||
|
||
How to Create Rules | ||
------------------- | ||
|
||
In PyReason, rules are used to create relationships between different elements in the graph. These relationships can be used to infer new facts or make decisions based on existing graph data. | ||
|
||
|
||
### PyReason Rule Class | ||
|
||
To create a new **Rule** object in PyReason, use the `Rule` class with the following parameters: | ||
|
||
1. **rule_text**: The rule in textual format (the actual rule logic). | ||
2. **name**: A name for the rule. This name will appear in the rule trace. | ||
3. **infer_edges**: A boolean indicating whether new edges should be inferred when the rule is applied. | ||
4. **set_static**: A boolean indicating whether the atom in the head should be set as static after the rule is applied. This means the bounds of that atom will no longer change. | ||
5. **immediate_rule**: A boolean indicating whether the rule is immediate. Immediate rules check for more applicable rules immediately after being applied. | ||
6. **custom_thresholds**: A list or map of custom thresholds for the rule. If not specified, default thresholds for ANY are used. This can be a list of thresholds, or a map of clause index to threshold. | ||
|
||
|
||
|
||
|
||
Important Notes on Rule Formating: | ||
|
||
1. The head of the rule is always on the left hand side of the rule. | ||
2. The body of the rule is always on the right hand side of the rule. | ||
3. You can include timestep in the rule by using the `<-timestep` body. | ||
4. You can include multiple bodies in the rule by using the `<-timestep body1, body2, body3`. | ||
5. To compare two nodes, both the nodes should have an attribute in common. | ||
1. For example using the :ref:`pyreason_graphs.rst` example, in the rule below, both the people have an attribute 'Friends' in common which is the friends in the graph. | ||
2. So, we can compare the Friends status of both the customers to check if they are the Friends or not. | ||
|
||
.. code-block:: python | ||
pr.add_rule(pr.Rule('popular(x) <-1 popular(y), Friends(x,y)')) | ||
6. To compare a particular attribute of a node with another node, you need to use the attribute like attribute "owns" is used here. | ||
1. Note that nodes can be attributes themeselves, and thus refered to by node name | ||
.. code-block:: python | ||
pr.add_rule('popular(x) <-1 popular(y), Friends(x,y), owns(y,z), owns(x,z)', 'popular_rule') | ||
More Examples | ||
------------- | ||
|
||
Refering to our :ref:`pyreason_graphs.rst` example, we want to create a rule to determine popularity. The rule will state that if a person has a friend who is popular *and* has the same pet as they do, then they are popular. | ||
|
||
.. code:: text | ||
popular(x) : [1,1] <-1 popular(y) : [1,1] , Friends(x,y) : [1,1] , owns(y,z) : [1,1] , owns(x,z) : [1,1] | ||
The rule is read as follows: | ||
- **Head**: `popular(x) : [1,1]` | ||
- **Body**: `popular(y) : [1,1], Friends(x,y) : [1,1], owns(y,z) : [1,1], owns(x,z) : [1,1]` | ||
- The **head** and **body** are separated by an arrow (`<-1`), and the rule is applied after `1` timestep. | ||
|
||
|
||
### Adding the Rule to PyReason | ||
|
||
1. Add the rule directly | ||
|
||
To add the rule directly, we must specify the rule and a name for it. Here we will use "popular_rule". | ||
|
||
.. code:: python | ||
import pyreason as pr | ||
pr.add_rule(pr.Rule('popular(x) <-1 popular(y), Friends(x,y), owns(y,z), owns(x,z)', 'popular_rule')) | ||
The name helps understand which rules fired during reasoning later on. | ||
|
||
2. Add the rule from a .txt file | ||
|
||
To add the rule from a text file, ensure the file is in .txt format, and contains the rule in the format shown above. | ||
|
||
.. code:: text | ||
popular(x) <-1 popular(y), Friends(x,y), owns(y,z), owns(x,z) | ||
Now we can load the rule from the file using the following code: | ||
|
||
.. code:: python | ||
import pyreason as pr | ||
pr.add_rules_from_file('rules.txt') | ||