Probabilistic Logic
In this example, we will explore a probabilistic extension of logic using the ProbLog framework.
Data model
Our data model will consist of:
- unary predicates that represent properties of a person
- binary predicates that represent relationships between persons
from dataclasses import dataclass
from typedlogic import FactMixin
PersonID = str
@dataclass
class Person(FactMixin):
"""
An instance of a person.
"""
id: PersonID
@dataclass
class Smokes(FactMixin):
"""
A person that smokes.
"""
id: PersonID
@dataclass
class Asthma(FactMixin):
"""
A person with asthma.
"""
id: PersonID
@dataclass
class Stress(FactMixin):
"""
A person with stress.
"""
id: PersonID
@dataclass
class Friend(FactMixin):
"""
A relationship between two people where both are friends.
"""
id: PersonID
other_id: PersonID
@dataclass
class Influences(FactMixin):
"""
A relationship between two people where one influences the other.
"""
id: PersonID
other_id: PersonID
We can create data just as for any other python data model:
Person("joris")
Friend("joris", "jonas")
We can build up a database of facts:
persons_data = ["angelika", "joris", "jonas", "dimitar"]
friends_data = [("joris", "jonas"), ("joris", "angelika"), ("joris", "dimitar"), ("angelika", "jonas")]
facts = []
for person in persons_data:
facts.append(Person(person))
for friend in friends_data:
facts.append(Friend(friend[0], friend[1]))
len(facts)
Logical Sentences
We will now augment our data model with logical sentences for simple rules. Note that these rules that have the same logical semantics as previous tutorials.
from typedlogic import axiom
@axiom
def smoking_from_stress(p: PersonID):
"""
If a person has stress, they smoke.
:param p: id of the person
"""
if Stress(p):
assert Smokes(p)
@axiom
def smoking_from_influencer(p: PersonID, other: PersonID):
"""
If a person is influenced by another person, and that person smokes, then the influenced person smokes.
:param p: id of the person that is entailed to smoke
:param other: id of the person that influences the person
"""
if Friend(p, other) and Influences(other, p) and Smokes(other):
assert Smokes(p)
So far, our model looks similar to all other models we have seen so far. We could reason over data using our model using any of the solvers we have seen so far, including simple rule-based solvers.
Next we will introduce probabilistic sentences to our model. We will do this using a special
probability
function.
Probabilistic Sentences
We will augment this data model with probabilistic sentences using probability/1
. With normal axiom-decorated
functions, we assert a crisp logical fact. We can also assert reified probability statements of the form
probability(<sentence>) == <probability>
.
from typedlogic.extensions.probabilistic import probability
@axiom
def priors_for_person(p: PersonID):
"""
Prior probabilities for a person to have stress, or
for a smoker to have asthma.
:param p: id of the person
"""
assert probability(Person(p) >> Stress(p)) == 0.3
assert probability(Smokes(p) >> Asthma(p)) == 0.4
@axiom
def priors_for_influences(p: PersonID, other: PersonID):
"""
Prior probability for one person to influence another.
:param p: id of the influencer
:param other: id of the influenced
"""
assert probability((Person(p) and Person(other)) >> Influences(p, other)) == 0.2
TODO investigate why >>
renders as >>
in the online docs.
The above program is combined into a single file in examples/influencers.py.
ProbLog Solver
We will use the ProbLog solver to reason over the probabilistic logic program we have defined.
from typedlogic.registry import get_solver
solver = get_solver("problog")
Next we will load examples/influencers.py.
import examples.influencers as influencers
solver.load(influencers)
for f in facts:
solver.add(f)
We can now query the solver to retrieve a model. This will be a subclass of the normal Model
class.
Note the concept of a Model in model-theoretic terms may not exactly apply here, but we keep the same concepts for consistency with other parts of this framework.
model = solver.model()
We can now retrieve the probabilities for a given predicate.
model.retrieve_probabilities('Asthma')
Adding Evidence
We can add evidence to the solver to update the probabilities. Note that adding evidence for one individual can propagate changes to other individuals. Here we will assert that Jonas has Asthma, this will increase the probability of his friends having asthma.
from typedlogic.extensions.probabilistic import Evidence, That
solver.add_evidence(Asthma("joris"), True)
model = solver.model()
model.retrieve_probabilities('Asthma')
How it works
The ProbLog solver works by converting the theorem into a ProbLog program, and then using a ProbLog.
We can examine the ProbLog program that is generated:
print(solver.dump())
This is the same as the ProbLog program in the ProbLog tutorial
(with the addition of the evidence
we added, and a collection of queries).
Future Work
In the future other frameworks that extend probabilistic horn rules, including Markov Logic Networks, will be supported.