Skip to content

Commit

Permalink
RefExplorer: view/save ref tree as JSON #232
Browse files Browse the repository at this point in the history
  • Loading branch information
glebbelov committed Apr 4, 2024
1 parent 9084944 commit 266b705
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 37 deletions.
53 changes: 36 additions & 17 deletions support/modelexplore/modelexplore.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import streamlit as st
import json

from scripts.python.modelreader import ReadExplorerModel
from scripts.python.matcher import MatchSubmodel
Expand All @@ -19,10 +20,15 @@

fwd = st.sidebar.checkbox(
'Add descendants', disabled=True,
help = 'Include all solver model items derived from matching items in the NL model')
help = 'When searching, include all solver model items derived from matching items in the NL model')
bwd = st.sidebar.checkbox(
'Add ancestors', disabled=True,
help = 'Include all NL model items reduced to matching items in the solver model')
help = 'When searching, Include all NL/intermediate model items reduced to matching items in the solver model')

nl_ref_tree = st.sidebar.radio(
"**NL model presentation mode:**",
["Text", "Reformulation tree :sparkles:"])
reftree = "Text"!=nl_ref_tree

left_column, right_column = st.columns(2)

Expand All @@ -34,21 +40,28 @@ def ReadModel(uploader):

# Cache the matching function?
# @st.cache_data Need cacheable Model.
def MatchSelection(m, srch, fwd, bwd):
return MatchSubmodel(m, srch, fwd, bwd)
def MatchSelection(m, srch, fwd, bwd, reftree):
return MatchSubmodel(m, srch, fwd, bwd, reftree)

# Write dictionary of entries
@st.cache_data
def WriteDict(d):
whole = ""
def WriteDict(d, reftree=False):
whole = "" if not reftree else {} ## dict/json: add only non-empty sections
for k, v in d.items():
if len(v):
whole = whole + '\n\n## ' + k + ' (' + str(v.count('\n')) + ')\n'
whole = whole + v
with st.expander("""### """ + k + ' (' + \
str(v.count('\n')) + ')'):
nv = v.count('\n') if not reftree else len(v)
if nv:
k1 = k + ' (' + str(nv) + ')'
if reftree:
whole[k1] = v
else:
whole = whole + '\n\n## ' + k1 + '\n'
whole = whole + v
with st.expander("""### """ + k1):
with st.container(height=200):
st.code(v, language='ampl')
if reftree:
st.json(v)
else:
st.code(v, language='ampl')
return whole


Expand All @@ -60,13 +73,21 @@ def WriteDict(d):
if uploader is not None:
model = ReadModel(uploader)
filename_upl = uploader.name
subm1, subm2 = MatchSelection(model, srch, fwd, bwd)
subm1, subm2 = MatchSelection(model, srch, fwd, bwd, reftree)
bytes1_data = subm1.GetData()
bytes2_data = subm2.GetData()
with left_column:
st.header("NL model",
help = 'NL model lines matching the search pattern')
modelNL = WriteDict(bytes1_data)
st.write("Display mode: **" + nl_ref_tree + "**")
modelNLTitle = "NL Model for '" + filename_upl + \
"' (search pattern: '" + srch + "')"
if reftree:
modelNL = WriteDict(bytes1_data, reftree)
modelNL["title"] = modelNLTitle
modelNL = json.dumps(modelNL, indent=2)
else:
modelNL = modelNLTitle + WriteDict(bytes1_data, reftree)
with right_column:
st.header("Solver model",
help = 'Solver model lines matching the search pattern')
Expand All @@ -79,10 +100,8 @@ def WriteDict(d):


