LukasZahradnik / PyNeuraLogic

PyNeuraLogic lets you use Python to create Differentiable Logic Programs
https://pyneuralogic.readthedocs.io/
MIT License
279 stars 18 forks source link

[✨ Feature Request]: #38

Closed erhc closed 1 year ago

erhc commented 1 year ago

Is your feature request related to a problem? Please describe.

Currently, I am implementing Subgraph network rules for GNNs, and I need to operate on edges and create new graphs based on the connectivity of these edges, but I am not aware of any way to do it in this framework at this moment. I think it may be beneficial to add some feature which would allow creation of new nodes based on some given rules.

Describe the solution you'd like

I would need some way of creating new nodes and graphs based on the rules, for example some code similar to the following would be expected:

Relation.edge_node(V.E) <= Relation.edge(V.X, V.Y) # creates a node for each edge of the original graph
Relation.edge(V.X, V.Y, V.E) <= Relation.edge_node(V.E), Relation.edge(V.X, V.Y) # defines a relation that ties the edge node and its original nodes together

Relation.edge(V.E1, V.E2) <= (Relation.edge(V.X, V.Y, V.E1), Relation.edge(V.X, V.Z, V.E2))# creates edge between the nodes created above

Describe alternatives you've considered

No response

Additional context

No response

joaquincabezas commented 1 year ago

is the implementation based on any published paper?

LukasZahradnik commented 1 year ago

Hi, thank you for the feature request.

I'm working on implementing this feature, but it requires changes to the underlying grounder, and those can easily break something. So I'm not sure when or if this feature will be possible.

I think that in your scenario, you do not necessarily need this feature. You could do something like:

R.edge(V.A, V.B, V.C, V.D) <= (R.edge(V.A, V.B), R.edge(V.C, V.D), R.special._in(V.A, V.C, V.D)) 
R.edge(V.A, V.B, V.C, V.D) <= (R.edge(V.A, V.B), R.edge(V.C, V.D), R.special._in(V.B, V.C, V.D)) 

Here we treat edge/2 as "nodes" and edge/4 as "edges" between them. The in predicate is there to make it more compact. Otherwise, it would require four rules instead two.

GustikS commented 1 year ago

Or just

R.edge_sym(V.A, V.B) <= R.edge(V.B, V.A)
R.edge_sym(V.A, V.B) <= R.edge(V.A, V.B)

R.edge2(V.A, V.B, V.C) <= (R.edge_sym(V.A, V.B), R.edge_sym(V.B, V.C))

if you really need to consider symmetric edges.

To generalize to the higher orders, I'd say just remember the first and last node from the edge composition (path), and take it as the new hypernode/edge - this way you can compose the "higher-order" graphs:

R.edge2(V.A, V.C) <= (R.edge(V.A, V.B), R.edge(V.B, V.C))
R.edge4(V.A, V.C) <= (R.edge2(V.A, V.B), R.edge2(V.B, V.C))
...

and so ons.

erhc commented 1 year ago

Thank you for your replies! The implementation is based on the original paper on Subgraph Networks by Qi Xuan et al.: https://arxiv.org/abs/1903.09022

I would also like to ask what exactly does the R.special._in predicate do? There seems to be no explanation in the docs, so far: https://pyneuralogic.readthedocs.io/en/latest/advanced/modifiers.html#special-modifier

LukasZahradnik commented 1 year ago

The R.special._in(V.X, V.Y0, V.Y1, ... V.Yn) is satisfiable if X == Y1, X == Y2, or X == Yn.

In my example, it's checking that V.A is either equal to V.C (source node of the second edge) or V.D (target of the second edge).