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

add SolSavingEnvironment, halving pickle size #1503

Merged
merged 1 commit into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 3 additions & 10 deletions gpkit/nomials/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ def mono_approximation(self, x0):

class ScalarSingleEquationConstraint(SingleEquationConstraint):
"A SingleEquationConstraint with scalar left and right sides."
nomials = []
generated_by = v_ss = parent = None
bounded = meq_bounded = {}

Expand Down Expand Up @@ -409,15 +408,13 @@ class PosynomialInequality(ScalarSingleEquationConstraint):
def __init__(self, left, oper, right):
ScalarSingleEquationConstraint.__init__(self, left, oper, right)
if self.oper == "<=":
self.p_lt, self.m_gt = self.left, self.right
p_lt, m_gt = self.left, self.right
elif self.oper == ">=":
self.m_gt, self.p_lt = self.left, self.right
m_gt, p_lt = self.left, self.right
else:
raise ValueError("operator %s is not supported." % self.oper)

self.unsubbed = self._gen_unsubbed(self.p_lt, self.m_gt)
self.nomials = [self.left, self.right, self.p_lt, self.m_gt]
self.nomials.extend(self.unsubbed)
self.unsubbed = self._gen_unsubbed(p_lt, m_gt)
self.bounded = set()
for p in self.unsubbed:
for exp in p.hmap:
Expand Down Expand Up @@ -513,8 +510,6 @@ def __init__(self, left, right):
# pylint: disable=super-init-not-called,non-parent-init-called
ScalarSingleEquationConstraint.__init__(self, left, self.oper, right)
self.unsubbed = self._gen_unsubbed(self.left, self.right)
self.nomials = [self.left, self.right]
self.nomials.extend(self.unsubbed)
self.bounded = set()
self.meq_bounded = {}
self._las = []
Expand Down Expand Up @@ -576,9 +571,7 @@ def __init__(self, left, oper, right):
pgt, plt = self.left, self.right
else:
raise ValueError("operator %s is not supported." % self.oper)
self.nomials = [self.left, self.right]
self.unsubbed = [plt - pgt]
self.nomials.extend(self.unsubbed)
self.bounded = self.as_gpconstr({}).bounded

def as_hmapslt1(self, substitutions):
Expand Down
41 changes: 31 additions & 10 deletions gpkit/solution_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@
]


class SolSavingEnvironment:
"""Temporarily removes construction/solve attributes from constraints.

This approximately halves the size of the pickled solution.
"""

def __init__(self, solarray):
self.solarray = solarray
self.attrstore = {}

def __enter__(self):
for constraint_attr in ["mfm", "pmap", "bounded", "meq_bounded",
"v_ss", "unsubbed", "varkeys"]:
store = {}
for constraint in self.solarray["sensitivities"]["constraints"]:
if getattr(constraint, constraint_attr, None):
store[constraint] = getattr(constraint, constraint_attr)
delattr(constraint, constraint_attr)
self.attrstore[constraint_attr] = store

def __exit__(self, type_, val, traceback):
for constraint_attr, store in self.attrstore.items():
for constraint, value in store.items():
setattr(constraint, constraint_attr, value)


def senss_table(data, showvars=(), title="Variable Sensitivities", **kwargs):
"Returns sensitivity table lines"
if "variables" in data.get("sensitivities", {}):
Expand Down Expand Up @@ -411,28 +437,23 @@ def diff(self, other, showvars=None, *,
def save(self, filename="solution.pkl", **pickleargs):
"""Pickles the solution and saves it to a file.

The saved solution is identical except for two things:
- the cost is made unitless
- the solution's 'program' attribute is removed
- the solution's 'model' attribute is removed
- the data field is removed from the solution's warnings
(the "message" field is preserved)

Solution can then be loaded with e.g.:
>>> import pickle
>>> pickle.load(open("solution.pkl"))
"""
pickle.dump(self, open(filename, "wb"), **pickleargs)
with SolSavingEnvironment(self):
pickle.dump(self, open(filename, "wb"), **pickleargs)

def save_compressed(self, filename="solution.pgz", **cpickleargs):
"Pickle a file and then compress it into a file with extension."
with gzip.open(filename, "wb") as f:
pickled = pickle.dumps(self, **cpickleargs)
with SolSavingEnvironment(self):
pickled = pickle.dumps(self, **cpickleargs)
f.write(pickletools.optimize(pickled))

@staticmethod
def decompress_file(file):
"Load any compressed pickle file"
"Load a gzip-compressed pickle file"
with gzip.open(file, "rb") as f:
return pickle.Unpickler(f).load()

Expand Down