# \[JuMP + Gurobi Bug?\] value of variable in solved model changes after deleting another variable

**URL:** <https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, bug\
**Created:** [October 7, 2019, 8:31pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619 "2019-10-07T20:31:41Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 7, 2019, 8:31pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/1 "2019-10-07T20:31:41Z")

</div>

```julia
> using JuMP, Gurobi
> model = Model(with_optimizer(Gurobi.Optimizer));
> @variable(model, x[1:3], Bin);
> @constraint(model, sum(all_variables(model)) <= 2);
> @objective(model, Max, sum(model[:x] .* [10, 5, 2]));
> optimize!(model);
> value.(x)
3-element Array{Float64,1}:
 1.0
 1.0
 0.0
> is_valid.(model, x)
3-element BitArray{1}:
 true
 true
 true
> delete(model, x[2]);
> is_valid.(model, x)
3-element BitArray{1}:
  true
 false
  true
> value(x[1])
1.0
> value(x[3])
1.0

```

In other words, if I delete a variable from an already solved model (x[2] in this case), I change the value of another (x[3] from zero to one).

If I use GLPK as the solver then the behaviour is the one I expected, this is, the variables I did not delete keep their values unchanged.

I do not know if querying the values of variables from a solved-and-then-modified model is undefined behaviour, or one of the implementations (GLPK layer or Gurobi layer) is correct and the other is not. So I cannot be sure if this is a bug.

If someone could point me to some official statement on which should happen in such cases, or a developer confirm this is a bug, I would be thankful. I can work around the problem, but makes the code a little uglier and harder to maintain (I cannot use GLPK for my real use case). If it is a bug I would prefer to contribute to fixing it in the source if possible.

I am using:  
[4076af6c] JuMP v0.20.0  
[2e9cd046] Gurobi v0.7.2

If you need any more info just ask.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [October 7, 2019, 9:30pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/2 "2019-10-07T21:30:09Z")

</div>

This is because of Gurobi’s lazy updating semantics. Essentially, when you call `delete`, it caches the deletion so that calls to `value` return the old solution. In this case, JuMP updates the column index of `x[3]`, but Gurobi doesn’t invalidate the stored solution or modify it because of. the lazy update.

In general, querying the solution of a solved-then-modified model is undefined behavior. This should be clarified in the JuMP docs. (A pull request would be most helpful.) I think this had been discussed before; I’ll try to dredge up the discussion.

We can’t support querying of solved-then-modified models because this would require the wrappers to cache the entire solution. On the flip-side, enforcing all solvers to discard the solution is probably an equally large amount of code. Hence the undefined nature.

Edit: [here’s the last time I came across this](https://github.com/odow/SDDP.jl/pull/237#discussion_r314724648), no wonder you didn’t find it 🙂, and all the more reason for JuMP docs.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 8, 2019, 8:11pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/3 "2019-10-08T20:11:15Z")

</div>

Thanks for the fast answer @odow.

I suppose I may deep copy the model, and pay some run time cost, or make my code a little uglier and work around that. I called deepcopy and it seem to work, it has some limitation? it is not guaranteed to be implemented for every solver? (i.e., JuMP model cannot guarantee that it can implement deepcopy?)

I am not sure I would be the best person to make such pull request, I am learning of this now. But if you think its the best you can throw me the git link and some general idea what you want. I will give it a try.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [October 8, 2019, 8:25pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/4 "2019-10-08T20:25:31Z")

</div>

In general, you should _not_ call `deepcopy` on a model. That is because it copies the pointer to the Gurobi C model, rather than copying the underlying C model. Search past discourse issues for a full explanation.

The work-around you should use is to cache the solution yourself:

```nohighlight
model = Model()
@variable(model, x[1:3])
optimize!(model)
x_val = Dict(
    xi => value(xi) for xi in x
)

delete!(model, x[2])
delete!(x_val, x[2])

@show x_val[x[3]]

```

The link to edit the docs is here: [https://github.com/JuliaOpt/JuMP.jl/blob/master/docs/src/solutions.md#obtaining-solutions](https://github.com/JuliaOpt/JuMP.jl/blob/master/docs/src/solutions.md#obtaining-solutions)  
Let me know if you need any help, or JuMP on Gitter: [JuliaOpt/JuMP-dev - Gitter](https://gitter.im/JuliaOpt/JuMP-dev)

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 8, 2019, 9:42pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/5 "2019-10-08T21:42:29Z")

</div>

Oh… (I have now read the previous discourse posts.)

In this case, would not be better to not implement deepcopy at all but  
just copy? Seems counter-intuitive that deepcopy is shallow (maybe it  
is deep for the Julia structure, but semantically it is not really  
deep).  
Other options would be: allow copy while there is not a pointer yet,  
and fail if there is; or, I don’t know, create a new pointer? I  
suppose a new Gurobi C model can be created as the first one was, no?

If you think some of these solutions is reasonable, I can give them a try too.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [October 8, 2019, 10:27pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/6 "2019-10-08T22:27:12Z")

</div>

Copying models is supported: [https://github.com/JuliaOpt/JuMP.jl/blob/04735d2648c5fbcf388b38f7f7d860fb474b772b/src/copy.jl#L38-L139](https://github.com/JuliaOpt/JuMP.jl/blob/04735d2648c5fbcf388b38f7f7d860fb474b772b/src/copy.jl#L38-L139)  
but it looks like it needs to be added to the JuMP docs (an oversight).

`deepcopy`ing is not supported, and will not be supported, because of the issues with the underlying solver.

Given your original post, it’s not obvious why you need to copy the model? Isn’t the work-around of caching the solution sufficient?

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 8, 2019, 10:42pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/7 "2019-10-08T22:42:17Z")

</div>

Oh, no, I can work around it. I am thinking of people that will end up  
with the same problem in the future. If there is no plan to support  
deepcopy why do not specialize deepcopy to show an error message and  
abort?

As it is just a stub, I can write it and make a pull request for it  
too. I suppose that the better is to specialize it for the concrete  
class and not the AbstractModel, as someone may want to implement a  
model that supports deepcopy.

I will submit a change to the documentation soon. I just don’t know if  
it is not kinda long considering the size of the issue. Feel free to  
say to me rewrite it.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [October 8, 2019, 10:54pm UTC](https://discourse.julialang.org/t/jump-gurobi-bug-value-of-variable-in-solved-model-changes-after-deleting-another-variable/29619/8 "2019-10-08T22:54:39Z")

</div>

> why do not specialize deepcopy to show an error message and  
> abort?

Sure! Make a pull request implementing `Base.deepcopy(::AbstractModel)`

> I just don’t know if  
> it is not kinda long considering the size of the issue.

It just needs to be

```Julia
!!! warn
    Accessing the solution of a model after modification (e.g., adding, 
    deleting, or modifying a constraint) is undefined behavior.

```
