Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New release v3.0.0 #43

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e000fe9
New release v3.0.0
dyumanaditya Mar 18, 2024
c6e27f6
modified the way node clauses with unseen variables are grounded
dyumanaditya Mar 18, 2024
16842a2
Merge branch 'main' into v3.0.0
dyumanaditya Mar 27, 2024
ab73263
Merge branch 'main' into v3.0.0
dyumanaditya Mar 28, 2024
61b1a84
Merge pull request #47 from lab-v2/main
dyumanaditya Apr 13, 2024
137a02c
added refinement for node/edge rule clauses
dyumanaditya Apr 13, 2024
ed630a8
fixed bug for interpretation dict returning empty when reason timeste…
dyumanaditya Apr 13, 2024
0f36198
parallel interpretation
dyumanaditya Apr 13, 2024
6d41219
bug
dyumanaditya Apr 13, 2024
defe3a9
Merge branch 'main' into v3.0.0
dyumanaditya May 27, 2024
7fdf73c
Update setup.py
dyumanaditya May 27, 2024
b01a803
new grounding methods, fixed annotations and speedups to be tested
dyumanaditya Jun 15, 2024
b0f201f
Merge remote-tracking branch 'origin/v3.0.0' into v3.0.0
dyumanaditya Jun 15, 2024
bd87afe
fixed test case for custom thresholds
dyumanaditya Jun 17, 2024
369f1fc
removed print statements from interpretation.py
dyumanaditya Jun 17, 2024
af73cae
removed print statements from interpretation.py
dyumanaditya Jun 17, 2024
8c750bf
Added unit test for 4 types of anyburl rules.
jaikrishnap98 Jun 18, 2024
2a23a8b
Added unit test for 4 types of anyburl rules: Updated graphml path
jaikrishnap98 Jun 18, 2024
d218a33
fixed parallel implementation,now works
dyumanaditya Jun 25, 2024
3dc5542
dummy push
dyumanaditya Jun 25, 2024
7c6ac8c
Fixed interpretation.py bug
dyumanaditya Jun 26, 2024
54e4446
added fix to parallel interpretation code
dyumanaditya Jun 26, 2024
70654d7
fixed bug with rules firing when they shouldn't. The issue was that w…
dyumanaditya Jul 19, 2024
a717cc8
updated parallel implementation with the bug fix
dyumanaditya Jul 19, 2024
47ad36e
Merge branch 'refs/heads/v3.0.0' into anyBurl-rules-4-types
dyumanaditya Jul 27, 2024
bc2a67c
Merged v3.0.0 into this tests branch
dyumanaditya Jul 27, 2024
bf13d1f
Merge pull request #56 from lab-v2/anyBurl-rules-4-types
dyumanaditya Jul 27, 2024
d5b9018
fixed test failures
dyumanaditya Jul 27, 2024
1eea18b
fixed test failures
dyumanaditya Jul 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Check out the [PyReason Hello World](https://pyreason.readthedocs.io/en/latest/t

## 1. Introduction
PyReason is a graphical inference tool that uses a set of logical rules and facts (initial conditions) to reason over graph structures. To get more details, refer to the paper/video/hello-world-example mentioned above.

## 2. Documentation
All API documentation and code examples can be found on [ReadTheDocs](https://pyreason.readthedocs.io/en/latest/)

Expand Down
6 changes: 3 additions & 3 deletions docs/group-chat-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Here is an example that utilizes custom thresholds.

The following graph represents a network of People and a Text Message in their group chat.
<img src="../media/group_chat_graph.png"/>
<img src="../media/group_chat_graph.png" width="400" height="400"/>

In this case, we want to know when a text message has been viewed by all members of the group chat.

Expand All @@ -14,7 +14,7 @@ First, lets create the group chat.
import networkx as nx

# Create an empty graph
G = nx.Graph()
G = nx.DiGraph()

# Add nodes
nodes = ["TextMessage", "Zach", "Justin", "Michelle", "Amy"]
Expand All @@ -35,7 +35,7 @@ G.add_edges_from(edges)
Considering that we only want a text message to be considered viewed by all if it has been viewed by everyone that can view it, we define the rule as follows:

```text
ViewedByAll(x) <- HaveAccess(x,y), Viewed(y)
ViewedByAll(y) <- HaveAccess(x,y), Viewed(x)
```

The `head` of the rule is `ViewedByAll(x)` and the body is `HaveAccess(x,y), Viewed(y)`. The head and body are separated by an arrow which means the rule will start evaluating from
Expand Down
Binary file modified media/group_chat_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions pyreason/pyreason.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,14 @@ def reset_rules():
__rules = None


def reset_settings():
"""
Resets settings to default
"""
global settings
settings = _Settings()


# FUNCTIONS
def load_graphml(path: str) -> None:
"""Loads graph from GraphMl file path into program
Expand Down
1,437 changes: 1,078 additions & 359 deletions pyreason/scripts/interpretation/interpretation.py

Large diffs are not rendered by default.

1,429 changes: 1,065 additions & 364 deletions pyreason/scripts/interpretation/interpretation_parallel.py

Large diffs are not rendered by default.

26 changes: 21 additions & 5 deletions pyreason/scripts/numba_wrapper/numba_types/rule_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def typeof_rule(val, c):
# Construct object from Numba functions (Doesn't work. We don't need this currently)
@type_callable(Rule)
def type_rule(context):
def typer(rule_name, type, target, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule):
if isinstance(rule_name, types.UnicodeType) and isinstance(type, types.UnicodeType) and isinstance(target, label.LabelType) and isinstance(delta, types.Integer) and isinstance(clauses, (types.NoneType, types.ListType)) and isinstance(bnd, interval.IntervalType) and isinstance(thresholds, types.ListType) and isinstance(ann_fn, types.UnicodeType) and isinstance(weights, types.Array) and isinstance(edges, types.Tuple) and isinstance(static, types.Boolean) and isinstance(immediate_rule, types.Boolean):
def typer(rule_name, type, target, head_variables, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule):
if isinstance(rule_name, types.UnicodeType) and isinstance(type, types.UnicodeType) and isinstance(target, label.LabelType) and isinstance(head_variables, types.ListType) and isinstance(delta, types.Integer) and isinstance(clauses, (types.NoneType, types.ListType)) and isinstance(bnd, interval.IntervalType) and isinstance(thresholds, types.ListType) and isinstance(ann_fn, types.UnicodeType) and isinstance(weights, types.Array) and isinstance(edges, types.Tuple) and isinstance(static, types.Boolean) and isinstance(immediate_rule, types.Boolean):
return rule_type
return typer

Expand All @@ -46,6 +46,7 @@ def __init__(self, dmm, fe_type):
('rule_name', types.string),
('type', types.string),
('target', label.label_type),
('head_variables', types.ListType(types.string)),
('delta', types.uint16),
('clauses', types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string)))),
('bnd', interval.interval_type),
Expand All @@ -63,6 +64,7 @@ def __init__(self, dmm, fe_type):
make_attribute_wrapper(RuleType, 'rule_name', 'rule_name')
make_attribute_wrapper(RuleType, 'type', 'type')
make_attribute_wrapper(RuleType, 'target', 'target')
make_attribute_wrapper(RuleType, 'head_variables', 'head_variables')
make_attribute_wrapper(RuleType, 'delta', 'delta')
make_attribute_wrapper(RuleType, 'clauses', 'clauses')
make_attribute_wrapper(RuleType, 'bnd', 'bnd')
Expand All @@ -75,16 +77,18 @@ def __init__(self, dmm, fe_type):


# Implement constructor
@lower_builtin(Rule, types.string, types.string, label.label_type, types.uint16, types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string))), interval.interval_type, types.ListType(types.ListType(types.Tuple((types.string, types.string, types.float64)))), types.string, types.float64[::1], types.Tuple((types.string, types.string, label.label_type)), types.boolean, types.boolean)
@lower_builtin(Rule, types.string, types.string, label.label_type, types.ListType(types.string), types.uint16, types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string))), interval.interval_type, types.ListType(types.ListType(types.Tuple((types.string, types.string, types.float64)))), types.string, types.float64[::1], types.Tuple((types.string, types.string, label.label_type)), types.boolean, types.boolean)
def impl_rule(context, builder, sig, args):
typ = sig.return_type
rule_name, type, target, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule = args
rule_name, type, target, head_variables, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule = args
context.nrt.incref(builder, types.ListType(types.string), head_variables)
context.nrt.incref(builder, types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string))), clauses)
context.nrt.incref(builder, types.ListType(types.Tuple((types.string, types.UniTuple(types.string, 2), types.float64))), thresholds)
rule = cgutils.create_struct_proxy(typ)(context, builder)
rule.rule_name = rule_name
rule.type = type
rule.target = target
rule.head_variables = head_variables
rule.delta = delta
rule.clauses = clauses
rule.bnd = bnd
Expand Down Expand Up @@ -119,6 +123,13 @@ def getter(rule):
return getter


@overload_method(RuleType, "get_head_variables")
def get_head_variables(rule):
def getter(rule):
return rule.head_variables
return getter


@overload_method(RuleType, "get_delta")
def get_delta(rule):
def getter(rule):
Expand Down Expand Up @@ -188,6 +199,7 @@ def unbox_rule(typ, obj, c):
name_obj = c.pyapi.object_getattr_string(obj, "_rule_name")
type_obj = c.pyapi.object_getattr_string(obj, "_type")
target_obj = c.pyapi.object_getattr_string(obj, "_target")
head_variables_obj = c.pyapi.object_getattr_string(obj, "_head_variables")
delta_obj = c.pyapi.object_getattr_string(obj, "_delta")
clauses_obj = c.pyapi.object_getattr_string(obj, "_clauses")
bnd_obj = c.pyapi.object_getattr_string(obj, "_bnd")
Expand All @@ -201,6 +213,7 @@ def unbox_rule(typ, obj, c):
rule.rule_name = c.unbox(types.string, name_obj).value
rule.type = c.unbox(types.string, type_obj).value
rule.target = c.unbox(label.label_type, target_obj).value
rule.head_variables = c.unbox(types.ListType(types.string), head_variables_obj).value
rule.delta = c.unbox(types.uint16, delta_obj).value
rule.clauses = c.unbox(types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string))), clauses_obj).value
rule.bnd = c.unbox(interval.interval_type, bnd_obj).value
Expand All @@ -213,6 +226,7 @@ def unbox_rule(typ, obj, c):
c.pyapi.decref(name_obj)
c.pyapi.decref(type_obj)
c.pyapi.decref(target_obj)
c.pyapi.decref(head_variables_obj)
c.pyapi.decref(delta_obj)
c.pyapi.decref(clauses_obj)
c.pyapi.decref(bnd_obj)
Expand All @@ -233,6 +247,7 @@ def box_rule(typ, val, c):
name_obj = c.box(types.string, rule.rule_name)
type_obj = c.box(types.string, rule.type)
target_obj = c.box(label.label_type, rule.target)
head_variables_obj = c.box(types.ListType(types.string), rule.head_variables)
delta_obj = c.box(types.uint16, rule.delta)
clauses_obj = c.box(types.ListType(types.Tuple((types.string, label.label_type, types.ListType(types.string), interval.interval_type, types.string))), rule.clauses)
bnd_obj = c.box(interval.interval_type, rule.bnd)
Expand All @@ -242,10 +257,11 @@ def box_rule(typ, val, c):
edges_obj = c.box(types.Tuple((types.string, types.string, label.label_type)), rule.edges)
static_obj = c.box(types.boolean, rule.static)
immediate_rule_obj = c.box(types.boolean, rule.immediate_rule)
res = c.pyapi.call_function_objargs(class_obj, (name_obj, type_obj, target_obj, delta_obj, clauses_obj, bnd_obj, thresholds_obj, ann_fn_obj, weights_obj, edges_obj, static_obj, immediate_rule_obj))
res = c.pyapi.call_function_objargs(class_obj, (name_obj, type_obj, target_obj, head_variables_obj, delta_obj, clauses_obj, bnd_obj, thresholds_obj, ann_fn_obj, weights_obj, edges_obj, static_obj, immediate_rule_obj))
c.pyapi.decref(name_obj)
c.pyapi.decref(type_obj)
c.pyapi.decref(target_obj)
c.pyapi.decref(head_variables_obj)
c.pyapi.decref(delta_obj)
c.pyapi.decref(clauses_obj)
c.pyapi.decref(ann_fn_obj)
Expand Down
6 changes: 5 additions & 1 deletion pyreason/scripts/rules/rule_internal.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
class Rule:

def __init__(self, rule_name, rule_type, target, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule):
def __init__(self, rule_name, rule_type, target, head_variables, delta, clauses, bnd, thresholds, ann_fn, weights, edges, static, immediate_rule):
self._rule_name = rule_name
self._type = rule_type
self._target = target
self._head_variables = head_variables
self._delta = delta
self._clauses = clauses
self._bnd = bnd
Expand All @@ -23,6 +24,9 @@ def get_rule_type(self):
def get_target(self):
return self._target

def get_head_variables(self):
return self._head_variables

def get_delta(self):
return self._delta

Expand Down
2 changes: 1 addition & 1 deletion pyreason/scripts/threshold/threshold.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ def to_tuple(self):
Returns:
tuple: A tuple representation of the Threshold instance.
"""
return (self.quantifier, self.quantifier_type, self.thresh)
return self.quantifier, self.quantifier_type, self.thresh
28 changes: 6 additions & 22 deletions pyreason/scripts/utils/rule_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,6 @@ def parse_rule(rule_text: str, name: str, custom_thresholds: list, infer_edges:
if rule_type == 'node':
infer_edges = False

# Replace the variables in the body with source/target if they match the variables in the head
# If infer_edges is true, then we consider all rules to be node rules, we infer the 2nd variable of the target predicate from the rule body
# Else we consider the rule to be an edge rule and replace variables with source/target
# Node rules with possibility of adding edges
if infer_edges or len(head_variables) == 1:
head_source_variable = head_variables[0]
for i in range(len(body_variables)):
for j in range(len(body_variables[i])):
if body_variables[i][j] == head_source_variable:
body_variables[i][j] = '__target'
# Edge rule, no edges to be added
elif len(head_variables) == 2:
for i in range(len(body_variables)):
for j in range(len(body_variables[i])):
if body_variables[i][j] == head_variables[0]:
body_variables[i][j] = '__source'
elif body_variables[i][j] == head_variables[1]:
body_variables[i][j] = '__target'

# Start setting up clauses
# clauses = [c1, c2, c3, c4]
# thresholds = [t1, t2, t3, t4]
Expand Down Expand Up @@ -184,15 +165,18 @@ def parse_rule(rule_text: str, name: str, custom_thresholds: list, infer_edges:
# Assert that there are two variables in the head of the rule if we infer edges
# Add edges between head variables if necessary
if infer_edges:
var = '__target' if head_variables[0] == head_variables[1] else head_variables[1]
edges = ('__target', var, target)
# var = '__target' if head_variables[0] == head_variables[1] else head_variables[1]
# edges = ('__target', var, target)
edges = (head_variables[0], head_variables[1], target)
else:
edges = ('', '', label.Label(''))

weights = np.ones(len(body_predicates), dtype=np.float64)
weights = np.append(weights, 0)

r = rule.Rule(name, rule_type, target, numba.types.uint16(t), clauses, target_bound, thresholds, ann_fn, weights, edges, set_static, immediate_rule)
head_variables = numba.typed.List(head_variables)

r = rule.Rule(name, rule_type, target, head_variables, numba.types.uint16(t), clauses, target_bound, thresholds, ann_fn, weights, edges, set_static, immediate_rule)
return r


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setup(
name='pyreason',
version='2.3.0',
version='3.0.0',
author='Dyuman Aditya',
author_email='[email protected]',
description='An explainable inference software supporting annotated, real valued, graph based and temporal logic',
Expand Down
19 changes: 8 additions & 11 deletions tests/group_chat_graph.graphml
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
<?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="HaveAccess" for="edge" attr.name="HaveAccess" attr.type="long" />
<graph edgedefault="undirected">
<key id="d0" for="edge" attr.name="HaveAccess" attr.type="long" />
<graph edgedefault="directed">
<node id="TextMessage" />
<node id="Zach" />
<node id="Justin" />
<node id="Michelle" />
<node id="Amy" />

<edge source="Zach" target="TextMessage">
<data key="HaveAccess">1</data>
<data key="d0">1</data>
</edge>
<edge source="Justin" target="TextMessage">
<data key="HaveAccess">1</data>
</edge>

<edge source="Amy" target="TextMessage">
<data key="HaveAccess">1</data>
<data key="d0">1</data>
</edge>
<edge source="Michelle" target="TextMessage">
<data key="HaveAccess">1</data>
<data key="d0">1</data>
</edge>
<edge source="Amy" target="TextMessage">
<data key="d0">1</data>
</edge>
</graph>
</graphml>
71 changes: 71 additions & 0 deletions tests/knowledge_graph_test_subset.graphml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?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="isConnectedTo" for="edge" attr.name="isConnectedTo" attr.type="long" />
<key id="Amsterdam_Airport_Schiphol" for="node" attr.name="Amsterdam_Airport_Schiphol" attr.type="long" />
<key id="Riga_International_Airport" for="node" attr.name="Riga_International_Airport" attr.type="long" />
<key id="Chișinău_International_Airport" for="node" attr.name="Chișinău_International_Airport" attr.type="long" />
<key id="Düsseldorf_Airport" for="node" attr.name="Düsseldorf_Airport" attr.type="long" />
<key id="Dubrovnik_Airport" for="node" attr.name="Dubrovnik_Airport" attr.type="long" />
<key id="Athens_International_Airport" for="node" attr.name="Athens_International_Airport" attr.type="long" />
<key id="Yali" for="node" attr.name="Yali" attr.type="long" />
<key id="Vnukovo_International_Airport" for="node" attr.name="Vnukovo_International_Airport" attr.type="long" />
<key id="Hévíz-Balaton_Airport" for="node" attr.name="Hévíz-Balaton_Airport" attr.type="long" />
<key id="Pobedilovo_Airport" for="node" attr.name="Pobedilovo_Airport" attr.type="long" />
<graph id="G" edgedefault="directed">
<node id="Amsterdam_Airport_Schiphol">
<data key="Amsterdam_Airport_Schiphol">1</data>
</node>
<node id="Riga_International_Airport">
<data key="Riga_International_Airport">1</data>
</node>
<node id="Chișinău_International_Airport">
<data key="Chișinău_International_Airport">1</data>
</node>
<node id="Yali">
<data key="Yali">1</data>
</node>
<node id="Düsseldorf_Airport">
<data key="Düsseldorf_Airport">1</data>
</node>
<node id="Pobedilovo_Airport">
<data key="Pobedilovo_Airport">1</data>
</node>
<node id="Dubrovnik_Airport">
<data key="Dubrovnik_Airport">1</data>
</node>
<node id="Hévíz-Balaton_Airport">
<data key="Hévíz-Balaton_Airport">1</data>
</node>
<node id="Athens_International_Airport">
<data key="Athens_International_Airport">1</data>
</node>
<node id="Vnukovo_International_Airport">
<data key="Vnukovo_International_Airport">1</data>
</node>
<edge source="Pobedilovo_Airport" target="Vnukovo_International_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Vnukovo_International_Airport" target="Hévíz-Balaton_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Düsseldorf_Airport" target="Dubrovnik_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Dubrovnik_Airport" target="Athens_International_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Riga_International_Airport" target="Amsterdam_Airport_Schiphol">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Riga_International_Airport" target="Düsseldorf_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Chișinău_International_Airport" target="Riga_International_Airport">
<data key="isConnectedTo">1</data>
</edge>
<edge source="Amsterdam_Airport_Schiphol" target="Yali">
<data key="isConnectedTo">1</data>
</edge>

</graph>
</graphml>
Loading
Loading