st.sidebar.download_button("Download NL Model",
"# NL Model for '" + filename_upl + \
"' (search pattern: '" + srch + "')\n" + \
modelNL,
filename_upl + '_NL.mod',
filename_upl + ('_NL.mod' if not reftree else '_NL.json'),
help = 'Download current NL model',
disabled = ("" == modelNL))
st.sidebar.download_button("Download Solver Model",
Expand Down
3 changes: 3 additions & 0 deletions support/modelexplore/scripts/python/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ def GetNode(self, idx):
def AddLink(self, s, d):
self._succ[s].append(d) ## Should have no duplicates

def GetSucc(self, n):
return self._succ[n]

def ToText(self):
return str(self._nodes)
4 changes: 2 additions & 2 deletions support/modelexplore/scripts/python/matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ def __init__(self):
self.data = None


def MatchSubmodel(m: Model, patt: str, fwd: bool, bwd: bool):
def MatchSubmodel(m: Model, patt: str, fwd: bool, bwd: bool, reftree: bool):
"""
Match a submodel containg the \a pattern,
optionally extended by forward/backward
reformulation graph search
"""
mv1 = ModelView()
mv2 = ModelView()
mv1.SetData(m.MatchOrigModel(patt))
mv1.SetData(m.MatchOrigModel(patt, reftree))
mv2.SetData(m.MatchFinalModel(patt))
return mv1, mv2
51 changes: 33 additions & 18 deletions support/modelexplore/scripts/python/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,23 +143,23 @@ def GetLinkNode(self, type, index):


# Match keyword to the original model
def MatchOrigModel(self, keyw):
def MatchOrigModel(self, keyw, reftree):
result = {}
result["NL Variables"] = self._matchRecords(self._vars, keyw, "is_from_nl")
result["NL Defined Variables"] = self._matchRecords(self._dvars, keyw)
result["NL Objectives"] = self._matchRecords(self._objs_NL, keyw)
result["NL Variables"] = self._matchRecords(self._vars, keyw, reftree, "is_from_nl")
result["NL Defined Variables"] = self._matchRecords(self._dvars, keyw, reftree)
result["NL Objectives"] = self._matchRecords(self._objs_NL, keyw, reftree)
# result["NL Constraints"] \
# = self._matchRecords(self._cons_NL.get("All"), keyw)
result["NL Nonlinear Constraints"] \
= self._matchRecords(self._cons_NL.get("Nonlinear"), keyw)
= self._matchRecords(self._cons_NL.get("Nonlinear"), keyw, reftree)
result["NL Linear Constraints"] \
= self._matchRecords(self._cons_NL.get("Linear"), keyw)
= self._matchRecords(self._cons_NL.get("Linear"), keyw, reftree)
result["NL Logical Constraints"] \
= self._matchRecords(self._cons_NL.get("Logical"), keyw)
= self._matchRecords(self._cons_NL.get("Logical"), keyw, reftree)
result["NL SOS1 Constraints"] \
= self._matchRecords(self._cons_NL.get("SOS1"), keyw)
= self._matchRecords(self._cons_NL.get("SOS1"), keyw, reftree)
result["NL SOS2 Constraints"] \
= self._matchRecords(self._cons_NL.get("SOS2"), keyw)
= self._matchRecords(self._cons_NL.get("SOS2"), keyw, reftree)
return result

# Match keyword to the final model
Expand All @@ -176,20 +176,35 @@ def MatchFinalModel(self, keyw):

# Add records containing keyword
# @return array of strings
def _matchRecords(self, cnt, keyw, keyNeed1=None):
result = ""
def _matchRecords(self, cnt, keyw, reftree=False, keyNeed1=None):
result = "" if not reftree else {}
if cnt is None:
return result
for i in cnt:
if "final" not in i or 1==i["final"]:
pr = str(i) ## TODO printed form
if "printed" in i:
pr = i["printed"]
assert len(pr)
if ';'!=pr[-1]:
pr = pr + ';'
pr = self.StringifyNode(i)
if (""==keyw or keyw in pr) \
and (keyNeed1==None \
or (keyNeed1 in i and 1==i[keyNeed1])):
result = result + " \n" + pr ## Markdown: 2x spaces + EOL
if reftree:
result[pr] = self._getSubtree(i) ## dict
else:
result = result + " \n" + pr ## Markdown: 2x spaces + EOL
return result

def StringifyNode(self, i):
pr = str(i)
if "printed" in i:
pr = i["printed"]
assert len(pr)
if ';'!=pr[-1]:
pr = pr + ';'
return pr

## Refomrulation descendants
def _getSubtree(self, node):
result = {}
for ic in self._graph.GetSucc(node["node_index"]):
c = self._graph.GetNode(ic)
result[self.StringifyNode(c)] = self._getSubtree(c)
return result

0 comments on commit 266b705

Please sign in to comment.