Skip to content
Snippets Groups Projects
Commit 0d149b84 authored by Valentin Bruch's avatar Valentin Bruch
Browse files

new feature: Keldysh diagrams

parent dc2e9457
Branches
No related tags found
No related merge requests found
...@@ -26,7 +26,7 @@ class BaseTeX(object): ...@@ -26,7 +26,7 @@ class BaseTeX(object):
def tikz(self, position=''): def tikz(self, position=''):
'Return TikZ code for drawing self.' 'Return TikZ code for drawing self.'
return '' return r'\coordinate[%s] (%s);'%(position, self.identifier)
def left(self): def left(self):
'Return TikZ node id of leftmost subnode.' 'Return TikZ node id of leftmost subnode.'
...@@ -43,6 +43,7 @@ class Vertex(BaseTeX): ...@@ -43,6 +43,7 @@ class Vertex(BaseTeX):
Each vertex consists of n circles to which contraction lines are attached. Each vertex consists of n circles to which contraction lines are attached.
''' '''
def __init__(self, def __init__(self,
contour,
n, n,
label='', label='',
radius=0.5, radius=0.5,
...@@ -51,6 +52,7 @@ class Vertex(BaseTeX): ...@@ -51,6 +52,7 @@ class Vertex(BaseTeX):
draw_color='black' draw_color='black'
): ):
super().__init__() super().__init__()
self.contour = contour
self.n = n # rank of the vertex self.n = n # rank of the vertex
self.label = label self.label = label
self.radius = radius self.radius = radius
...@@ -102,6 +104,9 @@ class Vertex(BaseTeX): ...@@ -102,6 +104,9 @@ class Vertex(BaseTeX):
) )
if self.label: if self.label:
string += '\n' string += '\n'
if self.contour.keldysh_order > 0:
string += r'\node[below of=%s] {%s};'%(self.identifier, self.label);
else:
string += r'\node[above of=%s] {%s};'%(self.identifier, self.label); string += r'\node[above of=%s] {%s};'%(self.identifier, self.label);
return string return string
...@@ -141,9 +146,21 @@ class Contraction: ...@@ -141,9 +146,21 @@ class Contraction:
shape_style = ', ' + self.shape_style shape_style = ', ' + self.shape_style
else: else:
shape_style = '' shape_style = ''
return r'\draw[%s] (%s) to[out=270, in=270%s] (%s);'%( try:
if self.v1.contour.keldysh_order > self.v2.contour.keldysh_order:
directions = (90, 270)
elif self.v1.contour.keldysh_order < self.v2.contour.keldysh_order:
directions = (270, 90)
elif self.v1.contour.keldysh_order != 0:
directions = (270, 270)
else:
directions = (90, 90)
except:
directions = (270, 270)
return r'\draw[%s] (%s) to[out=%d, in=%d%s] (%s);'%(
self.style, self.style,
self.v1.getId(self.idx1), self.v1.getId(self.idx1),
*directions,
shape_style, shape_style,
self.v2.getId(self.idx2), self.v2.getId(self.idx2),
) )
...@@ -154,12 +171,38 @@ class Interrupt(BaseTeX): ...@@ -154,12 +171,38 @@ class Interrupt(BaseTeX):
Node interrupting the diagram contour. Node interrupting the diagram contour.
This can be, e.g., a node ρ separating the two branches of the Keldysh contour. This can be, e.g., a node ρ separating the two branches of the Keldysh contour.
''' '''
def __init__(self, label=''): def __init__(self, label='', **kwargs):
super().__init__() super().__init__()
self.label = label self.label = label
for key, value in kwargs.items():
setattr(self, key, value)
def tikz(self, position=''): def isKeldysh(self):
return hasattr(self, 'keldysh_sep')
def tikzKeldyshConnect(self, previous):
if getattr(self, 'keldysh_connect', None) is None:
return ''
try:
assert self.keldysh_direction == 'right'
return r'\draw[%s] (%s) to[out=0, in=0] (%s);'%(self.keldysh_connect, previous.identifier, self.identifier)
except:
return r'\draw[%s] (%s) to[out=180, in=180] (%s);'%(self.keldysh_connect, previous.identifier, self.identifier)
def tikz(self, position='', previous=None):
'Return TikZ code for this interruption.' 'Return TikZ code for this interruption.'
if self.isKeldysh():
string = r'\coordinate[yshift=-%s] (%s) at (%s);'%(self.keldysh_sep, self.identifier, previous.identifier)
if getattr(self, 'keldysh_label', ''):
try:
assert self.keldysh_direction == 'right'
keldysh_label_shift = '1em'
except:
keldysh_label_shift = '-1em'
string += '\n'
string += r'\path (%s) -- (%s) node[pos=0.5, xshift=%s] {%s};'%(previous.identifier, self.identifier, keldysh_label_shift, self.keldysh_label)
return string
else:
if position: if position:
position = ', ' + position position = ', ' + position
return r'\node[fill=white%s] (%s) {%s};'%(position, self.identifier, self.label) return r'\node[fill=white%s] (%s) {%s};'%(position, self.identifier, self.label)
...@@ -190,6 +233,9 @@ class BaseLine: ...@@ -190,6 +233,9 @@ class BaseLine:
def pprint(self): def pprint(self):
print(self.string, end='') print(self.string, end='')
def tikz(self, node1, node2):
return r'\draw[%s] (%s) -- (%s);'%(self.style, node1.identifier, node2.identifier)
class Contour: class Contour:
''' '''
...@@ -198,6 +244,7 @@ class Contour: ...@@ -198,6 +244,7 @@ class Contour:
''' '''
def __init__(self): def __init__(self):
self.elements = [] self.elements = []
self.keldysh_order = -1
def pprint(self): def pprint(self):
for e in self.elements: for e in self.elements:
...@@ -246,6 +293,8 @@ class Diagram: ...@@ -246,6 +293,8 @@ class Diagram:
\end{pgfonlayer} \end{pgfonlayer}
\end{tikzpicture} \end{tikzpicture}
>>> # Keldysh diagram (still quite ugly)
>>> Diagram('- g12 - g23 - | -- g13 -')
>>> # Diagram with customized vertice, contraction, base line, and interruption. >>> # Diagram with customized vertice, contraction, base line, and interruption.
>>> d = Diagram() >>> d = Diagram()
>>> d.setVertexStyle('V', n=2, label='$V$') >>> d.setVertexStyle('V', n=2, label='$V$')
...@@ -262,6 +311,7 @@ class Diagram: ...@@ -262,6 +311,7 @@ class Diagram:
self.contractions = [] self.contractions = []
self.contours = [] self.contours = []
self.interruptions = [] self.interruptions = []
self.isKeldysh = False
# Properties # Properties
self.sep = sep self.sep = sep
self.sep_unit = sep_unit self.sep_unit = sep_unit
...@@ -281,7 +331,7 @@ class Diagram: ...@@ -281,7 +331,7 @@ class Diagram:
'~':dict(style='wiggly, thick') '~':dict(style='wiggly, thick')
} }
self.interrupt_styles = { self.interrupt_styles = {
'|':dict(label=''), '|':dict(label='', keldysh_sep='4ex', keldysh_direction='left', keldysh_label='', keldysh_connect='thick'),
'ρ':dict(label=r'$\rho$'), 'ρ':dict(label=r'$\rho$'),
} }
if string: if string:
...@@ -362,9 +412,11 @@ class Diagram: ...@@ -362,9 +412,11 @@ class Diagram:
# Start by an interrupt (possibly empty). # Start by an interrupt (possibly empty).
try: try:
self.interruptions = [Interrupt(**self.interrupt_styles[string[0]])] self.interruptions = [Interrupt(**self.interrupt_styles[string[0]])]
if self.interruptions[-1].isKeldysh():
self.isKeldysh = True
string = string[1:] string = string[1:]
except KeyError: except KeyError:
self.interruptions = [Interrupt()] self.interruptions = [BaseTeX()]
# Map of open indices: every open index is mapped to it origin (vertex, subindex). # Map of open indices: every open index is mapped to it origin (vertex, subindex).
vertex_indices = {} vertex_indices = {}
...@@ -379,7 +431,7 @@ class Diagram: ...@@ -379,7 +431,7 @@ class Diagram:
# Check if c represents a vertex. # Check if c represents a vertex.
if c in self.vertex_styles: if c in self.vertex_styles:
# Add a new vertex to the current contour. # Add a new vertex to the current contour.
self.contours[-1].elements.append(Vertex(**self.vertex_styles[c])) self.contours[-1].elements.append(Vertex(self.contours[-1], **self.vertex_styles[c]))
# Read the indices for this vertex. # Read the indices for this vertex.
for j in range(self.vertex_styles[c]['n']): for j in range(self.vertex_styles[c]['n']):
s = string[i+1+j] s = string[i+1+j]
...@@ -412,6 +464,8 @@ class Diagram: ...@@ -412,6 +464,8 @@ class Diagram:
elif c in self.interrupt_styles.keys(): elif c in self.interrupt_styles.keys():
# Add an interruption and start a new contour. # Add an interruption and start a new contour.
self.interruptions.append(Interrupt(**self.interrupt_styles[c])) self.interruptions.append(Interrupt(**self.interrupt_styles[c]))
if self.interruptions[-1].isKeldysh():
self.isKeldysh = True
self.contours.append(Contour()) self.contours.append(Contour())
else: else:
# Invalid character in diagram. # Invalid character in diagram.
...@@ -465,62 +519,85 @@ class Diagram: ...@@ -465,62 +519,85 @@ class Diagram:
\tikzset{wiggly/.style={decorate, decoration=snake}} \tikzset{wiggly/.style={decorate, decoration=snake}}
\tikzset{zigzag/.style={decorate, decoration=zigzag}} \tikzset{zigzag/.style={decorate, decoration=zigzag}}
''' '''
# List of all TikZ nodes on the diagram axis combined with the number of prior base lines.
nodes = [] # Begin creating the TikZ picture.
# List of all base line objects with the indices of their starting node: (baseline, index). print(r'\begin{tikzpicture}[node distance=%s, baseline=(%s.base)]'%(node_distance, self.interruptions[0].identifier), file=file)
baselines = {}
# Iterate over contours, interruptions and elements to collect all nodes and baselines. # Create the first node.
for i, contour in enumerate(self.contours): print(self.interruptions[0].tikz(), file=file)
# Iterate over contours, interruptions and elements to draw vertices and interruptions.
last_node = self.interruptions[0]
keldysh_order = -1 + self.isKeldysh
for i, contour in enumerate(self.contours, 1):
# First draw the contour.
contour.keldysh_order = keldysh_order
for j, e in enumerate(contour.elements):
if type(e) != BaseLine:
factor = getattr(contour.elements[j-1], 'factor', 1) or 1
print(e.tikz(position='right of=%s, xshift=%g%s'%(last_node.right(), factor*self.sep, self.sep_unit)), file=file)
last_node = e
# Then draw the interruption.
try: try:
if self.interruptions[i].label: if self.interruptions[i].isKeldysh():
nodes.append(self.interruptions[i]) if getattr(self.interruptions[i], 'keldysh_direction', 'left') == 'left':
print(self.interruptions[i].tikz(previous=self.interruptions[i-1]), file=file)
else:
assert type(last_node) == Interrupt
last_node = self.interruptions[i]
keldysh_order += 1
elif self.interruptions[i].label:
factor = getattr(contour.elements[-1], 'factor', 1) or 1
print(self.interruptions[i].tikz(position='right of=%s, xshift=%g%s'%(last_node.right(), factor*self.sep, self.sep_unit)), file=file)
last_node = self.interruptions[i]
except: except:
pass pass
for e in contour.elements:
if type(e) == BaseLine:
baselines[len(nodes)-1] = e
else:
nodes.append(e)
# Collect trailing interruptions. # Collect trailing interruptions.
for i in range(len(self.contours), len(self.interruptions)): for i in range(len(self.contours), len(self.interruptions)):
try: try:
if self.interruptions[i].label: if self.interruptions[i].label and not self.interruptions[i].isKeldysh():
nodes.append(self.interruptions[i]) print(self.interruptions[i].tikz(position='right of=%s, xshift=%g%s'%(self.interruptions[i-1].identifier, self.sep, self.sep_unit)), file=file)
except: except:
pass break
# Begin creating the TikZ picture.
print(r'\begin{tikzpicture}[node distance=%s, baseline=(%s.base)]'%(node_distance, nodes[0].identifier), file=file)
# Create the first node.
try:
print(nodes[0].tikz(), file=file)
except:
print('Error while printing node tikz:', nodes[0], file=sys.stderr)
# Draw vertices and interruptions (including their labels).
for i, n in enumerate(nodes[1:]):
try:
factor = baselines[i].factor or 1
except:
factor = 1
print(n.tikz(position='right of=%s, xshift=%g%s'%(nodes[i].right(), factor*self.sep, self.sep_unit)), file=file)
# Draw the rest on the background. # Draw the rest on the background.
print(r'\begin{pgfonlayer}{background}', file=file) print(r'\begin{pgfonlayer}{background}', file=file)
# Draw base lines. # Draw base lines.
for i, b in baselines.items(): for i, contour in enumerate(self.contours, 1):
if i < 0: # First draw the contour.
# Base line left of all nodes. if type(contour.elements[0]) == BaseLine:
print(r'\coordinate[left of=%s, xshift=-%s%s] (left);'%(nodes[0].identifier, b.factor*self.sep, self.sep_unit), file=file) print(contour.elements[0].tikz(self.interruptions[i-1], contour.elements[1]), file=file)
print(r'\draw[%s] (left) -- (%s);'%(b.style, nodes[0].identifier), file=file) for j, e in enumerate(contour.elements[1:], 1):
elif i > len(nodes)-2: if type(e) == BaseLine:
# Base line right of all nodes. try:
print(r'\coordinate[right of=%s, xshift=%s%s] (right);'%(nodes[-1].identifier, b.factor*self.sep, self.sep_unit), file=file) # Base line between last_node and e.
print(r'\draw[%s] (%s) -- (right);'%(b.style, nodes[-1].identifier), file=file) print(e.tikz(contour.elements[j-1], contour.elements[j+1]), file=file)
except:
pass
if type(e) == BaseLine:
try:
assert not self.interruptions[i].isKeldysh()
print(e.tikz(contour.elements[-2], self.interruptions[i]), file=file)
except:
try:
print(r'\coordinate[right of=%s, xshift=%g%s] (right%d);'%(contour.elements[-2].identifier, e.factor*self.sep, self.sep_unit, i), file=file)
print(r'\draw[%s] (%s) -- (right%d);'%(e.style, contour.elements[-2].identifier, i), file=file)
except:
pass
try:
if self.interruptions[i].isKeldysh():
# Draw connection of the contours.
if self.interruptions[i].keldysh_direction == 'right':
self.interruptions[i].tikz(previous=last_node)
string = self.interruptions[i].tikzKeldyshConnect(last_node)
else: else:
# Base line between two nodes. string = self.interruptions[i].tikzKeldyshConnect(self.interruptions[i-1])
print(r'\draw[%s] (%s) -- (%s);'%(b.style, nodes[i].identifier, nodes[i+1].identifier), file=file) if string:
print(string, file=file)
except:
pass
# Draw all contractions. # Draw all contractions.
for c in self.contractions: for c in self.contractions:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment