Skip to main content

A library for manipulating and evaluating metric temporal (fuzzy) logic.

Project description

py-metric-temporal-fuzzy-logic

License: MIT DOI

WARNING: This is an experimental fork of the metric-temporal-logic library, available at mvcisback/py-metric-temporal-logic. THis fork adds support for different fuzzy connectives to the evaluation logic; the changes support different semantics for the core logical operations: norm (and), conorm (or), implication, and negation (not). Unless those changes are a strong requirement, we recommend the use of the original library.

Table of Contents

About

Python library for working with Metric Temporal Logic (MTL) with fuzzy logic. Metric Temporal Logic is an extension of Linear Temporal Logic (LTL) for specifying properties over time series (See Alur). Fuzzy Logic introduces fuzzy valuation of the properties, as an example in the interval [0;1] as opposed to crisp True/False values (See FT-LTL). The library does not introduce Fuzzy Time operators such as almost always. Some practical examples are given in the usage.

Installation

There is currently no release of the library. It needs to be built locally using the poetry python package/dependency management tool: $ poetry build

The package can be installed locally using your reference package manager.

Usage

To begin, we import mtfl.

import mtfl

There are two APIs for interacting with the mtfl module. Namely, one can specify the MTL expression using:

  1. Python Operators.
  2. Strings + The parse API.

We begin with the Python Operator API:

Python Operator API

Propositional logic (using python syntax)

a, b = mtfl.parse('a'), mtfl.parse('b')
phi0 = ~a
phi1 = a & b
phi2 = a | b
phi3 = a ^ b
phi4 = a.iff(b)
phi5 = a.implies(b)

Modal Logic (using python syntax)

a, b = mtfl.parse('a'), mtfl.parse('b')

# Eventually `a` will hold.
phi1 = a.eventually()

# `a & b` will always hold.
phi2 = (a & b).always()

# `a` until `b`
phi3 = a.until(b)

# `a` weak until `b`
phi4 = a.weak_until(b)

# Whenever `a` holds, then `b` holds in the next two time units.
phi5 = (a.implies(b.eventually(lo=0, hi=2))).always()

# We also support timed until.
phi6 = a.timed_until(b, lo=0, hi=2)

# `a` holds in two time steps.
phi7 = a >> 2

String based API

Propositional logic (parse api)

# - Lowercase strings denote atomic predicates.
phi0 = mtfl.parse('atomicpred')

# - infix operators need to be surrounded by parens.
phi1 = mtfl.parse('((a & b & c) | d | e)')
phi2 = mtfl.parse('(a -> b) & (~a -> c)')
phi3 = mtfl.parse('(a -> b -> c)')
phi4 = mtfl.parse('(a <-> b <-> c)')
phi5 = mtfl.parse('(x ^ y ^ z)')

# - Unary operators (negation)
phi6 = mtfl.parse('~a')
phi7 = mtfl.parse('~(a)')

Modal Logic (parser api)

# Eventually `x` will hold.
phi1 = mtfl.parse('F x')

# `x & y` will always hold.
phi2 = mtfl.parse('G(x & y)')

# `x` holds until `y` holds. 
# Note that since `U` is binary, it requires parens.
phi3 = mtfl.parse('(x U y)')

# Weak until (`y` never has to hold).
phi4 = mtfl.parse('(x W y)')

# Whenever `x` holds, then `y` holds in the next two time units.
phi5 = mtfl.parse('G(x -> F[0, 2] y)')

# We also support timed until.
phi6 = mtfl.parse('(a U[0, 2] b)')

# Finally, if time is discretized, we also support the next operator.
# Thus, LTL can also be modeled.
# `a` holds in two time steps.
phi7 = mtfl.parse('XX a')

Quantitative Evaluate (Signal Temporal Logic)

Given a property phi, one can evaluate is a timeseries satisifies phi. Time Series can either be defined using a dictionary mapping atomic predicate names to lists of (time, val) pairs or using the DiscreteSignals API (used internally).

There are two types of evaluation. One uses the boolean semantics of MTL and the other uses Signal Temporal Logic like semantics.

# Assumes piece wise constant interpolation.
data = {
    'a': [(0, 100), (1, -1), (3, -2)],
    'b': [(0, 20), (0.2, 2), (4, -10)]
}

phi = mtfl.parse('F(a | b)')
print(phi(data))
# output: 100

# Evaluate at t=3
print(phi(data, time=3))
# output: 2

# Evaluate with discrete time
phi = mtfl.parse('X b')
print(phi(data, dt=0.2))
# output: 2

Boolean Evaluation

To Boolean semantics can be thought of as a special case of the quantitative semantics where True ↦ 1 and False ↦ -1. This conversion happens automatically using the quantitative=False flag.

# Assumes piece wise constant interpolation.
data = {
    'a': [(0, True), (1, False), (3, False)],
    'b': [(0, False), (0.2, True), (4, False)]
}

phi = mtfl.parse('F(a | b)')
print(phi(data, quantitative=False))
# output: True

