Several updates
This commit is contained in:
parent
f440e3cb4c
commit
a2f9fdbe09
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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())
|
|
@ -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())
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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}')
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Relations
|
||||
graph
|
||||
|
||||
AA BB CC DD EE AC CA AE EA CE EC BD DB
|
|
@ -1,2 +1,4 @@
|
|||
# Hasse
|
||||
# Relations
|
||||
hasseDiagram
|
||||
|
||||
ab ac ad ae bc ed ec
|
|
@ -0,0 +1,4 @@
|
|||
# Relations
|
||||
proveEquivalence
|
||||
|
||||
AA BB CC DD EE AC CA AE EA CE EC BD DB
|
|
@ -0,0 +1,4 @@
|
|||
# Relations
|
||||
provePoset
|
||||
|
||||
AA BB CC DD AB AC AD BC CD BD
|
Loading…
Reference in New Issue