PyReason not maintaining bound variables in rule body?
I'm modeling a simple purchasing scenario with two unconnected subgraphs of seller/buyer/house and seller2/buyer2/house2. I have a rule which defines how properties are transferred, and I set up some initial facts on seller and buyer. My main rule is:
property_transfer(x, y, p) <-1 owns(x,p), sold(x,y), paidMoney(y, x), noDeedCustom(p)"
I expect that once each variable in the predicate is bound, it would stay bound throughout the full evaluation of the rule's body (through all the clauses). I expect this to mean "x owns p AND x sold to y AND y paid money to x AND p has noDeedCustom".
However when I generate a rule trace, it seems like it's matching each clause independently, so owns(x,p) is matching both seller->buyer AND seller2->buyer, and then every subsequent clause is matching only the initial seller/buyer/house... yet somehow the property_transfer edge is getting applied to the seller2/buyer2/house2 nodes.
Am I misunderstanding something fundamental about how rules are evaluated, or is this a bug? Thank you.
Code and results below:
import pyreason as pr
import networkx as nx
pr.settings.verbose = True # Print info to screen
pr.settings.atom_trace = True # This allows us to view all the atoms that have made a certain rule fire
# Create a graph
g = nx.DiGraph()
# --- Scenario 1 ---
g.add_node('seller', person=1)
g.add_node('buyer', person=1)
g.add_node('house', property=1, noDeedCustom=1)
g.add_edge("seller", "house", owns=1)
g.add_edge("seller", "buyer", sellingTo=1)
g.add_edge("buyer", "seller", buyingFrom=1)
g.add_edge("buyer", "house", notOwns=1)
# --- Scenario 2 ---
g.add_node('seller2', person=1)
g.add_node('buyer2', person=1)
g.add_node('house2', property=1)
g.add_edge("seller2", "house2", owns=1)
g.add_edge("seller2", "buyer2", sellingTo=1)
g.add_edge("buyer2", "seller2", buyingFrom=1)
g.add_edge("buyer2", "house2", notOwns=1)
pr.add_rule(
pr.Rule("property_transfer(x, y, p) <-1 owns(x,p), sold(x,y), paidMoney(y, x), noDeedCustom(p)","basic_transfer")
)
pr.add_rule(
pr.Rule("owns(x, p) <-1 property_transfer(x, y, p), property(p)","basic_owns")
)
pr.add_fact(pr.Fact('sale_fact', ('seller', 'buyer'), 'sold', [1, 1], 0, 0,))
pr.add_fact(pr.Fact('money_fact', ('buyer', 'seller'), 'paidMoney', [1, 1], 0, 0,))
interpretation = pr.reason(timesteps=1)
Time ,Fixed-Point-Operation ,Edge ,Label ,Old Bound ,New Bound ,Occurred Due To ,Clause-1 ,Clause-2 ,Clause-3 ,Clause-4
0 , 0 ,"('seller', 'buyer')" ,sold ,"[0.0,1.0]" ,"[1.0,1.0]" ,sale_fact , , , ,
0 , 0 ,"('buyer', 'seller')" ,paidMoney ,"[0.0,1.0]" ,"[1.0,1.0]" ,money_fact , , , ,
1 , 1 ,"('seller', 'house')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('seller', 'buyer')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('buyer', 'seller')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('buyer', 'house')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('seller2', 'house2')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('seller2', 'buyer2')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('buyer2', 'seller2')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
1 , 1 ,"('buyer2', 'house2')" ,property_transfer ,"[0.0,1.0]" ,"[1.0,1.0]" ,basic_transfer ,"[('seller', 'house'), ('seller2', 'house2')]" ,"[('seller', 'buyer')]" ,"[('buyer', 'seller')]" ,['house']
Hello @Pugio,
PyReason only supports rules that have predicates over nodes or edges, and not over three elements. The head of your rule property_transfer(x, y, p) is therefore incorrect. Can you modify your scenario such that the head of the rule looks like head(x,y) ?
Thank you. I was unable to find a complete description of the rule syntax in the documentation. I will try to reformulate the problem and check that that works. It might be useful to throw an error in the case of incorrect predicate syntax.