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:
A linear time point algebra that is used, in a later section, to derive Allen’s algebra of time
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