Languagegenerator documentation

Table of Contents

1 Introduction

The main purpose of the LanguageGenerator class is to generate all expressions up to a given length that are unique when evaluated on models up to a given size. This file serves the purpose of documenting both how LanguageGenerator achieves this conceptually, as well as its implementation.

The method that generates all the expressions up to a given length is generate_all_sentences, which takes just the max_expression_length as an argument: i.e. the length up to which expressions should be generated. There are however a number of other parameters that determine the output of this function. We define these parameters in a separate json file under experimental_setups/ and we tell the LanguageGenerator upon initialization which .json file to look at.

An example of such a file is

cat experiment_setups/Logical_index.json
{
    "name": "Logical_index",
    "number_of_subsets": 3,
    "operators": [
        ">f",
        ">",
        ">=",
        "=",
        "/",
        "subset",
        "intersection",
        "union",
        "card",
        "diff",
        "+",
        "-",
        "and",
        "or",
        "not",
        "index"

    ]
}

apart from the name of the experiment, there are two interesting parameters here:

operators
the names of the operators to be used in constructing the expressions. These names match with operators defined in src/operators.py
number_of_subsets
either \(3\) or \(4\). Defines a restriction on the models to be used. If \(3\), we only consider models where each element is either part of A, B or both (so only \(A\), \(B\), or \(A \cap B\)). If \(4\), also consider models where there can be elements that are not part of either (so \(A\), \(B\), or \(A \cap B\), and \(M \setminus (A \cup B)\))

So when initializing, we tell our LanguageGenerator which experiment setup to use, by simply providing the name of the .json file:

import sys
sys.path.insert(0,'..')
from src.languagegenerator import LanguageGenerator

lg = LanguageGenerator(
    max_model_size=3, experiment_setup="Logical_index",
)

Let us generate all expressions up to length 6:

lg.generate_all_sentences(6)

Now before going into how the LanguageGenerator generates unique expressions up to a given length, let us describe how we represent models, expressions, and meaning of expressions.

2 Representations

2.1 Expressions

We define expressions using tuples. See some examples:

("subset", "A", ("diff", "A", ("index", "3", "B"))),
("=", "3", ("card", ("union", "A", "B"))),
(">", "6", ("+", ("card", "A"), ("card", "B")))

("+", ("card", "A"), ("card", ("index", "5", "A"))),
("card", ("index", "5", ("union", "A", "B"))),
("+", "1", ("card", ("index", "5", "A")))

("/", ("card", "B"), ("-", ("card", "B"), "1"))
("/", ("+", "5", "6"), ("+", "2", "6"))
("/", ("card", ("index", "6", "B")), ("card", "A"))

("union", "B", ("index", "4", "A")),
("diff", ("index", "4", "A"), ("index", "6", "B")),
("index", "3", ("diff", "A", ("index", "1", "B")))
index 3 (diff A (index 1 B))

2.2 Models

We represent models of size \(k\) here by a \(2\times k\) matrix, where the ith column denotes the \(i^{th}\) object of the model. Each object then is represented by a 2-vector, where the first value is a 1 iff this object is in \(A\), and the second element denotes membership of \(B\). So for example

1 0 1 0
1 1 0 0

denotes a model where the first object is in \(A \cap B\), the second element is in \(B \setminus A\), the third element is in \(A \setminus B\), and the last element is in \(M \setminus (A\cup B)\).

We can equivalently represent models as a tuple of 2-tuples, where the ith 2-tuple contains the values in the \(i^{th}\) column of the matrix above. We call this the tuple format.

We define the universe up to n to be an ordered tuple/list of all models up to a size n, or simply the universe if \(n\) is clear from the context.

2.3 Representing meanings of expressions

We use a dynamic programming approach to compute the meaning of new expressions, where we combine the previously computed meanings of sub-expressions. To this end, the meaning of every expression is stored. We represent meanings of expressions in different ways, depending on the output type of the expressions. We distinguish between boolean expressions, int, float and set.

2.3.1 Boolean expressions representation

Boolean expressions are those that are either true or false on a given model. If the universe up to \(n\) is of size \(m\), then we represent a boolean expression \(e\) as a binary array of size \(m\), where the ith element is 1 iff the expression \(e\) is true on the ith model in the universe.

So an example of the meaning of a boolean expression:

