Predicates and functions in Python¶
Solvers can be extended with libraries of plain Python functions. Their arguments are converted from terms to Python values, and their results back:
predicatemakes a predicate out of a function returning a boolean (success or failure), or returning (or yielding) tuples of argument values, one per solution;functionmakes an arithmetic function, usable inis/2.
See also: Define predicates in Python.
In [1]:
Copied!
from tuprolog.solve.function import function
from tuprolog.solve.library import library
from tuprolog.solve.primitive import predicate
from tuprolog.solve.prolog import prolog_solver
def is_prime(n):
return n > 1 and all(n % d for d in range(2, int(n**0.5) + 1))
def divisors(n, _):
# the second argument is the output: one solution per divisor
for d in range(1, n + 1):
if n % d == 0:
yield n, d
lib = library(
"number.theory",
primitives={"prime/1": predicate(is_prime), "divisor/2": predicate(divisors)},
functions={"sqrt/1": function(lambda x: x**0.5)},
)
solver = prolog_solver(libraries=lib)
from tuprolog.solve.function import function
from tuprolog.solve.library import library
from tuprolog.solve.primitive import predicate
from tuprolog.solve.prolog import prolog_solver
def is_prime(n):
return n > 1 and all(n % d for d in range(2, int(n**0.5) + 1))
def divisors(n, _):
# the second argument is the output: one solution per divisor
for d in range(1, n + 1):
if n % d == 0:
yield n, d
lib = library(
"number.theory",
primitives={"prime/1": predicate(is_prime), "divisor/2": predicate(divisors)},
functions={"sqrt/1": function(lambda x: x**0.5)},
)
solver = prolog_solver(libraries=lib)
In [2]:
Copied!
list(solver.query("divisor(12, D)"))
list(solver.query("divisor(12, D)"))
Out[2]:
[{'D': 1}, {'D': 2}, {'D': 3}, {'D': 4}, {'D': 6}, {'D': 12}]
Python predicates mix freely with Prolog code and built-ins:
In [3]:
Copied!
list(solver.query("findall(P, (member(P, [2, 3, 4, 5, 6, 7, 8, 9]), prime(P)), Primes)"))
list(solver.query("findall(P, (member(P, [2, 3, 4, 5, 6, 7, 8, 9]), prime(P)), Primes)"))
Out[3]:
[{'Primes': [2, 3, 5, 7]}]
In [4]:
Copied!
list(solver.query("X is sqrt(16) + 1"))
list(solver.query("X is sqrt(16) + 1"))
Out[4]:
[{'X': 5.0}]
Errors¶
Python exceptions raised by these functions become logic errors (a system_error), which
Prolog code can handle with catch/3:
In [5]:
Copied!
from tuprolog.theory.parsing import parse_theory
risky = library("risky", functions={"inverse/1": function(lambda x: 1 / x)})
solver = prolog_solver(
libraries=risky,
static_kb=parse_theory("safe_inverse(X, Y) :- catch(Y is inverse(X), error(_, _), Y = none)."),
)
list(solver.query("safe_inverse(4, A), safe_inverse(0, B)"))
from tuprolog.theory.parsing import parse_theory
risky = library("risky", functions={"inverse/1": function(lambda x: 1 / x)})
solver = prolog_solver(
libraries=risky,
static_kb=parse_theory("safe_inverse(X, Y) :- catch(Y is inverse(X), error(_, _), Y = none)."),
)
list(solver.query("safe_inverse(4, A), safe_inverse(0, B)"))
Out[5]:
[{'A': 0.25, 'B': 'none'}]