Conversation
|
Added a bug fix, xarray copies need to be done with
|
brynpickering
left a comment
There was a problem hiding this comment.
@bobbyxng couple of test suite simplifications suggested + an open question on naming / harmonising with Python convention for copy methods.
I would also add tests for the "deep" part of your copy, i.e. you should update some data in c in the variables, constraints, and expressions, then assert that they haven't changed in m.
You're also missing solver_name and solver_model from the copied Model object. solver_model isn't an easy one to copy consistently (e.g. the gurobi model has a copy method, but the highs model doesn't) and I guess there's no benefit to copying it across as the next solve would delete and recreate it anyway?
linopy/io.py
Outdated
| return m | ||
|
|
||
|
|
||
| def copy(src: Model, include_solution: bool = False) -> Model: |
There was a problem hiding this comment.
Should this be called deep_copy / deepcopy to align with Python convention that copy refers to a shallow copy?
Or, you provide a deep arg, returning copy.copy(self) if they set deep=False.
There was a problem hiding this comment.
You should also map your deep copy function to __deepcopy__ so that copy.deepcopy(m) will return the same result. It allows other classes which have a linopy model as an attribute to get a deepcopy of that attribute back on request.
See the xarray dataset copy methods for an example.
There was a problem hiding this comment.
Personally I prefer going copy() with the deep arg, which is what I have now implemented. Currently Model.copy(deep: bool=True) is the main API, deep copy by default, shallow copy when deep=False
__copy__is mapped to conventional shallow copy behaviour__deepcopy__is mapped to conventional deep copy behaviour. So python'scopy.deepcopy(m)returns the same kind of result asm.copy(deep=True)(default).- A potential confusion might be that
m.copy()by default hasdeep=True, but personally I believe this addresses more common workflows, where you'd want a copy of a linopy model to be independent. Open for discussion thouh @brynpickering @FabianHofmann
…k for deep copy independence.
|
Thanks @brynpickering, this was very helpful feedback, I learned a lot in this process. I have implemented your suggestions and aligned copy behaviour with python's copy/deepcopy conventions.
I also extended the tests, including more detiled deep copy coverage to mutate data in the copied models across all objects (vars, constraints, objective), also grouped solved copy testing under a dedicated class with a single skip marker. Regarding the solver meta data:
|
Changes proposed in this Pull Request
Adds
Model.copy(), a method to create a deep copy of a linopy model.include_solution=False) the copy contains only the problem structure (variables, constraints, objective expression, parameters, blocks, and counters), and is returned in an initialized state.include_solution=Trueadditionally copies solution and dual values, solve status, and objective value.testing/test_model.pyThe motivation behind this was an automated LP dualiser (following in a future PR) which requires building on the primal model to build the dual (without modifying the original primal).
Checklist
doc.doc/release_notes.rstof the upcoming release is included.