Skip to content

Commit 06d67ec

Browse files
committed
docs for funcs in the argument
1 parent 74b1f6b commit 06d67ec

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

docs/source/user_guide/3_pyreason_rules.rst

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,79 @@ Then you can create rules of the following format:
189189
The annotation function will be called when all clauses in the rule have been satisfied and the head of the rule is to be annotated.
190190
The ``annotations`` parameter in the annotation function will contain the bounds of the grounded atoms for each of the 4 clauses in the rule.
191191

192+
Head Functions and Functional Arguments
193+
---------------------------------------
194+
195+
PyReason also supports applying user-defined functions directly within the **arguments of a rule head**. These head functions make it
196+
possible to transform the node or edge identifiers that will receive the rule's annotation. Common use cases include normalising IDs,
197+
mapping relationships, or selecting a subset of grounded nodes before the head is produced.
198+
199+
Registering a Head Function
200+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
201+
202+
Head functions, just like annotation functions, must be compiled with ``numba.njit`` and registered with PyReason before they can be
203+
used by rules. A head function receives a list of grounded variable bindings (each binding is a ``numba.typed.List`` of strings) and
204+
returns a new list containing the head substitutions that should be produced for that argument.
205+
206+
.. code-block:: python
207+
208+
import numba
209+
import pyreason as pr
210+
211+
@numba.njit
212+
def identity_func(groundings):
213+
"""
214+
Return the same grounded values that were passed in.
215+
`groundings` is a list where each element is the list of nodes bound to a variable.
216+
For example, if the rule body binds ``X`` to ``[a, b]`` and ``Y`` to ``[c, d]``,
217+
then ``groundings`` will be ``[[a, b], [c, d]]``.
218+
"""
219+
return numba.typed.List([groundings[0][0]])
220+
221+
pr.add_head_function(identity_func)
222+
223+
Using Functions in the Rule Head
224+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225+
226+
Once registered, a head function can be referenced in the textual rule by replacing a head argument with a function call. The parser
227+
automatically rewrites the argument to use an internal placeholder variable and associates the call with the registered function.
228+
229+
.. code-block:: text
230+
231+
Processed(identity_func(X)) <- property(X), property(Y), connected(X, Y)
232+
233+
During grounding, the rule body binds ``X`` and ``Y`` exactly as usual. Before emitting the head atom, PyReason invokes
234+
``identity_func`` with the grounded values for ``X`` (and any additional arguments if the function requires them). The function's return
235+
value determines which nodes (or edges) will receive the ``Processed`` label.
236+
237+
Edge Rules with Head Functions
238+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
239+
240+
Head functions may be used for either argument of an edge rule. Each head argument is handled independently, so you can transform the
241+
source, the target, or both.
242+
243+
.. code-block:: text
244+
245+
Route(identity_func(A), B) <- property(X), property(Y), connected(X, Y)
246+
Path(A, identity_func(B)) <- property(X), property(Y), connected(X, Y)
247+
Link(identity_func(A), identity_func(B)) <- property(X), property(Y), connected(X, Y)
248+
249+
In the example above, every head argument that uses ``identity_func`` will receive the transformed grounding returned by the function.
250+
If both arguments reference a function, each function call is resolved separately before the head edge is emitted.
251+
252+
Guidelines and Best Practices
253+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
254+
255+
* Head functions must be decorated with ``@numba.njit`` so that they can execute inside PyReason's JIT-compiled reasoning loop.
256+
* Each argument supplied to the function corresponds to a grounded variable from the rule body; that argument is represented as a
257+
``numba.typed.List`` of strings containing all candidate nodes for that variable.
258+
* The function must return a ``numba.typed.List`` of strings that represent the substituted values for the head argument.
259+
* Multiple head functions can be registered, and you can mix plain variables and function calls within the same rule head.
260+
* If you need the original grounding unchanged, simply return it from the function (as shown in ``identity_func``).
261+
262+
By leveraging head functions you can encapsulate common transformations and keep your rule text concise while still benefitting from
263+
PyReason's compiled execution path.
264+
192265

193266
Custom Thresholds
194267
-----------------

0 commit comments

Comments
 (0)