Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
bqpd committed Aug 10, 2020
1 parent 03790a7 commit d2057e6
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 23 deletions.
2 changes: 1 addition & 1 deletion docs/source/examples/performance_modeling_output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,4 @@ Sensitivity Differences |above 0.1|
-----------------------------------
The largest is -0.00451643.

Links exceed link limit (56 > 50); skipping constraints inside arrays.
Links exceed link limit (52 > 50); skipping constraints inside arrays.
56 changes: 34 additions & 22 deletions gpkit/interactive/sankey.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ def getcolor(value):
return "#cfcfcf"
return GPCOLORS[0 if value < 0 else 1]


# pylint: disable=too-many-instance-attributes
class Sankey:
"diagrams of sensitivity flow"
maxdepth = np.inf
maxlinks = 50
constraintlabels = True

def __init__(self, solution, constraintset, leftlabel=None):
self.csenss = solution["sensitivities"]["constraints"]
Expand All @@ -37,18 +40,19 @@ def __init__(self, solution, constraintset, leftlabel=None):
self.links_to_target = defaultdict(int)
self.nodes = {}

def add_node(self, target, title, type):
def add_node(self, target, title, tag):
"adds nodes of a given target, title, and tag to self.nodes"
self.links_to_target[target] += 1
node = "%s.%04i" % (target, self.links_to_target[target])
self.nodes[node] = {"id": node, "title": title, type: True}
self.nodes[node] = {"id": node, "title": title, tag: True}
return node

# pylint: disable=too-many-branches
def link(self, cset, target, var, depth=0, subarray=False, named=False):
def link(self, cset, target, var, depth=0, subarray=False, labeled=False):
"adds links of a given constraint set to self.links"
total_sens = 0
switchedtarget = False
if not named and isnamedmodel(cset) and 0 < depth <= self.maxdepth:
if not labeled and isnamedmodel(cset) and 0 < depth <= self.maxdepth:
switchedtarget = target
target = self.add_node(target, cset.lineage[-1][0], "namedmodel")
depth += 1
Expand All @@ -65,12 +69,13 @@ def link(self, cset, target, var, depth=0, subarray=False, named=False):
cset = {k: cset[i] for k, i in cset.idxlookup.items()}
if isinstance(cset, dict):
for label, c in cset.items():
if depth > self.maxdepth:
subtotal_sens = self.link(c, target, var, depth, subarray)
if depth > self.maxdepth or not self.constraintlabels:
subtotal_sens = self.link(c, target, var, depth,
subarray, True)
else:
source = self.add_node(target, label, "constraintlabel")
subtotal_sens = self.link(c, source, var, depth+1,
subarray, isnamedmodel(c))
subarray, True)
self.links[source, target] += subtotal_sens
total_sens += subtotal_sens
elif isinstance(cset, Iterable):
Expand All @@ -79,36 +84,40 @@ def link(self, cset, target, var, depth=0, subarray=False, named=False):
total_sens += subtotal_sens
else:
if var is None and cset in self.csenss:
total_sens = -abs(self.csenss[cset])
total_sens = -abs(self.csenss[cset]) or -1e-10
elif var is not None and var.key in cset.v_ss:
total_sens = cset.v_ss[var.key] or 1e-30 # just an epsilon
if subarray:
source = self.add_node(target, "", "subarray")
else:
cstr = cset.str_without(["lineage", "units"])
label = cstr if len(cstr) <= 30 else "%s ..." % cstr[:30]
source = self.add_node(target, label, "constraint")
self.links[source, target] = total_sens
total_sens = cset.v_ss[var.key] or 1e-10 # just an epsilon
if not labeled and depth <= self.maxdepth:
if subarray:
source = self.add_node(target, "", "subarray")
else:
cstr = cset.str_without(["lineage", "units"])
label = cstr if len(cstr) <= 30 else "%s ..." % cstr[:30]
source = self.add_node(target, label, "constraint")
self.links[source, target] = total_sens
if switchedtarget:
self.links[target, switchedtarget] += total_sens
return total_sens

def filterlinks(self, label, function):
def filterlinks(self, label, function, forced=False):
"If over maxlinks, removes links that do not match criteria."
if len(self.links) > self.maxlinks:
if label:
if len(self.links) > self.maxlinks or forced:
if label and not forced:
print("Links exceed link limit (%s > %s); skipping %s."
% (len(self.links), self.maxlinks, label))
self.links = {(s, t): v for (s, t), v in self.links.items()
if function(s, t, v)}

# pylint: disable=too-many-locals
def diagram(self, variable=None, *, maxdepth=None,
width=900, height=400, top=0, bottom=0, left=120, right=120):
def diagram(self, variable=None, *,
top=0, bottom=0, left=120, right=120, width=900, height=400,
maxdepth=None, constraints=True, constraintlabels=True):
"creates links and an ipython widget to show them"
margins = dict(top=top, bottom=bottom, left=left, right=right)
if maxdepth is not None:
self.maxdepth = maxdepth # NOTE: side effects
if constraintlabels is not None:
self.constraintlabels = constraintlabels

total_sens = self.link(self.cset, self.leftlabel, variable)
if variable:
Expand All @@ -122,7 +131,8 @@ def diagram(self, variable=None, *, maxdepth=None,
self.filterlinks("constraints inside arrays",
lambda s, t, v: "subarray" not in self.nodes[s])
self.filterlinks("all constraints",
lambda s, t, v: "constraint" not in self.nodes[s])
lambda s, t, v: "constraint" not in self.nodes[s],
forced=constraints)
self.filterlinks("constraint labels",
lambda s, t, v: "constraintlabel" not in self.nodes[s])

Expand All @@ -137,11 +147,13 @@ def diagram(self, variable=None, *, maxdepth=None,
out = SankeyWidget(nodes=nodes, links=links,
margins=margins,
layout=Layout(width=str(width), height=str(height)))

filename = self.leftlabel
if variable:
filename += "_" + variable.key.name
out.auto_save_png(filename + ".png")
# clean up side effects
self.links = defaultdict(float)
self.maxdepth = Sankey.maxdepth
self.constraintlabels = Sankey.constraintlabels
return out

0 comments on commit d2057e6

Please sign in to comment.