phi = mtfl.parse('F(a | b)')
print(phi(data))
# output: True

# Note, quantitative parameter defaults to False

# Evaluate at t=3. 
print(phi(data, time=3, quantitative=False))
# output: False

# Compute sliding satisifaction.
print(phi(data, time=None, quantitative=False))
# output: [(0, True), (0.2, True), (4, False)]

# Evaluate with discrete time
phi = mtfl.parse('X b')
print(phi(data, dt=0.2, quantitative=False))
# output: True

Fuzzy Evaluation

Fuzzy evaluation considers the signals as fuzzy values or values to be compared through fuzzy operators. The connectives used for evaluation, that is the implementation of the basic logic operations such as and or or, can be specified through the logic parameter. The connectives available in mtfl.connective are default, zadeh, godel, lukasiewicz, and product (See FT-LTL).

a = mtfl.parse("a")
b = mtfl.parse("b")

d = {
    "a": [(0,  5.), (1, 10.),           (3,  0.), (4, 10.), (5, 11.)],
    "b": [(0, 15.),           (2,  5.),           (4, 10.),        ],
}

# Crisp comparison between the value of a and a constant
i = a < 6
print(i, i(d, time=None, logic=mtfl.connective.zadeh, quantitative=True))
# output: (a < 6) [(0, 1.0), (1, 0.0), (3, 1.0), (4, 0.0), (5, 0.0)]

# Crisp comparison between the value of a and b
i = a < b
print(i, i(d, time=None, logic=mtfl.connective.zadeh, quantitative=True))
# output: (a < b) [(0, 1.0), (1, 1.0), (2, 0.0), (3, 1.0), (4, 0.0), (5, 0.0)]

# Fuzzy comparison between the value of a and b, increasing as a decreases in the interval (b;b+10]
i = a.lt(b, 10)
print(i, i(d, time=None, logic=mtfl.connective.zadeh, quantitative=True))
# output: (a <[~10] b) [(0, 1.0), (1, 1.0), (2, 0.5), (3, 1.0), (4, 1.0), (5, 0.9)]

# Temporal fuzzy operation
i = a.lt(b, 10).always()
print(i, i(d, logic=mtfl.connective.zadeh, quantitative=True))
# output: G(a <[~10] b) 0.5

Fuzzy caveats

Consider using quantitvative=True when specifying a fuzzy logic. Combining fuzzy and boolean evaluation might result in unexpected behaviours due to the underlying conversion of signal values to boolean ones.

Note that the library only evaluates signals and operations at pivot points, where their value changes. This might result in invalid values for some connectives (product). Consider the following example:

data = {
    'a': [(0, 0.5), (1, 0.1)],
}

phi = mtfl.parse('G(a)')
print(phi(data, quantitative=False))
# output: 0.05

Under the product connective (See FT-LTL), the norm operator is defined as the product of two values. phi thus evaluates to 0.05 = 0.5 * 0.1 across the whole signal when it should actually tend towards 0 due to the repeated valuation of a < 1 at all time instants.

Utilities

import mtfl
from mtfl import utils

print(utils.scope(mtfl.parse('XX a'), dt=0.1))
# output: 0.2

print(utils.discretize(mtfl.parse('F[0, 0.2] a'), dt=0.1))
# output: (a | X a | XX a)

Similar Projects

Feel free to open up a pull-request to add other similar projects. This library was written to meet some of my unique needs, for example I wanted the AST to be immutable and wanted the library to just handle manipulating MTL. Many other similar projects exist with different goals.

  1. https://github.com/doganulus/python-monitors
  2. https://github.com/STLInspector/STLInspector

Citing

@misc{pyMTL,
  author       = {Marcell Vazquez-Chanlatte},
  title        = {mvcisback/py-metric-temporal-logic: v0.1.1},
  month        = jan,
  year         = 2019,
  doi          = {10.5281/zenodo.2548862},
  url          = {https://doi.org/10.5281/zenodo.2548862}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metric-temporal-fuzzy-logic-0.0.6.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

File details

Details for the file metric-temporal-fuzzy-logic-0.0.6.tar.gz.

File metadata

File hashes

Hashes for metric-temporal-fuzzy-logic-0.0.6.tar.gz
Algorithm Hash digest
SHA256 57a605e6343fb161308621138bd12cf63dc3edfd9b1cd39efa95307b5cac064a
MD5 985f2a3a2fbc0ada2b8b6959721a1a6d
BLAKE2b-256 93083cd387fd95e6d9a6e838b6c36f082c72b4da25e57800475deec143f902e7

See more details on using hashes here.

File details

Details for the file metric_temporal_fuzzy_logic-0.0.6-py3-none-any.whl.

File metadata

File hashes

Hashes for metric_temporal_fuzzy_logic-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 5cbfd91763ccd142773c8910cbb8546de51c6355ef180a8a96b9587f33c0a2c1
MD5 693f441a4c2a369e5fc98bb5487a44e8
BLAKE2b-256 25a5e1de63389cf08ca04669b4234bcc2fde9d54cfc9152f5728e55771c82ade

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page