Defining Logic Formulas

This example demonstrates how to define logic formulas using iglsynth.logic module. Presently, IGLSynth defines three classes of logic formulas; namely atomic propositions AP, propositional logic PL and linear temporal logic LTL.

Atomic Propositions

The most common use of logic formulas in IGLSynth is to define certain properties of a game on graph. Hence, we define atomic propositions to be Callable objects over states (or vertices) of a graph.

An AP can be defined by providing a name:

a = AP(formula='a')

However, it is desirable to evaluate an AP at a given state. This can be achieved by instantiating an AP in the following way:

# Define atomic proposition with evaluation function
@ap
def is_goal(st):
    return st == 10

# Define atomic proposition with evaluation function accepting extra arguments
@ap
def is_colliding(st, *args):
    obs = args[0]
    return st in obs

# Define atomic proposition with evaluation function accepting extra keyword arguments
@ap
def is_close(st, **kwargs):
    threshold = kwargs['threshold']
    return st - threshold < 10

Let us understand the three AP definitions given above. The first AP, is_goal, accepts a state st as an input parameter and returns whether st == 10 or not. It is imperative that an AP must return a bool.

In many cases, it is convenient to define a parameterized atomic proposition. In such cases, the extra parameters can be passed to the AP’s evaluation function as optional arguments args or optional keyword arguments kwargs.

Note

It is strongly recommended that AP’s function template to have *args, **kwargs as parameters.

Given an AP, it is possible to evaluate it at a given state:

res = is_goal(10)
res = is_goal(20)

res = is_colliding(10, [5, 10])
res = is_colliding(20, [5, 10])

res = is_close(5, threshold=10)
res = is_close(50, threshold=10)

Propositional Logic Formulas

Propositional Logic Formulas can be defined by instantiating PL class. Similar to AP, a propositional logic formula can be defined by passing a formula string containing only And(&), Or(|) and/or Negation(!) operators:

plf0 = PL("a & b")
plf1 = PL("a | b")
plf2 = PL("!a | b")

A PL formula is, generally, defined over an alphabet. An alphabet is a set of atomic propositions. To define a PL formula over alphabet, first define an alphabet:

@ap
def a(st, *args, **kwargs):
    return st == 10

@ap
def b(st, *args, **kwargs):
    return st in args[0]

sigma = Alphabet([a, b])

An important feature of alphabet is that it can be evaluated at a given state. For example,:

result = sigma.evaluate(10, [10, 20])

returns a dictionary with keys as AP’s in alphabet and values as the result of evaluating the AP at given state. In above case, result = {a: True, b: True}.

Note

Observe that evaluating an alphabet at a state is equivalent to computing the label of that state.

Now, we can define a PL formula over an alphabet.

plf = PL("a & b", alphabet=sigma)

PL formulas can also be evaluated at a given state.

# PL is callable class
res = plf(10, [10, 20])

# Explicitly call evaluate function
res = plf.evaluate(10, [10, 20])

When a PL formula is evaluated at a given state, the alphabet is evaluated at that state. Then, the APs in PL formula are substituted with the evaluated values to get back True or False.

Warning

Calling PL object like res = plf(10, [10, 20]) is currently failing. See Bug Report Issue #17.


Temporal Logic Formulas

(Linear) Temporal Logic formulas can be defined by instantiating LTL class. Similar to PL, LTL formulas can be defined with/without providing an alphabet.

# Alphabet not provided.
ltlf0 = LTL(formula="Gp1 & !(p1 & X(p0 xor p1))")

# Alphabet provided
ltlf = LTL("F(a & Fb)", alphabet=sigma)