Several updates

master
Oystein Kristoffer Tveit 2021-05-17 00:33:41 +02:00
parent f440e3cb4c
commit a2f9fdbe09
12 changed files with 307 additions and 15 deletions

View File

@ -2,7 +2,7 @@ from sys import argv
from pathlib import Path
from math import sin, cos, pi
# from run import printred
from common import printc
class Matrix:
""" Adjacency matrix which supports 0 and 1 """
@ -59,12 +59,12 @@ class Matrix:
class Graph:
def __init__(self, nodes, edges):
def __init__(self, nodes, edges): # Nodes = str, Edges = (str,str)
self.nodes = nodes
self.edges = edges
def __str__(self):
print(self.isUndirected())
printc('Is undirected: ' + str(self.isUndirected()))
return \
f'Nodes: {" ".join(self.nodes)}\n' \
+ f'Edges: {" ".join(x+y for x,y in (self.undirectedEdgeSet() if self.isUndirected() else self.edges))}'
@ -117,6 +117,34 @@ class Graph:
edges = sorted(list(set((x,y) if x < y else (y,x) for x,y in edges)))
return edges
# def latexifyEulerPath(self):
# degrees = [sum(line) for line in self.toMatrix()]
# def latexifyEulerCircuit():
# pass
def getKruskalsSpanningTree(self, weigths): # weights = dict[str, int]
edges = []
connected_subgraphs = [set(v) for v in self.nodes]
def find_subgraph_containing(v):
return [x for x in connected_subgraphs if v in x][0]
def merge_subgraphs_that_contains(u, v):
subgraph_v = find_subgraph_containing(v)
new_connected_subgraphs = [x for x in connected_subgraphs if v not in x]
new_connected_subgraphs = [subgraph_v.union(x) if u in x else x for x in new_connected_subgraphs]
return new_connected_subgraphs
sorted_edges = [ e for e in sorted(self.edges, key=lambda e: weigths[str(e)]) ]
for u,v in sorted_edges:
if find_subgraph_containing(u) != find_subgraph_containing(v):
edges += [(u, v)]
connected_subgraphs = merge_subgraphs_that_contains(u, v)
return Graph(self.nodes, edges)
def toLaTeX(self):
zippedNodes = zip(self.nodes, generateNodeCoords(len(self.nodes)))
nodeString = '\n'.join(f'\\node ({name}) at ({x},{y}) {{${name}$}};' for name,(x,y) in zippedNodes)

View File

@ -1,7 +1,7 @@
from sys import argv
from pathlib import Path
from run import printred
from common import printc
# Increase if the diagram becomes too clobbered
HEIGHT_SEPARATOR = 1
@ -57,10 +57,10 @@ def latex_hasse(hasse):
output.append(f'\\draw ({x}) -- ({y});')
printred(f"Minimal elements: $\{{ {', '.join(str(e) for e in min_el)} \}}$ \\\\")
printc(f"Minimal elements: $\{{ {', '.join(str(e) for e in min_el)} \}}$ \\\\")
max_el = set(v for k,v in hasse if v not in (x for x,_ in hasse))
printred(f"Maximal elements: $\{{ {', '.join(str(e) for e in max_el)} \}}$" )
printc(f"Maximal elements: $\{{ {', '.join(str(e) for e in max_el)} \}}$" )
return '\n'.join(output)

View File

@ -0,0 +1,51 @@
from itertools import combinations
class Set:
def __init__(self, elements):
self.elements = set(elements)
def __str__(self):
if len(self) == 0: return "\\emptyset"
return f"\\{{ {', '.join(sorted(self.elements))} \\}}"
def __iter__(self):
return list(sorted(self.elements))
def __len__(self):
return len(self.elements)
def __gt__(self, value):
if len(self) != len(value):
return len(self) > len(value)
return self.elements > value.elements
def __ge__(self, value):
return self > value
def __lt__(self, value):
return not self > value
def __le__(self, value):
return self < value
def cardinality(self):
return len(self)
def powerset(self):
powerset = []
for i in range(len(self) + 1):
for subset in combinations(self.elements, i):
powerset.append(Set(list(subset)))
return Set(powerset)
def to_vertical_latex(self):
column = []
for e in sorted(self.elements):
column.append(str(e) + ' \\\\')
return '\\{\n' + '\n'.join(column) + '\n\\}'
#TODO: make process input func
if __name__ == "__main__":
print(Set(['a', 'b', 'c']).powerset().to_vertical_latex())
# print(a for a in Set(['A', 'B', 'C']).powerset())

