ProbLog allows calling functions written in Python from a ProbLog model.
The functionality is provided by the problog.extern
module.
This module introduces two decorators.
problog_export
: for deterministic functions (i.e. that return exactly one result)problog_export_nondet
: for non-deterministic functions (i.e. that return any number of results)problog_export_raw
: for functions without clear distinction between input and outputThese decorators take as arguments the types of the arguments. The possible argument types are
str
: a stringint
: an integer numberfloat
: a floating point numberlist
: a list of termsterm
: an arbitrary Prolog termEach argument is prepended with a +
or a -
to indicate whether it is an input or an output argument.
The arguments should be in order with input arguments first, followed by output arguments.
The function decorated with these decorators should have exactly the number of input arguments and it should return a tuple of length the number of output arguments. If there is only one output argument, it should not be wrapped in a tuple.
Functions decorated with problog_export_nondet
should return a list of result tuples.
Functions decorated with problog_export_raw
should return a list of tuples where each tuple
contains a value for each argument listed in the specification.
A function decorated with this decorator should have only +
specifiers and it should be prepared to receive
additional arguments containing the execution context (i.e. by adding **kwargs as last argument).
The internal Prolog database that is in use can be accessed through the variable problog_export.database
.
For example, consider the following Python module numbers.py
which defines two functions.
from problog.extern import problog_export, problog_export_nondet, problog_export_raw
@problog_export('+int', '+int', '-int')
def sum(a, b):
"""Computes the sum of two numbers."""
return a + b
@problog_export('+int', '+int', '-int', '-int')
def sum_and_product(a, b):
"""Computes the sum and product of two numbers."""
return a + b, a * b
@problog_export_nondet('+int', '+int', '-int')
def in_range(a, b):
"""Returns all numbers between a and b (not including b)."""
return list(range(a, b)) # list can be empty
@problog_export_nondet('+int')
def is_positive(a):
"""Checks whether the number is positive."""
if a > 0:
return [()] # one result (empty tuple)
else:
return [] # no results
@problog_export_raw('+term', '+term')
def successor(a, b, **kwargs):
"""Defines the successor relation between a and b."""
from problog.engine_builtin import check_mode
# We support three modes: a,b both integer; a integer and b variable; a variable and b integer.
# This will raise an error for any other case.
mode = check_mode([a, b], ['ii', 'iv', 'vi'], functor='successor', **kwargs)
if mode == 0:
# Both integers
av = int(a)
bv = int(b)
if av + 1 == bv:
return [(av, bv)]
else:
return []
elif mode == 1:
# Integer / Variable
av = int(a)
return [(av, Constant(av + 1))]
else:
# Variable / Integer
bv = int(b)
return [(Constant(bv - 1), bv)]
This module can be used in ProbLog by loading it using the use_module
directive.
:- use_module('numbers.py').
query(sum(2,4,X)).
query(sum_and_product(2,3,X,Y)).
query(in_range(1,4,X)).
query(is_positive(3)).
query(is_positive(-3)).
The result of this model is
in_range(1,4,1): 1
in_range(1,4,2): 1
in_range(1,4,3): 1
sum(2,4,6): 1
sum(2,3,5,6): 1
is_positive(-3): 0
is_positive(3): 1
It is possible to store persistent information in the internal database.
This database can be accessed as problog_export.database
.