lg.output_type2expression2meaning[bool][('not', ('subset', 'A', 'B'))]
True True False True True False True False True True True False True True False True False True True True False True False True True True False True True True False True True True True True True True False

2.3.2 Int and float expression representation

Expressions that evaluate to integers and floats are represented by an array the size of the universe, and the \(i^{th}\) element denotes the value of that expression for the \(i^{th}\) model:

Example of an integer meaning:

lg.output_type2expression2meaning[int][("+", ("card", "A"), ("card", "B"))]
1 1 2 2 2 3 2 3 2 3 3 4 3 3 4 3 4 3 4 4 5 3 4 3 4 4 5 3 4 4 5 3 4 4 5 4 5 5 6

2.3.3 Set representations

Representing the meaning of expressions that describe sets requires a slightly more complicated approach. Let us look at some examples:

for models_of_size in lg.output_type2expression2meaning[set]["A"]:
    print(models_of_size,'\n')
[[0 1 1]]

[[0 0 0 1 1 1 1 1 1]
 [0 1 1 0 0 1 1 1 1]]

[[0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1]]


What we see above is 3 matrices, one for each model size. We represent A here by setting the values to 1 that represent elements that belong to A. Let us look at another example of B:

for models_of_size in lg.output_type2expression2meaning[set]["B"]:
    print(models_of_size, "\n")
[[1 0 1]]

[[1 1 1 0 1 0 0 1 1]
 [1 0 1 1 1 0 1 0 1]]

[[1 1 1 1 1 1 1 1 1 0 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 1]
 [1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 1 1]
 [1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 1 1 1 1 0 1 0 1 0 1 0 1]]


Now let us look at what the meaning of the expression \(A \cap B\) looks like:

for models_of_size in lg.output_type2expression2meaning[set][
    ("intersection", "A", "B")
]:
    print(models_of_size, "\n")
[[0 0 1]]

[[0 0 0 0 1 0 0 1 1]
 [0 0 1 0 0 0 1 0 1]]

[[0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 1]
 [0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 0 1 1 0 0 1 1]
 [0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 1]]


We can see that representing the intersection of A and B corresponds to preforming the element-wise AND operation on the set representations of A and B. For the union, we have

for models_of_size in lg.output_type2expression2meaning[set][
    ("union", "A", "B")
]:
    print(models_of_size, "\n")
[[1 1 1]]

[[1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1]]

[[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]]


exactly what we expected: all ones. This is because we only consider models where every element is at part of at least one of A or B.

3 Getting started

As shown above, we can use the LanguageGenerator by pointing it to the experiment_setup json file to be used, and setting maximum model size:

import src
from src.languagegenerator import LanguageGenerator

lg = LanguageGenerator(max_model_size=3, experiment_setup="Logical_index", )
'org_babel_python_eoe'

4 How expressions are generated

We generate all expressions by starting with expressions of length 1, storing their meanings, and then defining expressions of greater length by combining previously formed sub-expressions. For each new expression, its meaning is computed and only if its meaning is not the same as any previously formed meaning, we store it as a new expression (which will be used in later expressions as subexpressions again). The method that implements this functionality is generate_all_sentences, which takes the length of the expressions to be generated.

By default, the LanguageGenerator will store itself, and the expressions generated for each length up to the given length in .dill format under the results/ folder. The name of the folder in the results folder to be used is uniquely identifying for the parameters that lead to generation of these sentences, e.g. results/Experiment=Logical_index-max_model_size=5/..

Now let's see what all expressions up to length 4 look like Let's see what all unique boolean expressions up to length 4 looks like:

lg.generate_all_sentences(4)
True False (subset A A) (subset A B) (subset B A) (subset B B) (> 1 (card A)) (> 1 (card B)) (> 2 (card A)) (> 2 (card B)) (> 3 (card A)) (> 3 (card B)) (> (card A) 1) (> (card A) 2) (> (card B) 1) (> (card B) 2) (= 1 (card A)) (= 1 (card B)) (= 2 (card A)) (= 2 (card B)) (not (subset A B)) (not (subset B A))

5 Scoring

The LanguageGenerator is also able to score the expressions it has generated based on some custom-defined signature function. See score.pdf for more information on the scoring itself.

We define a meaning matrix to be a pd.DataFrame that is indexed by the unique models in the universe, represented in tuple format, and where each column corresponds to an expression:

