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.5.tar.gz (18.9 kB view details)

Uploaded Source

Built Distribution

File details

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

File metadata

File hashes

Hashes for metric-temporal-fuzzy-logic-0.0.5.tar.gz
Algorithm Hash digest
SHA256 b08e945653767ebdb7bf3f7ca8cc8f55330e15fa496e16a1f6450b0949cdd6fc
MD5 5d3516b8bbd87dc3740b9f4488abf7a3
BLAKE2b-256 eaded7e2e38a6b5f3772618361809610433879e9c0403b8de65ef1cc1d17bfd8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for metric_temporal_fuzzy_logic-0.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 1dfb701eb42182a23e0fd005ee67c6d709df8735eea67d9a081c445921b1ee33
MD5 23be2a0239f4667599f5a17421dcfb50
BLAKE2b-256 89e5be58becb0a40693917cfacee50e00b18fd860b0773eb721351184ef30d87

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