Deriving Point Algebras

Deriving an interval-based algebra of time begins with point-based algebras of time.

In this section two point algebras are derived:

  1. A linear time point algebra that is used, in a later section, to derive Allen’s algebra of time

  2. A 2-dimensional point algebra, based on eight compass directions (e.g., east, northwest) plus equals, that can be used to derive an algebra of rectangles – a 2-dimensional version of Allen’s interval algebra

Transitivity Table for Linear Time Point Relations

The following code produces the transitivity table in Figure 5 of Reich’s 1994 paper, “Intervals, Points, and Branching Time”:

rels = ["<", "=", ">"]
def rel(x, y):
    """Return the relation between x and y."""
    if x < y:
        return "<"
    elif x > y:
        return ">"
    else:
        return "="

Initialize an empty table:

def create_initial_table(relations):
    table = dict()
    for rel1 in relations:
        table[rel1] = dict()
        for rel2 in relations:
            table[rel1][rel2] = set()
    return table
fig5 = create_initial_table(rels)

fig5
{'<': {'<': set(), '=': set(), '>': set()},
 '=': {'<': set(), '=': set(), '>': set()},
 '>': {'<': set(), '=': set(), '>': set()}}

Here, we use the integers on the number line as surrogates for time points, and then collect an exhaustive set of transitivity relations for three integers, a, b, & c.

First, we choose the number, n, of values that each of the three integers will range over. Actually, n=3 appears to work in this case, but to be sure, we’ll use a larger value.

n = 7

The 2-dimensional table, fig5 below, produced by this “brute force” process is identical to that found in Figure 5 of [Reich, 1994]:

for a in range(0, n):
    for b in range(0, n):
        for c in range(0, n):
            # Add transitive relation to table
            fig5[rel(a, b)][rel(b, c)].add(rel(a, c))

fig5
{'<': {'<': {'<'}, '=': {'<'}, '>': {'<', '=', '>'}},
 '=': {'<': {'<'}, '=': {'='}, '>': {'>'}},
 '>': {'<': {'<', '=', '>'}, '=': {'>'}, '>': {'>'}}}

Create Linear Point Algebra from Generated Table

The Algebra constructor in qualreas requires the table cell entries to be lists, not sets, so that conversion is made below:

from copy import deepcopy

def convert_sets_to_lists(table):
    """Returns a copy of the input table that has lists in places where there were sets."""
    tbl = deepcopy(table)
    for r1 in tbl:
        for r2 in tbl[r1]:
            tbl[r1][r2] = list(tbl[r1][r2])
    return tbl
fig5x = convert_sets_to_lists(fig5)
fig5x
{'<': {'<': ['<'], '=': ['<'], '>': ['=', '<', '>']},
 '=': {'<': ['<'], '=': ['='], '>': ['>']},
 '>': {'<': ['=', '<', '>'], '=': ['>'], '>': ['>']}}