View File

View File

@ -0,0 +1,182 @@
from sys import argv
from pathlib import Path
from common import printc, printerr
from Graph import Graph
# Increase if hasse diagram becomes too clobbered
HEIGHT_SEPARATOR = 1
def pairToString(pair):
if str(pair[0]).isdigit() or str(pair[1]).isdigit():
return f'({pair[0]},{pair[1]})'
else:
return pair[0] + pair[1]
def stringToPair(string):
if string[0] == '(':
return tuple(string.split(',')[0][1:], string.split(',')[0][:-1])
else:
return tuple(list(string))
class Relation:
def __init__(self, pairs): # pair = (str,str)
self.pairs = set(pairs)
def __str__(self):
return f'\\{{ {", ".join(x + y for x,y in self.pairs)} \}}'
@classmethod
def fromString(cls, string):
relations = (stringToPair(x) for x in relations.split(' '))
return cls(relations)
@classmethod
def fromDivisibilityPosetTo(cls, n):
pairs = set()
for dst in range(n):
pairs.add((dst, dst))
for src in range(1, dst):
if dst % src == 0:
pairs.add((src, dst))
return cls(pairs)
def isReflexive(self):
elements = set(list(sum(self.pairs, ())))
result = all((x,x) in self.pairs for x in elements)
if not result:
printc('Not reflexive, missing following pairs:', color='green')
missingPairs = [(x,x) for x in elements if (x,x) not in self.pairs]
print(f'\\[ {", ".join(pairToString(p) for p in missingPairs)} \\]')
return result
def isSymmetric(self):
result = all((y,x) in self.pairs for x,y in self.pairs)
if not result:
printc('Not symmetric, missing following pairs:', color='green')
missingPairs = [(x,y) for x,y in self.pairs if (y,x) not in self.pairs]
print(f'\\[ {", ".join(pairToString(p) for p in missingPairs)} \\]')
return result
def isAntiSymmetric(self):
result = not any((y,x) in self.pairs and y != x for x,y in self.pairs)
if not result:
printc('Not antisymmetric, following pairs are symmetric:', color='green')
symmetricPairs = [((x,y), (y,x)) for x,y in self.pairs if (y,x) in self.pairs and y > x]
print(f'\\[ {", ".join(f"{pairToString(p)} and {pairToString(q)}" for p,q in symmetricPairs)} \\]')
return result
def isTransitive(self):
result = True
nonTransitivePairs = []
for x,y in self.pairs:
for z,w in self.pairs:
if not (y != z or ((x,w) in self.pairs)):
nonTransitivePairs.append(((x,y), (z,w)))
result = False
if not result:
printc('Not transitive, following pairs are missing its transitive counterpart:', color='green')
print(f'\\[ {", ".join(f"{pairToString(p)} and {pairToString(q)} without {pairToString((p[0], q[1]))}" for p,q in nonTransitivePairs)} \\]')
return result
def isEquivalenceRelation(self):
return self.isReflexive() and self.isSymmetric() and self.isTransitive()
def isPartialOrder(self):
return self.isReflexive() and self.isAntiSymmetric() and self.isTransitive()
def getHassePairs(self):
if not self.isPartialOrder():
printerr('This is not a partial order')
return
hassePairs = set()
for x1, y1 in self.pairs:
for x2, y2 in self.pairs:
if y1 == x2:
hassePairs.add((x1, y2))
return self.pairs - hassePairs
def latexifyHasseDiagram(self):
hasse = self.getHassePairs()
min_el = set(a for a,b in hasse if a not in list(zip(*hasse))[1])
keys = set(item for tup in hasse for item in tup)
y_pos = dict.fromkeys(keys, 0)
i = 0
while len(next_row := [val for key,val in hasse if key in [x for x,y in y_pos.items() if y == i] ]) != 0:
for item in next_row:
y_pos[item] = i + 1
i += 1
inv_ypos = dict()
for key in set(y_pos.values()):
inv_ypos[key] = [x for x,y in y_pos.items() if y == key]
output = []
for y in inv_ypos.keys():
for i, n in enumerate(inv_ypos[y]):
output.append(f'\\node ({n}) at ({i - len(inv_ypos[y])/2}, {y * HEIGHT_SEPARATOR}) {{${n}$}};')
output.append('')
for x,y in hasse:
output.append(f'\\draw ({x}) -- ({y});')
printc(f"Minimal elements: $\{{ {', '.join(str(e) for e in min_el)} \}}$ \\\\")
max_el = set(v for k,v in hasse if v not in (x for x,_ in hasse))
printc(f"Maximal elements: $\{{ {', '.join(str(e) for e in max_el)} \}}$" )
return '\n'.join(output)
def latexifyGraph(self):
if self.isEquivalenceRelation():
graphType = 'undirected'
pairs = [(x,y) for x,y in self.pairs if x != y]
else:
graphType = 'directed'
pairs = self.pairs
pass
def processFileContent(raw, template):
outputType, inp = raw.split('\n\n')
if inp.startsWith('divisibilityPosetTo'):
n = int(inp.split(' ')[1])
relation = Relation.fromDivisibilityPosetTo(n)
else:
relation = Relation.fromString(inp)
if outputType == 'proveEquivalence':
content = relation.isEquivalenceRelation()
elif outputType == 'provePoset':
content = relation.isPartialOrder()
elif outputType == 'hasseDiagram':
content = relation.latexifyHasseDiagram()
elif outputType == 'graph':
content = relation.latexifyGraph()
content = Relation.fromString(raw).latexifyHasseDiagram()
return template.replace("%CONTENT", content)
if __name__ == '__main__':
inp = 'AA BB CC DD AB AC AD BC BD CD'
relations = [stringToPair(p) for p in inp.split(' ')]
# print(Relation(relations).isEquivalenceRelation())
print(Relation(relations).isPartialOrder())
print(Relation.fromDivisibilityPosetTo(30).isPartialOrder())

