Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimise time taken to get raw graph #2312

Merged
merged 2 commits into from
Jun 6, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
383 changes: 189 additions & 194 deletions lib/cylc/config.py

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions lib/cylc/cycling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,17 @@ def __getitem__(self, key):
"""Allows indexing of the exclusion object"""
return self.exclusion_sequences[key]

def __str__(self):
returns = []
for point in sorted(self.exclusion_points):
returns.append(str(point))
for sequence in self.exclusion_sequences:
returns.append(str(sequence))
ret = ','.join(returns)
if ',' in ret:
ret = '(' + ret + ')'
return ret


if __name__ == "__main__":
import unittest
Expand Down
2 changes: 1 addition & 1 deletion lib/cylc/cycling/iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ def __init__(self, dep_section, context_start_point=None,
self.step = ISO8601Interval(str(self.recurrence.duration))
self.value = str(self.recurrence)
# Concatenate the strings in exclusion list
if self.exclusions:
if str(self.exclusions):
self.value += '!' + str(self.exclusions)

def get_interval(self):
Expand Down
133 changes: 34 additions & 99 deletions lib/cylc/graphing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,8 @@
"""Cylc suite graphing module. Modules relying on this should test for
ImportError due to pygraphviz/graphviz not being installed."""

import re
import pygraphviz
from cylc.task_id import TaskID
from cycling.loader import get_point, get_point_relative, get_interval
from graphnode import graphnode

# TODO: Do we still need autoURL below?


class CGraphPlain(pygraphviz.AGraph):
Expand Down Expand Up @@ -55,7 +50,7 @@ def node_attr_by_taskname(self, node_string):
def style_edge(self, left, right):
pass

def style_node(self, node_string, autoURL, base=False):
def style_node(self, node_string, base=False):
node = self.get_node(node_string)
try:
name, point_string = TaskID.split(node_string)
Expand All @@ -71,30 +66,12 @@ def style_node(self, node_string, autoURL, base=False):
label += "\\n" + self.suite_polling_tasks[name][3]
label += "\\n" + point_string
node.attr['label'] = label
if autoURL:
if base:
# TODO - This is only called from cylc_add_edge in this
# base class ... should it also be called from add_node?
node.attr['URL'] = 'base:' + node_string
else:
node.attr['URL'] = node_string

def cylc_add_node(self, node_string, autoURL, **attr):
pygraphviz.AGraph.add_node(self, node_string, **attr)
self.style_node(node_string, autoURL)

def cylc_add_edge(self, left, right, autoURL, **attr):
if left is None and right is None:
pass
elif left is None:
self.cylc_add_node(right, autoURL)
elif right is None:
self.cylc_add_node(left, autoURL)
if base:
# TODO - This is only called from add_edges in this
# base class ... should it also be called from add_node?
node.attr['URL'] = 'base:' + node_string
else:
pygraphviz.AGraph.add_edge(self, left, right, **attr)
self.style_node(left, autoURL, base=True)
self.style_node(right, autoURL, base=True)
self.style_edge(left, right)
node.attr['URL'] = node_string

def cylc_remove_nodes_from(self, nodes):
"""Remove nodes, returning extra edge structure if possible.
Expand All @@ -107,7 +84,6 @@ def cylc_remove_nodes_from(self, nodes):
if not nodes:
return
existing_nodes = set(self.nodes())
add_edges = set()
remove_nodes = set(nodes)
remove_node_groups = {}
groups = {}
Expand Down Expand Up @@ -221,39 +197,36 @@ def cylc_remove_nodes_from(self, nodes):
new_edges.add((l_node, new_r_node, True, False, False))

self.remove_nodes_from(nodes)
self.add_edges(list(new_edges))
self.add_edges(sorted(new_edges))

def add_edges(self, edges, ignore_suicide=False):
edges.sort() # TODO: does sorting help layout stability?
"""Add edges and nodes connected by the edges."""
for edge in edges:
left, right, skipped, suicide, conditional = edge
if suicide and ignore_suicide:
if left is None and right is None or suicide and ignore_suicide:
continue
attrs = {}
if conditional:
if suicide:
attrs['style'] = 'dashed'
attrs['arrowhead'] = 'odot'
else:
attrs['style'] = 'solid'
attrs['arrowhead'] = 'onormal'
if left is None:
pygraphviz.AGraph.add_node(self, right)
self.style_node(right)
elif right is None:
pygraphviz.AGraph.add_node(self, left)
self.style_node(left)
else:
if suicide:
attrs['style'] = 'dashed'
attrs['arrowhead'] = 'dot'
attrs = {'penwidth': 2}
if skipped:
attrs.update({'style': 'dotted', 'arrowhead': 'oinv'})
elif conditional and suicide:
attrs.update({'style': 'dashed', 'arrowhead': 'odot'})
elif conditional:
attrs.update({'style': 'solid', 'arrowhead': 'onormal'})
elif suicide:
attrs.update({'style': 'dashed', 'arrowhead': 'dot'})
else:
attrs['style'] = 'solid'
attrs['arrowhead'] = 'normal'
if skipped:
# override
attrs['style'] = 'dotted'
attrs['arrowhead'] = 'oinv'

attrs['penwidth'] = 2

self.cylc_add_edge(
left, right, True, **attrs
)
attrs.update({'style': 'solid', 'arrowhead': 'normal'})
pygraphviz.AGraph.add_edge(self, left, right, **attrs)
self.style_node(left, base=True)
self.style_node(right, base=True)
self.style_edge(left, right)

def add_cycle_point_subgraphs(self, edges):
"""Draw nodes within cycle point groups (subgraphs)."""
Expand Down Expand Up @@ -314,11 +287,11 @@ def __init__(self, title, suite_polling_tasks={}, vizconfig={}):
# graph attributes
# - default node attributes
for item in vizconfig['default node attributes']:
attr, value = re.split('\s*=\s*', item)
attr, value = [val.strip() for val in item.split('=', 1)]
self.node_attr[attr] = value
# - default edge attributes
for item in vizconfig['default edge attributes']:
attr, value = re.split('\s*=\s*', item)
attr, value = [val.strip() for val in item.split('=', 1)]
self.edge_attr[attr] = value

# non-default node attributes by task name
Expand All @@ -341,12 +314,11 @@ def __init__(self, title, suite_polling_tasks={}, vizconfig={}):
self.task_attr[item] = []
self.task_attr[item].append(attr)

def style_node(self, node_string, autoURL, base=False):
super(self.__class__, self).style_node(
node_string, autoURL, False)
def style_node(self, node_string, base=False):
super(self.__class__, self).style_node(node_string, base)
node = self.get_node(node_string)
for item in self.node_attr_by_taskname(node_string):
attr, value = re.split('\s*=\s*', item)
attr, value = [val.strip() for val in item.split('=', 1)]
node.attr[attr] = value
if self.vizconfig['use node color for labels']:
node.attr['fontcolor'] = node.attr['color']
Expand All @@ -362,40 +334,3 @@ def style_edge(self, left, right):
if left_node.attr['style'] == 'filled':
edge.attr['color'] = left_node.attr['fillcolor']
edge.attr['penwidth'] = self.vizconfig['edge penwidth']


class edge(object):
def __init__(self, left, right, sequence, suicide=False,
conditional=False):
"""contains qualified node names, e.g. 'foo[T-6]:out1'"""
self.left = left
self.right = right
self.suicide = suicide
self.sequence = sequence
self.conditional = conditional

def get_right(self, inpoint, start_point):
inpoint_string = str(inpoint)
if self.right is None:
return None

# strip off special outputs
self.right = re.sub(':\w+', '', self.right)

return TaskID.get(self.right, inpoint_string)

def get_left(self, inpoint, start_point, base_interval):
# strip off special outputs
left = re.sub(':[\w-]+', '', self.left)

left_graphnode = graphnode(left, base_interval=base_interval)
if left_graphnode.offset_is_from_ict:
point = get_point_relative(left_graphnode.offset_string,
start_point)
elif left_graphnode.offset_string:
point = get_point_relative(left_graphnode.offset_string, inpoint)
else:
point = inpoint
name = left_graphnode.name

return TaskID.get(name, point)
Loading