meaning_matrix = lg.get_meaning_matrix()
meaning_matrix
                          True  False  ...  (=, 1, (card, B))  (=, 1, (card, A))
((0, 1),)                    1      0  ...                  1                  0
((1, 0),)                    1      0  ...                  0                  1
((1, 1),)                    1      0  ...                  1                  1
((0, 1), (0, 1))             1      0  ...                  0                  0
((0, 1), (1, 0))             1      0  ...                  1                  1
((0, 1), (1, 1))             1      0  ...                  0                  1
((1, 0), (0, 1))             1      0  ...                  1                  1
((1, 1), (0, 1))             1      0  ...                  0                  1
((1, 0), (1, 0))             1      0  ...                  0                  0
((1, 0), (1, 1))             1      0  ...                  1                  0
((1, 1), (1, 0))             1      0  ...                  1                  0
((1, 1), (1, 1))             1      0  ...                  0                  0
((0, 1), (0, 1), (0, 1))     1      0  ...                  0                  0
((0, 1), (0, 1), (1, 0))     1      0  ...                  0                  1
((0, 1), (0, 1), (1, 1))     1      0  ...                  0                  1
((0, 1), (1, 0), (0, 1))     1      0  ...                  0                  1
((0, 1), (1, 1), (0, 1))     1      0  ...                  0                  1
((0, 1), (1, 0), (1, 0))     1      0  ...                  1                  0
((0, 1), (1, 0), (1, 1))     1      0  ...                  0                  0
((0, 1), (1, 1), (1, 0))     1      0  ...                  0                  0
((0, 1), (1, 1), (1, 1))     1      0  ...                  0                  0
((1, 0), (0, 1), (0, 1))     1      0  ...                  0                  1
((1, 1), (0, 1), (0, 1))     1      0  ...                  0                  1
((1, 0), (0, 1), (1, 0))     1      0  ...                  1                  0
((1, 0), (0, 1), (1, 1))     1      0  ...                  0                  0
((1, 1), (0, 1), (1, 0))     1      0  ...                  0                  0
((1, 1), (0, 1), (1, 1))     1      0  ...                  0                  0
((1, 0), (1, 0), (0, 1))     1      0  ...                  1                  0
((1, 0), (1, 1), (0, 1))     1      0  ...                  0                  0
((1, 1), (1, 0), (0, 1))     1      0  ...                  0                  0
((1, 1), (1, 1), (0, 1))     1      0  ...                  0                  0
((1, 0), (1, 0), (1, 0))     1      0  ...                  0                  0
((1, 0), (1, 0), (1, 1))     1      0  ...                  1                  0
((1, 0), (1, 1), (1, 0))     1      0  ...                  1                  0
((1, 0), (1, 1), (1, 1))     1      0  ...                  0                  0
((1, 1), (1, 0), (1, 0))     1      0  ...                  1                  0
((1, 1), (1, 0), (1, 1))     1      0  ...                  0                  0
((1, 1), (1, 1), (1, 0))     1      0  ...                  0                  0
((1, 1), (1, 1), (1, 1))     1      0  ...                  0                  0

[39 rows x 22 columns]

The cell in position \(i \times j\) is 1 iff the \(j^{th}\) expression is true on the \(i^{th}\) model.

5.1 Signature functions

We defined signature functions for graded measures of monotonicity, quantity, and conserativity in src/signature_functions.py. As discussed in score.pdf, signature functions induce partitions on the meaning matrix. Our implementation of signature functions only take the meaning matrix, and return the induced partition.

We distinguish between expression-invariant signature functions and expression-sensitive signature functions:

A signature function is expression-invariant if it purely a function of the models itself. A signature function is expression-sensitive, on the other hand, if it's value for a particular model is expression-specific.

For instance, the signature function for quantity is expression-invariant: it simply maps each model to a tuple with information on the cardinalities of the relevant subsets of that model (\(|A|\), \(|B|\), \(|A \cap B|\), \(M \setminus (A \cup B)\)). Therefore, the results of the quantity signature look like:

from src import signature_functions

