Several fixes

This commit is contained in:
2021-05-17 18:39:28 +02:00
parent a2f9fdbe09
commit adfc6c4bc4
30 changed files with 563 additions and 155 deletions

View File

@@ -2,6 +2,8 @@ from sys import argv
from pathlib import Path
import re
from common import replaceContent
placementChart = {
'a': 'above',
'b': 'below',
@@ -62,16 +64,17 @@ def generate_latex(inputLines):
return '\n'.join(output)
def processFileContent(raw, template):
def processFileContent(raw):
content = generate_latex(raw)
return template.replace('%CONTENT', content)
return replaceContent(content, 'FSA')
if __name__ == '__main__':
filename = argv[1]
pass
# filename = argv[1]
with open(filename) as file:
content = generate_latex(file.read())
# with open(filename) as file:
# content = generate_latex(file.read())
with open(str(Path(__file__).parent.absolute()) + '/tex_templates/FSA.tex') as template:
with open(argv[2], 'w') as destination_file:
destination_file.write(template.read().replace('%CONTENT', content))
# with open(str(Path(__file__).parent.absolute()) + '/tex_templates/FSA.tex') as template:
# with open(argv[2], 'w') as destination_file:
# destination_file.write(template.read().replace('%CONTENT', content))

View File

@@ -2,7 +2,7 @@ from sys import argv
from pathlib import Path
from math import sin, cos, pi
from common import printc
from common import printc, replaceContent
class Matrix:
""" Adjacency matrix which supports 0 and 1 """
@@ -186,7 +186,7 @@ def generateNodeCoords(n):
return nodeCoords
def processFileContent(raw, template):
def processFileContent(raw):
lines = raw.split('\n')
lines.pop(1)
outputType = lines.pop(0)
@@ -198,15 +198,20 @@ def processFileContent(raw, template):
if outputType == 'toGraph':
content = graph.toLaTeX()
return template.replace('%NODES', content[0]).replace('%EDGES', content[1])
return replaceContent(
content,
'Graph',
replaceF = lambda temp, cont: temp.replace('%NODES', cont[0]).replace('%EDGES', cont[1])
)
else:
content = graph.toMatrix().toLaTeX()
return template.replace('%CONTENT', content)
return replaceContent(content, 'Matrix')
if __name__ == '__main__':
matrix = Matrix([[False, False, True], [True, False, True], [True, False, False]])
print(matrix)
print(matrix.toGraph())
print(matrix.toGraph().isUndirected())
pass
# matrix = Matrix([[False, False, True], [True, False, True], [True, False, False]])
# print(matrix)
# print(matrix.toGraph())
# print(matrix.toGraph().isUndirected())

View File

@@ -1,7 +1,7 @@
from sys import argv
from pathlib import Path
from common import printc
from common import printc, replaceContent
# Increase if the diagram becomes too clobbered
HEIGHT_SEPARATOR = 1
@@ -64,23 +64,11 @@ def latex_hasse(hasse):
return '\n'.join(output)
def processFileContent(raw, template):
def processFileContent(raw):
rels = getRels(raw)
content = latex_hasse(hasse_diagram(rels))
return template.replace("%CONTENT", content)
return replaceContent(content, 'Hasse')
if __name__ == '__main__':
filename = argv[1]
with open(filename) as file:
rels = getRels(file.read())
# rels = getRels()
# rels = divisibility_graph(30)
content = latex_hasse(hasse_diagram(rels))
with open(str(Path(__file__).parent.absolute()) + '/tex_templates/Hasse.tex') as template:
with open(argv[2], 'w') as destination_file:
destination_file.write(template.read().replace('%CONTENT', content))
pass

View File

@@ -1,5 +1,7 @@
from itertools import combinations
from common import replaceContent
class Set:
def __init__(self, elements):
self.elements = set(elements)
@@ -27,6 +29,10 @@ class Set:
def __le__(self, value):
return self < value
@classmethod
def fromString(cls, string):
return cls(string.split(' '))
def cardinality(self):
return len(self)
@@ -44,6 +50,13 @@ class Set:
column.append(str(e) + ' \\\\')
return '\\{\n' + '\n'.join(column) + '\n\\}'
def processFileContent(raw):
s = Set.fromString(raw)
content = s.powerset().to_vertical_latex()
return replaceContent(content, 'Powerset')
#TODO: make process input func
if __name__ == "__main__":

View File

@@ -1,11 +1,10 @@
from sys import argv
from pathlib import Path
from common import printc, printerr
from Graph import Graph
from common import printc, printerr, replaceContent
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():
@@ -19,6 +18,18 @@ def stringToPair(string):
else:
return tuple(list(string))
def textLatex(string):
return '\\text{' + string + '}'
def mathModeListLatex(elements):
if len(elements) > 7 or len(elements) and max(len(str(e)) for e in elements) > 10:
return '\\begin{gather*}\n' \
+ ' \\\\\n'.join(elements) + '\n' \
+ '\\end{gather*}'
else:
return f'\\[ {", ".join(elements)} \\]'
class Relation:
def __init__(self, pairs): # pair = (str,str)
@@ -29,7 +40,7 @@ class Relation:
@classmethod
def fromString(cls, string):
relations = (stringToPair(x) for x in relations.split(' '))
relations = (stringToPair(x) for x in string.split(' '))
return cls(relations)
@classmethod
@@ -43,7 +54,7 @@ class Relation:
return cls(pairs)
def isReflexive(self):
def isReflexive(self, verbose=False):
elements = set(list(sum(self.pairs, ())))
result = all((x,x) in self.pairs for x in elements)
@@ -51,31 +62,47 @@ class Relation:
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)} \\]')
if verbose:
return (False, missingPairs)
if verbose:
return (True, [(x,x) for x in elements if (x,x) in self.pairs])
return result
def isSymmetric(self):
def isSymmetric(self, verbose=False):
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)} \\]')
if verbose:
return (False, missingPairs)
if verbose:
return (True, [((x,y), (y,x)) for x,y in self.pairs if (y,x) in self.pairs and x < y])
return result
def isAntiSymmetric(self):
def isAntiSymmetric(self, verbose=False):
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)} \\]')
print(f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs)} \\]')
if verbose:
return (False, symmetricPairs)
if verbose:
return (True, [])
return result
def isTransitive(self):
def isTransitive(self, verbose=False):
result = True
transitivePairs = []
nonTransitivePairs = []
for x,y in self.pairs:
@@ -83,33 +110,110 @@ class Relation:
if not (y != z or ((x,w) in self.pairs)):
nonTransitivePairs.append(((x,y), (z,w)))
result = False
elif (y == z and x != y != w and ((x,w) in self.pairs)):
transitivePairs.append(((x,y), (z,w)))
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)} \\]')
print(f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in nonTransitivePairs)} \\]')
if verbose:
return (False, nonTransitivePairs)
if verbose:
return (True, transitivePairs)
return result
def isEquivalenceRelation(self):
def isEquivalenceRelation(self, verbose=False):
if verbose:
isReflexive, reflexivePairs = self.isReflexive(verbose=True)
isSymmetric, symmetricPairs = self.isSymmetric(verbose=True)
isTransitive, transitivePairs = self.isTransitive(verbose=True)
if not isReflexive:
return 'The relation is not an equivalence relation, because it is not reflexive.'
+ ' The following elements should be related:\n\n'
+ f'\\[ {", ".join(pairToString(p) for p in reflexivePairs)} \\]'
if not isSymmetric:
return 'The relation is not an equivalence relation, because it is not symmetric.'
+ ' It is missing the following symmetric pairs of relations\n\n'
+ f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs)} \\]'
if not isTransitive:
return 'The relation is not an equivalence relation, because it is not transitive.'
+ ' The following pairs of relations are missing its transitive counterpart\n\n'
+ f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs)} \\]'
rxStr = mathModeListLatex([pairToString(p) for p in reflexivePairs])
smStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in symmetricPairs])
trStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" with ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs])
return replaceContent(
(rxStr, smStr, trStr),
'Relations/EquivalenceProof',
lambda temp, cont: temp.replace('%REFLEXIVE', cont[0]).replace('%SYMMETRIC', cont[1]).replace('%TRANSITIVE', cont[2])
)
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():
def isPartialOrder(self, verbose=False):
result = self.isReflexive() and self.isAntiSymmetric() and self.isTransitive()
if result:
hasse= self.getHassePairs(checkIfPartialOrder=False)
min_el = set(a for a,b in hasse if a not in list(zip(*hasse))[1])
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)} \}}$" )
if verbose:
isReflexive, reflexivePairs = self.isReflexive(verbose=True)
isAntiSymmetric, antiSymmetricPairs = self.isAntiSymmetric(verbose=True)
isTransitive, transitivePairs = self.isTransitive(verbose=True)
if not isReflexive:
return 'The relation is not a partial order, because it is not reflexive.'
+ ' The following elements should be related:\n\n'
+ f'\\[ {", ".join(pairToString(p) for p in reflexivePairs)} \\]'
if not isAntiSymmetric:
return 'The relation is not a partial order, because it is not antisymmetric.'
+ ' The following relations are symmetric\n\n'
+ f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}""" for p,q in antiSymmetricPairs)} \\]'
if not isTransitive:
return 'The relation is not a partial order, because it is not transitive.'
+ ' The following pairs of relations are missing its transitive counterpart\n\n'
+ f'\\[ {", ".join(f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" without ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs)} \\]'
rxStr = mathModeListLatex([pairToString(p) for p in reflexivePairs])
trStr = mathModeListLatex([f"""{pairToString(p)}{textLatex(" and ")}{pairToString(q)}{textLatex(" with ")}{pairToString((p[0], q[1]))}""" for p,q in transitivePairs])
return replaceContent(
(rxStr, trStr),
'Relations/PosetProof',
lambda temp, cont: temp.replace('%REFLEXIVE', cont[0]).replace('%TRANSITIVE', cont[1])
)
return result
def getHassePairs(self, checkIfPartialOrder=True):
if checkIfPartialOrder and not self.isPartialOrder():
printerr('This is not a partial order')
return
nonReflexivePairs = set((x,y) for x,y in self.pairs if x != y)
hassePairs = set()
for x1, y1 in self.pairs:
for x2, y2 in self.pairs:
for x1, y1 in nonReflexivePairs:
for x2, y2 in nonReflexivePairs:
if y1 == x2:
hassePairs.add((x1, y2))
return self.pairs - hassePairs
return nonReflexivePairs - 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)
@@ -127,19 +231,13 @@ class Relation:
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(f'\\node ({n}) at ({i - len(inv_ypos[y])/2}, {y * 0.5 * (len(hasse) ** 0.4)}) {{${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):
@@ -150,33 +248,46 @@ class Relation:
graphType = 'directed'
pairs = self.pairs
pass
nodes = set()
for x,y in pairs:
nodes.add(x)
nodes.add(y)
edges = "\n".join(pairToString(p) for p in pairs)
processingString = f'toGraph\n\n{graphType}\n\n{ " ".join(nodes) }\n\n{edges}'
return Graph.processFileContent(processingString)
def processFileContent(raw, template):
def processFileContent(raw):
outputType, inp = raw.split('\n\n')
if inp.startsWith('divisibilityPosetTo'):
if inp.startswith('divisibilityPosetTo'):
n = int(inp.split(' ')[1])
relation = Relation.fromDivisibilityPosetTo(n)
else:
relation = Relation.fromString(inp)
if outputType == 'proveEquivalence':
content = relation.isEquivalenceRelation()
content = relation.isEquivalenceRelation(verbose=True)
return content
elif outputType == 'provePoset':
content = relation.isPartialOrder()
content = relation.isPartialOrder(verbose=True)
return content
elif outputType == 'hasseDiagram':
content = relation.latexifyHasseDiagram()
return replaceContent(content, 'Relations/Hasse')
elif outputType == 'graph':
content = relation.latexifyGraph()
content = Relation.fromString(raw).latexifyHasseDiagram()
return template.replace("%CONTENT", content)
return 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())
pass
# 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,7 +1,7 @@
from sys import argv
from pathlib import Path
from common import printerr
from common import printerr, replaceContent
try:
from tabulate import tabulate
@@ -20,18 +20,21 @@ class OverloadedBool:
self.val = val
def __bool__(self):
return self.val
val = self.val
while(type(val) is OverloadedBool):
val = val.val
return val
def __str__(self):
return str(self.val)
def __add__(self, bool2):
""" Implies """
return (not self.val or bool2)
return OverloadedBool(not self.val or bool2)
def __sub__(self, bool2):
""" Iff """
return (not self.val or bool2) and (not bool2 or self.val)
return OverloadedBool((not self.val or bool2) and (not bool2 or self.val))
def flattenImpliesIff(exps):
return [exp.replace('implies', '+').replace('iff', '-').replace('E', '') for exp in exps]
@@ -51,7 +54,7 @@ def generateTruthTable(exps):
def calculateGridLine():
line = []
for exp in exps:
exec(f'line.append(({exp}).__bool__())')
exec(f'line.append(({exp}))')
return line
truthtable.append(calculateGridLine())
@@ -95,30 +98,14 @@ def printTruthtable(exps, truthtable):
except:
pass
def processFileContent(raw, template):
def processFileContent(raw):
exps = parseExpressionList(raw)
truthtable = generateTruthTable(exps)
printTruthtable(exps, generateTruthTable(exps))
content = latexify(exps, truthtable)
return template.replace('%CONTENT', content)
return replaceContent(content, 'Truthtable')
if __name__ == '__main__':
filename = argv[1]
with open(filename) as file:
exps = parseExpressionList(file.read())
truthtable = generateTruthTable(exps)
from tabulate import tabulate
printTruthtable(exps, generateTruthTable(exps))
content = latexify(exps, truthtable)
with open(str(Path(__file__).parent.absolute()) + '/tex_templates/Truthtable.tex') as template:
with open(argv[2], 'w') as destination_file:
destination_file.write(template.read().replace('%CONTENT', content))
pass

View File

@@ -1,3 +1,4 @@
from pathlib import Path
clear = '\033[0m'
colors = {
@@ -13,4 +14,8 @@ def printc(text, color='blue'):
print(f'{colors[color]}{text}{clear}')
def printerr(text):
print(f'\033[31;5m[ERROR] {text}{clear}')
print(f'\033[31;5m[ERROR] {text}{clear}')
def replaceContent(content, template, replaceF = lambda temp, cont: temp.replace("%CONTENT", cont)):
with open(str(Path(__file__).parent.absolute()) + f'/tex_templates/{template}.tex') as template:
return replaceF(template.read(), content)

View File

@@ -15,22 +15,21 @@ def fetchContentType(content):
def processContent(content):
contentType, content = fetchContentType(content)
with open(str(Path(__file__).parent.absolute()) + f'/tex_templates/{contentType}.tex') as template:
if contentType == 'Hasse':
result = Hasse.processFileContent(content, template.read())
elif contentType == 'FSA':
result = FSA.processFileContent(content, template.read())
elif contentType == 'Graph':
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:
printerr('DIDN\'T RECOGNIZE FILE TYPE')
exit(1)
if contentType == 'FSA':
result = FSA.processFileContent(content)
elif contentType == 'Graph':
result = Graph.processFileContent('toGraph\n\n' + content)
elif contentType == 'Matrix':
result = Graph.processFileContent('toMatrix\n\n' + content)
elif contentType == 'Relations':
result = Relations.processFileContent(content)
elif contentType == 'Truthtable':
result = Truthtable.processFileContent(content)
else:
printerr('DIDN\'T RECOGNIZE FILE TYPE')
exit(1)
return result
if __name__ == '__main__':

View File

@@ -0,0 +1,8 @@
\codeFile{%CODEFILE}{python}
Output:
\begin{verbatim}
%OUTPUT
\end{verbatim}

View File

@@ -0,0 +1,22 @@
In order for this relation to be an equivalence equation, it has to be reflexive, symmetric and transitive.
\textbf{Reflexive:}
All elements are related to themself
%REFLEXIVE
\textbf{Symmetric:}
All relations has its symmetric counterpart
%SYMMETRIC
\textbf{Transitive:}
All pair of relations where $xRy$ and $yRz$ has its transitive counterpart
%TRANSITIVE
Hence the relation is an equivalence relation

View File

@@ -0,0 +1,22 @@
In order for this relation to be a partial order, it has to be reflexive, antisymmetric and transitive.
\textbf{Reflexive:}
All elements are related to themself
%REFLEXIVE
\textbf{Antisymmetric:}
No relation have a symmetric counterpart \\
(Listing the ones that don't have a symmetric counterpart would just be listing the whole set) \\
\textbf{Transitive:}
All pair of relations where $xRy$ and $yRz$ has its transitive counterpart
%TRANSITIVE
Hence the relation is a partial order