View File

@ -1,9 +1,12 @@
from sys import argv
from pathlib import Path
from common import printerr
try:
from tabulate import tabulate
except:
print('Couldn\'t find tabulate. Do you have it installed?')
printerr('Couldn\'t find tabulate. Do you have it installed?')
def parseExpressionList(inputData):
@ -111,11 +114,8 @@ if __name__ == '__main__':
exps = parseExpressionList(file.read())
truthtable = generateTruthTable(exps)
# try:
from tabulate import tabulate
printTruthtable(exps, generateTruthTable(exps))
# except e:
# print(e)
content = latexify(exps, truthtable)

View File

@ -0,0 +1,16 @@
clear = '\033[0m'
colors = {
'red': '\033[31m',
'green': '\033[32m',
'yellow': '\033[33m',
'blue': '\033[34m',
'purple': '\033[35m',
'cyan': '\033[36m',
}
def printc(text, color='blue'):
print(f'{colors[color]}{text}{clear}')
def printerr(text):
print(f'\033[31;5m[ERROR] {text}{clear}')

View File

@ -5,9 +5,8 @@ import FSA
import Graph
import Hasse
import Truthtable
def printred(text):
print(f'\033[31m{text}\033[0m')
import Relations
from common import printerr
def fetchContentType(content):
new_content = content.split('\n')
@ -25,10 +24,12 @@ def processContent(content):
result = Graph.processFileContent('toGraph\n\n' + content, template.read())
elif contentType == 'Matrix':
result = Graph.processFileContent('toMatrix\n\n' + content, template.read())
elif contentType == 'Relations':
result = Relations.processFileContent(content, template.read())
elif contentType == 'Truthtable':
result = Truthtable.processFileContent(content, template.read())
else:
print('DIDN\'T RECOGNIZE FILE TYPE')
printerr('DIDN\'T RECOGNIZE FILE TYPE')
exit(1)
return result

View File

@ -0,0 +1,4 @@
# Relations
graph
AA BB CC DD EE AC CA AE EA CE EC BD DB

View File

@ -1,2 +1,4 @@
# Hasse
# Relations
hasseDiagram
ab ac ad ae bc ed ec

View File

@ -0,0 +1,4 @@
# Relations
proveEquivalence
AA BB CC DD EE AC CA AE EA CE EC BD DB

View File

@ -0,0 +1,4 @@
# Relations
provePoset
AA BB CC DD AB AC AD BC CD BD