signature_quantity_output = signature_functions.signature_quantity(
    meaning_matrix
)
signature_quantity_output
((0, 1),)                   (0, 0, 1, 0)
((1, 0),)                   (0, 1, 0, 0)
((1, 1),)                   (1, 0, 0, 0)
((0, 1), (0, 1))            (0, 0, 2, 0)
((0, 1), (1, 0))            (0, 1, 1, 0)
((0, 1), (1, 1))            (1, 0, 1, 0)
((1, 0), (0, 1))            (0, 1, 1, 0)
((1, 1), (0, 1))            (1, 0, 1, 0)
((1, 0), (1, 0))            (0, 2, 0, 0)
((1, 0), (1, 1))            (1, 1, 0, 0)
((1, 1), (1, 0))            (1, 1, 0, 0)
((1, 1), (1, 1))            (2, 0, 0, 0)
((0, 1), (0, 1), (0, 1))    (0, 0, 3, 0)
((0, 1), (0, 1), (1, 0))    (0, 1, 2, 0)
((0, 1), (0, 1), (1, 1))    (1, 0, 2, 0)
((0, 1), (1, 0), (0, 1))    (0, 1, 2, 0)
((0, 1), (1, 1), (0, 1))    (1, 0, 2, 0)
((0, 1), (1, 0), (1, 0))    (0, 2, 1, 0)
((0, 1), (1, 0), (1, 1))    (1, 1, 1, 0)
((0, 1), (1, 1), (1, 0))    (1, 1, 1, 0)
((0, 1), (1, 1), (1, 1))    (2, 0, 1, 0)
((1, 0), (0, 1), (0, 1))    (0, 1, 2, 0)
((1, 1), (0, 1), (0, 1))    (1, 0, 2, 0)
((1, 0), (0, 1), (1, 0))    (0, 2, 1, 0)
((1, 0), (0, 1), (1, 1))    (1, 1, 1, 0)
((1, 1), (0, 1), (1, 0))    (1, 1, 1, 0)
((1, 1), (0, 1), (1, 1))    (2, 0, 1, 0)
((1, 0), (1, 0), (0, 1))    (0, 2, 1, 0)
((1, 0), (1, 1), (0, 1))    (1, 1, 1, 0)
((1, 1), (1, 0), (0, 1))    (1, 1, 1, 0)
((1, 1), (1, 1), (0, 1))    (2, 0, 1, 0)
((1, 0), (1, 0), (1, 0))    (0, 3, 0, 0)
((1, 0), (1, 0), (1, 1))    (1, 2, 0, 0)
((1, 0), (1, 1), (1, 0))    (1, 2, 0, 0)
((1, 0), (1, 1), (1, 1))    (2, 1, 0, 0)
((1, 1), (1, 0), (1, 0))    (1, 2, 0, 0)
((1, 1), (1, 0), (1, 1))    (2, 1, 0, 0)
((1, 1), (1, 1), (1, 0))    (2, 1, 0, 0)
((1, 1), (1, 1), (1, 1))    (3, 0, 0, 0)
dtype: object

The signature function for monotonicity, however, is expression-sensitive, as it depends on the presence of a submodel that satisfies the expression. The output of this signature function is therefore of the same shape as the meaning matrix:

signature_monotonicity_output = signature_functions.signature_monotonicity(
    meaning_matrix
)
signature_monotonicity_output
                          True  False  ...  (=, 1, (card, B))  (=, 1, (card, A))