ptalg_dict = {
    "Description": "Autogenerated Linear Point Algebra",
    "Name": "Linear_Point_Algebra",
    "Relations": {
        "<": {
            "Domain": ["Point"],
            "Converse": ">",
            "Name": "LessThan",
            "Range": ["Point"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "=": {
            "Domain": ["Point"],
            "Converse": "=",
            "Name": "Equals",
            "Range": ["Point"],
            "Reflexive": True,
            "Symmetric": True,
            "Transitive": True
        },
        ">": {
            "Domain": ["Point"],
            "Converse": "<",
            "Name": "GreaterThan",
            "Range": ["Point"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        }
    },
    "TransTable": fig5x
}
import qualreas as qr
ptalg = qr.Algebra(alg_dict=ptalg_dict)
ptalg
<qualreas.Algebra at 0x7fa2f0848410>
ptalg.summary()
  Algebra Name: Linear_Point_Algebra
   Description: Autogenerated Linear Point Algebra
 Equality Rels: =
     Relations:
            NAME (SYMBOL)         CONVERSE (ABBREV)  REFLEXIVE  SYMMETRIC TRANSITIVE   DOMAIN        RANGE
           LessThan (  <)         GreaterThan (  >)    False      False       True         Pt            Pt
             Equals (  =)              Equals (  =)     True       True       True         Pt            Pt
        GreaterThan (  >)            LessThan (  <)    False      False       True         Pt            Pt

Domain & Range Abbreviations:
   Pt = Point
 PInt = Proper Interval
ptalg.transitivity_table
{'<': {'<': relset(['<']), '=': relset(['<']), '>': relset(['<', '=', '>'])},
 '=': {'<': relset(['<']), '=': relset(['=']), '>': relset(['>'])},
 '>': {'<': relset(['<', '=', '>']), '=': relset(['>']), '>': relset(['>'])}}
ptalg.print_compact_transitivity_table()
"TransTable": {
    "<": {
        "<": "<",
        "=": "<",
        ">": "<|=|>"
    },
    "=": {
        "<": "<",
        "=": "=",
        ">": ">"
    },
    ">": {
        "<": "<|=|>",
        "=": ">",
        ">": ">"
    }
}

Transitivity Table for Branching Time Point Relations

TODO: Modify the function rel, defined above, so that it can handle left and right branching time.

The point algebras used to derive branching-time versions of Allen’s interval algebra are not derived here (at this time) but were instead derived “by hand”.

Transitivity Table for 2-Dimensional Point Relations

Now, we’ll apply the concept above to create a 2-dimensional point algebra by using an nxn array of integers as surrogates for 2-dimensional spatial coordinates, and 8 compass directions, along with equals.

def rel2d(coord1, coord2):
    x1, y1 = coord1
    x2, y2 = coord2
    if x1 == x2:  # Coords are vertically aligned
        if y1 == y2:
            return "EQ"  # coord1 equals coord2
        elif y1 < y2:
            return "S"  # coord1 south-of coord2
        else:
            return "N"  # coord1 north-of coord2
    elif x1 < x2:  # coord1 is to the left (west) of coord2
        if y1 == y2:
            return "W"  # coord1 west-of coord2
        elif y1 < y2:
            return "SW"  # coord1 southwest-of coord2
        else:
            return "NW"  # coord1 northwest-of coord2
    else:  # coord1 is to the right (east) of coord2
        if y1 == y2:
            return "E"  # coord1 east-of coord2
        elif y1 < y2:
            return "SE"  # coord1 southeast-of coord2
        else:
            return "NE"  # coord1 northeast-of coord2

Here are the 8 compass directions, along with equals:

rels2d = ['S', 'EQ', 'N', 'SW', 'W', 'NW', 'SE', 'E', 'NE']

First, initialize an empty 9x9 table:

tbl2d = create_initial_table(rels2d)

The following code populates the table. Note that there will be many duplicate compass directions generated by the combinations of 3 coordinates (x, y, & z), but since they are stored in sets, only one of each direction can be stored.

for x1 in range(0, n):
    for x2 in range(0, n):
        x = (x1, x2)
        for y1 in range(0, n):
            for y2 in range(0, n):
                y = (y1, y2)
                for z1 in range(0, n):
                    for z2 in range(0, n):
                        z = (z1, z2)
                        # Add transitive relation to table
                        tbl2d[rel2d(x, y)][rel2d(y, z)].add(rel2d(x, z))

Here’s the resulting transitivity table:

tbl2d
{'S': {'S': {'S'},
  'EQ': {'S'},
  'N': {'EQ', 'N', 'S'},
  'SW': {'SW'},
  'W': {'SW'},
  'NW': {'NW', 'SW', 'W'},
  'SE': {'SE'},
  'E': {'SE'},
  'NE': {'E', 'NE', 'SE'}},
 'EQ': {'S': {'S'},
  'EQ': {'EQ'},
  'N': {'N'},
  'SW': {'SW'},
  'W': {'W'},
  'NW': {'NW'},
  'SE': {'SE'},
  'E': {'E'},
  'NE': {'NE'}},
 'N': {'S': {'EQ', 'N', 'S'},
  'EQ': {'N'},
  'N': {'N'},
  'SW': {'NW', 'SW', 'W'},
  'W': {'NW'},
  'NW': {'NW'},
  'SE': {'E', 'NE', 'SE'},
  'E': {'NE'},
  'NE': {'NE'}},
 'SW': {'S': {'SW'},
  'EQ': {'SW'},
  'N': {'NW', 'SW', 'W'},
  'SW': {'SW'},
  'W': {'SW'},
  'NW': {'NW', 'SW', 'W'},
  'SE': {'S', 'SE', 'SW'},
  'E': {'S', 'SE', 'SW'},
  'NE': {'E', 'EQ', 'N', 'NE', 'NW', 'S', 'SE', 'SW', 'W'}},
 'W': {'S': {'SW'},
  'EQ': {'W'},
  'N': {'NW'},
  'SW': {'SW'},
  'W': {'W'},
  'NW': {'NW'},
  'SE': {'S', 'SE', 'SW'},
  'E': {'E', 'EQ', 'W'},
  'NE': {'N', 'NE', 'NW'}},
 'NW': {'S': {'NW', 'SW', 'W'},
  'EQ': {'NW'},
  'N': {'NW'},
  'SW': {'NW', 'SW', 'W'},
  'W': {'NW'},
  'NW': {'NW'},
  'SE': {'E', 'EQ', 'N', 'NE', 'NW', 'S', 'SE', 'SW', 'W'},
  'E': {'N', 'NE', 'NW'},
  'NE': {'N', 'NE', 'NW'}},
 'SE': {'S': {'SE'},
  'EQ': {'SE'},
  'N': {'E', 'NE', 'SE'},
  'SW': {'S', 'SE', 'SW'},
  'W': {'S', 'SE', 'SW'},
  'NW': {'E', 'EQ', 'N', 'NE', 'NW', 'S', 'SE', 'SW', 'W'},
  'SE': {'SE'},
  'E': {'SE'},
  'NE': {'E', 'NE', 'SE'}},
 'E': {'S': {'SE'},
  'EQ': {'E'},
  'N': {'NE'},
  'SW': {'S', 'SE', 'SW'},
  'W': {'E', 'EQ', 'W'},
  'NW': {'N', 'NE', 'NW'},
  'SE': {'SE'},
  'E': {'E'},
  'NE': {'NE'}},
 'NE': {'S': {'E', 'NE', 'SE'},
  'EQ': {'NE'},
  'N': {'NE'},
  'SW': {'E', 'EQ', 'N', 'NE', 'NW', 'S', 'SE', 'SW', 'W'},
  'W': {'N', 'NE', 'NW'},
  'NW': {'N', 'NE', 'NW'},
  'SE': {'E', 'NE', 'SE'},
  'E': {'NE'},
  'NE': {'NE'}}}
tbl2dx = convert_sets_to_lists(tbl2d)

Here’s the converted 2D point transitivity table:

tbl2dx
{'S': {'S': ['S'],
  'EQ': ['S'],
  'N': ['EQ', 'N', 'S'],
  'SW': ['SW'],
  'W': ['SW'],
  'NW': ['W', 'SW', 'NW'],
  'SE': ['SE'],
  'E': ['SE'],
  'NE': ['E', 'SE', 'NE']},
 'EQ': {'S': ['S'],
  'EQ': ['EQ'],
  'N': ['N'],
  'SW': ['SW'],
  'W': ['W'],
  'NW': ['NW'],
  'SE': ['SE'],
  'E': ['E'],
  'NE': ['NE']},
 'N': {'S': ['EQ', 'N', 'S'],
  'EQ': ['N'],
  'N': ['N'],
  'SW': ['W', 'SW', 'NW'],
  'W': ['NW'],
  'NW': ['NW'],
  'SE': ['E', 'SE', 'NE'],
  'E': ['NE'],
  'NE': ['NE']},
 'SW': {'S': ['SW'],
  'EQ': ['SW'],
  'N': ['W', 'SW', 'NW'],
  'SW': ['SW'],
  'W': ['SW'],
  'NW': ['W', 'SW', 'NW'],
  'SE': ['SW', 'S', 'SE'],
  'E': ['SW', 'S', 'SE'],
  'NE': ['EQ', 'N', 'NE', 'S', 'NW', 'W', 'E', 'SW', 'SE']},
 'W': {'S': ['SW'],
  'EQ': ['W'],
  'N': ['NW'],
  'SW': ['SW'],
  'W': ['W'],
  'NW': ['NW'],
  'SE': ['SW', 'S', 'SE'],
  'E': ['EQ', 'E', 'W'],
  'NE': ['NW', 'N', 'NE']},
 'NW': {'S': ['W', 'SW', 'NW'],
  'EQ': ['NW'],
  'N': ['NW'],
  'SW': ['W', 'SW', 'NW'],
  'W': ['NW'],
  'NW': ['NW'],
  'SE': ['EQ', 'N', 'NE', 'S', 'NW', 'W', 'E', 'SW', 'SE'],
  'E': ['NW', 'N', 'NE'],
  'NE': ['NW', 'N', 'NE']},
 'SE': {'S': ['SE'],
  'EQ': ['SE'],
  'N': ['E', 'SE', 'NE'],
  'SW': ['SW', 'S', 'SE'],
  'W': ['SW', 'S', 'SE'],
  'NW': ['EQ', 'N', 'NE', 'S', 'NW', 'W', 'E', 'SW', 'SE'],
  'SE': ['SE'],
  'E': ['SE'],
  'NE': ['E', 'SE', 'NE']},
 'E': {'S': ['SE'],
  'EQ': ['E'],
  'N': ['NE'],
  'SW': ['SW', 'S', 'SE'],
  'W': ['EQ', 'E', 'W'],
  'NW': ['NW', 'N', 'NE'],
  'SE': ['SE'],
  'E': ['E'],
  'NE': ['NE']},
 'NE': {'S': ['E', 'SE', 'NE'],
  'EQ': ['NE'],
  'N': ['NE'],
  'SW': ['EQ', 'N', 'NE', 'S', 'NW', 'W', 'E', 'SW', 'SE'],
  'W': ['NW', 'N', 'NE'],
  'NW': ['NW', 'N', 'NE'],
  'SE': ['E', 'SE', 'NE'],
  'E': ['NE'],
  'NE': ['NE']}}

Create Point Algebras from Generated Tables

Much more work is needed here. For now, I’m just forcing the tables into algebras to see what’s missing or could be done better.

qualreas requires that the domains and ranges of relations be specified:

qr.class_type_dict["2DPoint"] = qr.SpatialEntity

Abbreviated names of entity types/classes are used to print table summaries more compactly:

ENTITY_ABBREVIATIONS = {"Point": "Pt",
                        "ProperInterval": "PInt",
                        "Interval": "Int",
                        "Region": "Reg",
                        "2DPoint": "2DPt"}
def abbrev(term_list, abbrev_dict=ENTITY_ABBREVIATIONS):
    """Given a list of terms, return the corresponding list of abbreviated terms."""
    return '|'.join([abbrev_dict[term] for term in term_list])
qr.abbrev = abbrev

WARNING: The values, below, for Reflexive, Symmetric, & Transitive have all been temporarily set to False.

alg2d_dict = {
    "Description": "Autogenerated 2-dimensional point algebra",
    "Name": "2D_Point_Algebra",
    "Relations": {
        "S": {
            "Domain": ["2DPoint"],
            "Converse": "N",
            "Name": "South",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "EQ": {
            "Domain": ["2DPoint"],
            "Converse": "EQ",
            "Name": "Equals",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "N": {
            "Domain": ["2DPoint"],
            "Converse": "S",
            "Name": "North",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "SW": {
            "Domain": ["2DPoint"],
            "Converse": "NE",
            "Name": "Southwest",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "W": {
            "Domain": ["2DPoint"],
            "Converse": "E",
            "Name": "West",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "NW": {
            "Domain": ["2DPoint"],
            "Converse": "SE",
            "Name": "Northwest",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "SE": {
            "Domain": ["2DPoint"],
            "Converse": "NW",
            "Name": "Southeast",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "E": {
            "Domain": ["2DPoint"],
            "Converse": "W",
            "Name": "East",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        },
        "NE": {
            "Domain": ["2DPoint"],
            "Converse": "SW",
            "Name": "Northeast",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": False
        }
    },
    "TransTable": tbl2dx
}

Now, finally, we can create a 2D point algebra:

alg2d = qr.Algebra(alg_dict=alg2d_dict)
alg2d.summary()
  Algebra Name: 2D_Point_Algebra
   Description: Autogenerated 2-dimensional point algebra
 Equality Rels:
     Relations:
            NAME (SYMBOL)         CONVERSE (ABBREV)  REFLEXIVE  SYMMETRIC TRANSITIVE   DOMAIN        RANGE
              South (  S)               North (  N)    False      False      False       2DPt          2DPt
             Equals ( EQ)              Equals ( EQ)    False      False      False       2DPt          2DPt
              North (  N)               South (  S)    False      False      False       2DPt          2DPt
          Southwest ( SW)           Northeast ( NE)    False      False      False       2DPt          2DPt
               West (  W)                East (  E)    False      False      False       2DPt          2DPt
          Northwest ( NW)           Southeast ( SE)    False      False      False       2DPt          2DPt
          Southeast ( SE)           Northwest ( NW)    False      False      False       2DPt          2DPt
               East (  E)                West (  W)    False      False      False       2DPt          2DPt
          Northeast ( NE)           Southwest ( SW)    False      False      False       2DPt          2DPt

Domain & Range Abbreviations:
   Pt = Point
 PInt = Proper Interval
alg2d.print_compact_transitivity_table()
"TransTable": {
    "S": {
        "S": "S",
        "EQ": "S",
        "N": "S|EQ|N",
        "SW": "SW",
        "W": "SW",
        "NW": "SW|W|NW",
        "SE": "SE",
        "E": "SE",
        "NE": "SE|E|NE"
    },
    "EQ": {
        "S": "S",
        "EQ": "EQ",
        "N": "N",
        "SW": "SW",
        "W": "W",
        "NW": "NW",
        "SE": "SE",
        "E": "E",
        "NE": "NE"
    },
    "N": {
        "S": "S|EQ|N",
        "EQ": "N",
        "N": "N",
        "SW": "SW|W|NW",
        "W": "NW",
        "NW": "NW",
        "SE": "SE|E|NE",
        "E": "NE",
        "NE": "NE"
    },
    "SW": {
        "S": "SW",
        "EQ": "SW",
        "N": "SW|W|NW",
        "SW": "SW",
        "W": "SW",
        "NW": "SW|W|NW",
        "SE": "S|SW|SE",
        "E": "S|SW|SE",
        "NE": "S|EQ|N|SW|W|NW|SE|E|NE"
    },
    "W": {
        "S": "SW",
        "EQ": "W",
        "N": "NW",
        "SW": "SW",
        "W": "W",
        "NW": "NW",
        "SE": "S|SW|SE",
        "E": "EQ|W|E",
        "NE": "N|NW|NE"
    },
    "NW": {
        "S": "SW|W|NW",
        "EQ": "NW",
        "N": "NW",
        "SW": "SW|W|NW",
        "W": "NW",
        "NW": "NW",
        "SE": "S|EQ|N|SW|W|NW|SE|E|NE",
        "E": "N|NW|NE",
        "NE": "N|NW|NE"
    },
    "SE": {
        "S": "SE",
        "EQ": "SE",
        "N": "SE|E|NE",
        "SW": "S|SW|SE",
        "W": "S|SW|SE",
        "NW": "S|EQ|N|SW|W|NW|SE|E|NE",
        "SE": "SE",
        "E": "SE",
        "NE": "SE|E|NE"
    },
    "E": {
        "S": "SE",
        "EQ": "E",
        "N": "NE",
        "SW": "S|SW|SE",
        "W": "EQ|W|E",
        "NW": "N|NW|NE",
        "SE": "SE",
        "E": "E",
        "NE": "NE"
    },
    "NE": {
        "S": "SE|E|NE",
        "EQ": "NE",
        "N": "NE",
        "SW": "S|EQ|N|SW|W|NW|SE|E|NE",
        "W": "N|NW|NE",
        "NW": "N|NW|NE",
        "SE": "SE|E|NE",
        "E": "NE",
        "NE": "NE"
    }
}

Putting everything above together into a complete, compact definition of a 2D point algebra:

alg2d_dict_FULL = {
    "Description": "Autogenerated 2-dimensional point algebra",
    "Name": "2D_Point_Algebra",
    "Relations": {
        "S": {
            "Domain": ["2DPoint"],
            "Converse": "N",
            "Name": "South",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "EQ": {
            "Domain": ["2DPoint"],
            "Converse": "EQ",
            "Name": "Equals",
            "Range": ["2DPoint"],
            "Reflexive": True,
            "Symmetric": True,
            "Transitive": True
        },
        "N": {
            "Domain": ["2DPoint"],
            "Converse": "S",
            "Name": "North",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "SW": {
            "Domain": ["2DPoint"],
            "Converse": "NE",
            "Name": "Southwest",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "W": {
            "Domain": ["2DPoint"],
            "Converse": "E",
            "Name": "West",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "NW": {
            "Domain": ["2DPoint"],
            "Converse": "SE",
            "Name": "Northwest",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "SE": {
            "Domain": ["2DPoint"],
            "Converse": "NW",
            "Name": "Southeast",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "E": {
            "Domain": ["2DPoint"],
            "Converse": "W",
            "Name": "East",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        },
        "NE": {
            "Domain": ["2DPoint"],
            "Converse": "SW",
            "Name": "Northeast",
            "Range": ["2DPoint"],
            "Reflexive": False,
            "Symmetric": False,
            "Transitive": True
        }
    },
    "TransTable": {
        "S": {
            "S": "S",
            "EQ": "S",
            "N": "S|EQ|N",
            "SW": "SW",
            "W": "SW",
            "NW": "SW|W|NW",
            "SE": "SE",
            "E": "SE",
            "NE": "SE|E|NE"
        },
        "EQ": {
            "S": "S",
            "EQ": "EQ",
            "N": "N",
            "SW": "SW",
            "W": "W",
            "NW": "NW",
            "SE": "SE",
            "E": "E",
            "NE": "NE"
        },
        "N": {
            "S": "S|EQ|N",
            "EQ": "N",
            "N": "N",
            "SW": "SW|W|NW",
            "W": "NW",
            "NW": "NW",
            "SE": "SE|E|NE",
            "E": "NE",
            "NE": "NE"
        },
        "SW": {
            "S": "SW",
            "EQ": "SW",
            "N": "SW|W|NW",
            "SW": "SW",
            "W": "SW",
            "NW": "SW|W|NW",
            "SE": "S|SW|SE",
            "E": "S|SW|SE",
            "NE": "S|EQ|N|SW|W|NW|SE|E|NE"
        },
        "W": {
            "S": "SW",
            "EQ": "W",
            "N": "NW",
            "SW": "SW",
            "W": "W",
            "NW": "NW",
            "SE": "S|SW|SE",
            "E": "EQ|W|E",
            "NE": "N|NW|NE"
        },
        "NW": {
            "S": "SW|W|NW",
            "EQ": "NW",
            "N": "NW",
            "SW": "SW|W|NW",
            "W": "NW",
            "NW": "NW",
            "SE": "S|EQ|N|SW|W|NW|SE|E|NE",
            "E": "N|NW|NE",
            "NE": "N|NW|NE"
        },
        "SE": {
            "S": "SE",
            "EQ": "SE",
            "N": "SE|E|NE",
            "SW": "S|SW|SE",
            "W": "S|SW|SE",
            "NW": "S|EQ|N|SW|W|NW|SE|E|NE",
            "SE": "SE",
            "E": "SE",
            "NE": "SE|E|NE"
        },
        "E": {
            "S": "SE",
            "EQ": "E",
            "N": "NE",
            "SW": "S|SW|SE",
            "W": "EQ|W|E",
            "NW": "N|NW|NE",
            "SE": "SE",
            "E": "E",
            "NE": "NE"
        },
        "NE": {
            "S": "SE|E|NE",
            "EQ": "NE",
            "N": "NE",
            "SW": "S|EQ|N|SW|W|NW|SE|E|NE",
            "W": "N|NW|NE",
            "NW": "N|NW|NE",
            "SE": "SE|E|NE",
            "E": "NE",
            "NE": "NE"
        }
    }
}

Test the version of this saved in the Algebras directory (in JSON format)

The 2-dimensional point algebra, derived above, has been stored in the Algebras directory in JSON format.

In this section, the JSON file is used to instantiate the algebra.

A summary of the algebra is printed, and it is check for associativity and the composition identity.

import os

path = os.path.join(os.getenv('PYPROJ'), 'qualreas')

alg = qr.Algebra(os.path.join(path, "Algebras/2D_Point_Algebra.json"))
alg.summary()
  Algebra Name: 2D_Point_Algebra
   Description: Autogenerated 2-dimensional point algebra
 Equality Rels: EQ
     Relations:
            NAME (SYMBOL)         CONVERSE (ABBREV)  REFLEXIVE  SYMMETRIC TRANSITIVE   DOMAIN        RANGE
              South (  S)               North (  N)    False      False       True       2DPt          2DPt
             Equals ( EQ)              Equals ( EQ)     True       True       True       2DPt          2DPt
              North (  N)               South (  S)    False      False       True       2DPt          2DPt
          Southwest ( SW)           Northeast ( NE)    False      False       True       2DPt          2DPt
               West (  W)                East (  E)    False      False       True       2DPt          2DPt
          Northwest ( NW)           Southeast ( SE)    False      False       True       2DPt          2DPt
          Southeast ( SE)           Northwest ( NW)    False      False       True       2DPt          2DPt
               East (  E)                West (  W)    False      False       True       2DPt          2DPt
          Northeast ( NE)           Southwest ( SW)    False      False       True       2DPt          2DPt

Domain & Range Abbreviations:
   Pt = Point
 PInt = Proper Interval
alg.check_composition_identity()
True
alg.is_associative()
TEST SUMMARY: 729 OK, 0 Skipped, 0 Failed (729 Total)
True