((0, 1),)                 True  False  ...               True              False
((1, 0),)                 True  False  ...               True               True
((1, 1),)                 True  False  ...               True               True
((0, 1), (0, 1))          True  False  ...               True              False
((0, 1), (1, 0))          True  False  ...               True               True
((0, 1), (1, 1))          True  False  ...               True               True
((1, 0), (0, 1))          True  False  ...               True               True
((1, 1), (0, 1))          True  False  ...               True               True
((1, 0), (1, 0))          True  False  ...               True              False
((1, 0), (1, 1))          True  False  ...               True              False
((1, 1), (1, 0))          True  False  ...               True              False
((1, 1), (1, 1))          True  False  ...               True              False
((0, 1), (0, 1), (0, 1))  True  False  ...               True              False
((0, 1), (0, 1), (1, 0))  True  False  ...               True               True
((0, 1), (0, 1), (1, 1))  True  False  ...               True               True
((0, 1), (1, 0), (0, 1))  True  False  ...               True               True
((0, 1), (1, 1), (0, 1))  True  False  ...               True               True
((0, 1), (1, 0), (1, 0))  True  False  ...               True              False
((0, 1), (1, 0), (1, 1))  True  False  ...               True              False
((0, 1), (1, 1), (1, 0))  True  False  ...               True              False
((0, 1), (1, 1), (1, 1))  True  False  ...               True              False
((1, 0), (0, 1), (0, 1))  True  False  ...               True               True
((1, 1), (0, 1), (0, 1))  True  False  ...               True               True
((1, 0), (0, 1), (1, 0))  True  False  ...               True              False
((1, 0), (0, 1), (1, 1))  True  False  ...               True              False
((1, 1), (0, 1), (1, 0))  True  False  ...               True              False
((1, 1), (0, 1), (1, 1))  True  False  ...               True              False
((1, 0), (1, 0), (0, 1))  True  False  ...               True              False
((1, 0), (1, 1), (0, 1))  True  False  ...               True              False
((1, 1), (1, 0), (0, 1))  True  False  ...               True              False
((1, 1), (1, 1), (0, 1))  True  False  ...               True              False
((1, 0), (1, 0), (1, 0))  True  False  ...              False              False
((1, 0), (1, 0), (1, 1))  True  False  ...               True              False
((1, 0), (1, 1), (1, 0))  True  False  ...               True              False
((1, 0), (1, 1), (1, 1))  True  False  ...               True              False
((1, 1), (1, 0), (1, 0))  True  False  ...               True              False
((1, 1), (1, 0), (1, 1))  True  False  ...               True              False
((1, 1), (1, 1), (1, 0))  True  False  ...               True              False
((1, 1), (1, 1), (1, 1))  True  False  ...               True              False

[39 rows x 22 columns]

A cell in position \(i \times j\) is True iff the \(i^{th}\) model has a submodel that satisfies the \(j^{th}\) expression.

5.2 Computing scores for the expressions

So while the signature function computes the induced partition, the LanguageGenerator.get_exp2score method implements the computation of the reduction of entropy that the provided signature function provides with respect to knowing if a model satisfies a given expression or not. Again, see score.pdf for more information on how this is computed.

The output of LanguageGenerator.get_exp2score is a pd.Series that is indexed by expressions, and maps each expression to its score:

exp2score_monotonicity = lg.get_exp2score(
    signature_functions.signature_monotonicity
)
exp2score_monotonicity
True                     1.000000
False                    1.000000
(subset, B, B)           0.259171
(subset, B, A)           0.260862
(subset, A, B)           1.000000
(subset, A, A)           1.000000
(not, (subset, B, A))    0.000000
(not, (subset, A, B))    0.000000
(>, (card, B), 2)        0.516886
(>, (card, B), 1)        0.362747
(>, (card, A), 2)        1.000000
(>, (card, A), 1)        1.000000
(>, 3, (card, B))        0.000000
(>, 3, (card, A))        1.000000
(>, 2, (card, B))        0.000000
(>, 2, (card, A))        1.000000
(>, 1, (card, B))        0.023665
(>, 1, (card, A))        1.000000
(=, 2, (card, B))        0.186723
(=, 2, (card, A))        1.000000
(=, 1, (card, B))        0.015518
(=, 1, (card, A))        1.000000
dtype: float64

And for conservativity:

exp2score_conservativity = lg.get_exp2score(
    signature_functions.signature_conservativity
)
exp2score_conservativity
True                     1.000000
False                    1.000000
(subset, B, B)           0.531720
(subset, B, A)           0.592618
(subset, A, B)           1.000000
(subset, A, A)           1.000000
(not, (subset, B, A))    0.592618
(not, (subset, A, B))    1.000000
(>, (card, B), 2)        0.579694
(>, (card, B), 1)        0.489300
(>, (card, A), 2)        1.000000
(>, (card, A), 1)        1.000000
(>, 3, (card, B))        0.579694
(>, 3, (card, A))        1.000000
(>, 2, (card, B))        0.489300
(>, 2, (card, A))        1.000000
(>, 1, (card, B))        0.531720
(>, 1, (card, A))        1.000000
(=, 2, (card, B))        0.369893
(=, 2, (card, A))        1.000000
(=, 1, (card, B))        0.369402
(=, 1, (card, A))        1.000000
dtype: float64

Considering none of these expressions contain the index operator, we already know that all the quantity scores are going to be 1:

exp2score_quantity = lg.get_exp2score(signature_functions.signature_quantity)
exp2score_quantity
True                     1.0
False                    1.0
(subset, B, B)           1.0
(subset, B, A)           1.0
(subset, A, B)           1.0
(subset, A, A)           1.0
(not, (subset, B, A))    1.0
(not, (subset, A, B))    1.0
(>, (card, B), 2)        1.0
(>, (card, B), 1)        1.0
(>, (card, A), 2)        1.0
(>, (card, A), 1)        1.0
(>, 3, (card, B))        1.0
(>, 3, (card, A))        1.0
(>, 2, (card, B))        1.0
(>, 2, (card, A))        1.0
(>, 1, (card, B))        1.0
(>, 1, (card, A))        1.0
(=, 2, (card, B))        1.0
(=, 2, (card, A))        1.0
(=, 1, (card, B))        1.0
(=, 1, (card, A))        1.0
dtype: float64

5.3 Visualizing results

In addition to computing the scores, the LanguageGenerator also implements visualizing the resulting scores as .png or as rotating .gif (in case of 3 scores).

Let's first compute expressions up to a greater length so that we can see some spread in the quantity scores as well.

import os

lg = LanguageGenerator(
    max_model_size=3, experiment_setup="Logical_index",
)
lg.generate_all_sentences(6)
['True',
 'False',
 ('subset', 'A', 'A'),
 ('subset', 'A', 'B'),
 ('subset', 'B', 'A'),
 ('subset', 'B', 'B'),
 ('>', '1', ('card', 'A')),
 ('>', '1', ('card', 'B')),
 ('>', '2', ('card', 'A')),
 ('>', '2', ('card', 'B')),
 ('>', '3', ('card', 'A')),
 ('>', '3', ('card', 'B')),
 ('>', ('card', 'A'), '1'),
 ('>', ('card', 'A'), '2'),
 ('>', ('card', 'B'), '1'),
 ('>', ('card', 'B'), '2'),
 ('=', '1', ('card', 'A')),
 ('=', '1', ('card', 'B')),
 ('=', '2', ('card', 'A')),
 ('=', '2', ('card', 'B')),
 ('not', ('subset', 'A', 'B')),
 ('not', ('subset', 'B', 'A')),
 ('>', ('card', 'A'), ('card', 'B')),
 ('>', ('card', 'B'), ('card', 'A')),
 ('>=', ('card', 'A'), ('card', 'B')),
 ('>=', ('card', 'B'), ('card', 'A')),
 ('=', ('card', 'A'), ('card', 'B')),
 ('subset', 'A', ('diff', 'A', 'B')),
 ('subset', 'A', ('index', '0', 'B')),
 ('subset', 'A', ('index', '1', 'B')),
 ('subset', 'A', ('index', '2', 'B')),
 ('subset', 'A', ('index', '3', 'B')),
 ('subset', 'B', ('diff', 'B', 'A')),
 ('subset', 'B', ('index', '0', 'A')),
 ('subset', 'B', ('index', '1', 'A')),
 ('subset', 'B', ('index', '2', 'A')),
 ('subset', 'B', ('index', '3', 'A')),
 ('subset', ('intersection', 'A', 'B'), 'A'),
 ('subset', ('union', 'A', 'B'), 'A'),
 ('subset', ('union', 'A', 'B'), 'B'),
 ('subset', ('diff', 'A', 'B'), 'A'),
 ('subset', ('diff', 'B', 'A'), 'B'),
 ('subset', ('index', '0', 'A'), 'B'),
 ('subset', ('index', '0', 'B'), 'A'),
 ('subset', ('index', '1', 'A'), 'B'),
 ('subset', ('index', '1', 'B'), 'A'),
 ('subset', ('index', '2', 'A'), 'B'),
 ('subset', ('index', '2', 'B'), 'A'),
 ('subset', ('index', '3', 'A'), 'B'),
 ('subset', ('index', '3', 'B'), 'A'),
 ('not', ('=', '1', ('card', 'A'))),
 ('not', ('=', '1', ('card', 'B'))),
 ('not', ('=', '2', ('card', 'A'))),
 ('not', ('=', '2', ('card', 'B'))),
 ('>', '1', ('card', ('intersection', 'A', 'B'))),
 ('>', '1', ('card', ('index', '0', 'A'))),
 ('>', '1', ('card', ('index', '0', 'B'))),
 ('>', '2', ('card', ('intersection', 'A', 'B'))),
 ('>', '2', ('card', ('union', 'A', 'B'))),
 ('>', '2', ('card', ('diff', 'A', 'B'))),
 ('>', '2', ('card', ('diff', 'B', 'A'))),
 ('>', '3', ('card', ('intersection', 'A', 'B'))),
 ('>', '3', ('card', ('union', 'A', 'B'))),
 ('>', '3', ('card', ('diff', 'A', 'B'))),
 ('>', '3', ('card', ('diff', 'B', 'A'))),
 ('>', ('card', ('intersection', 'A', 'B')), '1'),
 ('>', ('card', ('intersection', 'A', 'B')), '2'),
 ('>', ('card', ('union', 'A', 'B')), '1'),
 ('>', ('card', ('union', 'A', 'B')), '2'),
 ('>', ('card', ('diff', 'A', 'B')), '1'),
 ('>', ('card', ('diff', 'A', 'B')), '2'),
 ('>', ('card', ('diff', 'B', 'A')), '1'),
 ('>', ('card', ('diff', 'B', 'A')), '2'),
 ('=', '1', ('card', ('intersection', 'A', 'B'))),
 ('=', '1', ('card', ('diff', 'A', 'B'))),
 ('=', '1', ('card', ('diff', 'B', 'A'))),
 ('=', '2', ('card', ('intersection', 'A', 'B'))),
 ('=', '2', ('card', ('union', 'A', 'B'))),
 ('=', '2', ('card', ('diff', 'A', 'B'))),
 ('=', '2', ('card', ('diff', 'B', 'A'))),
 ('subset', 'A', ('index', ('card', 'B'), 'A')),
 ('subset', 'A', ('index', ('card', 'B'), 'B')),
 ('subset', 'B', ('index', ('card', 'A'), 'A')),
 ('subset', ('index', ('card', 'A'), 'A'), 'B'),
 ('subset', ('index', ('card', 'A'), 'B'), 'A'),
 ('subset', ('index', ('card', 'A'), 'B'), 'B'),
 ('subset', ('index', ('card', 'B'), 'A'), 'A'),
 ('subset', ('index', ('card', 'B'), 'A'), 'B'),
 ('subset', ('index', ('card', 'B'), 'B'), 'A'),
 ('not', ('=', ('card', 'A'), ('card', 'B'))),
 ('not', ('subset', 'A', ('diff', 'A', 'B'))),
 ('not', ('subset', 'A', ('index', '0', 'B'))),
 ('not', ('subset', 'A', ('index', '1', 'B'))),
 ('not', ('subset', 'A', ('index', '2', 'B'))),
 ('not', ('subset', 'A', ('index', '3', 'B'))),
 ('not', ('subset', 'B', ('diff', 'B', 'A'))),
 ('not', ('subset', 'B', ('index', '0', 'A'))),
 ('not', ('subset', 'B', ('index', '1', 'A'))),
 ('not', ('subset', 'B', ('index', '2', 'A'))),
 ('not', ('subset', 'B', ('index', '3', 'A'))),
 ('not', ('subset', ('index', '1', 'A'), 'B')),
 ('not', ('subset', ('index', '1', 'B'), 'A')),
 ('not', ('subset', ('index', '2', 'A'), 'B')),
 ('not', ('subset', ('index', '2', 'B'), 'A')),
 ('not', ('subset', ('index', '3', 'A'), 'B')),
 ('not', ('subset', ('index', '3', 'B'), 'A'))]

Now if we want to produce a 2d scatter plot showing the conservativity vs. monotonicity, we do:

lg.plot_png(
    [
        signature_functions.signature_conservativity,
        signature_functions.signature_monotonicity,
    ],
)

conservativity-vs-monotonicity.png

Of if we want to produce a 3d scatter plot:

import os

lg.plot_png(
    [
        signature_functions.signature_conservativity,
        signature_functions.signature_monotonicity,
        signature_functions.signature_quantity,
    ],
)

conservativity-vs-monotonicity-vs-quantity.png

In addition, it is possible to produce 3d scatter gifs:

lg.plot_gif(
    [
        signature_functions.signature_conservativity,
        signature_functions.signature_monotonicity,
        signature_functions.signature_quantity,
    ],
    angle_shift_per_frame=1,
    frame_rate=1 / 24,
)

conservativity-monotonicity-quantity-angle_shift=1-fr=0.042.gif

Date: April 14, 2020

Author: Paul Lodder

Created: 2020-04-16 Thu 18:37

Validate