Are all variable values from the first solution transferred to the second solution?

Yes

I get a different result. In particular, it finds a feasible solution with

Okay, so the difference is very subtle.

In the first case, Gurobi automatically re-uses the previous solution as a starting point. (We don’t tell it to.) But it obviously doesn’t try very hard to repair feasibility, and so it doesn’t produce a new incumbent.

In the second case, you are explicitly telling Gurobi “here is a starting solution that should be close to feasible/optimal,” to it runs a feasibility repair heuristic that attempts to find a solution close to your starting point. That succeeds.

@vasyfa works for Gurobi and might be able to offer more insight.

However,
something doesn’t make sense to me.
Indeed,
with steps 1-4 and without the explicit warm start, if I write

set_start_value(some variables not all,GRB_UNDEFINED)

I obtain

No start values specified in MIP start
MIP start from previous solve did not produce a new incumbent solution

and then a feasible solution with Another try with MIP start.

With step 1-4 and with the explicit warm start, depending on the variables I choose to “reset” with GRB_UNDEFINED, I get different constraints violations.
For example,

User MIP start did not produce a new incumbent solution
User MIP start violates constraint R15282 by 64.749216900
MIP start from previous solve did not produce a new incumbent solution

Note that the violated constraint R15282 is not one of the constraints I declared, but it is a constraint that jump automatically defines with the warm start.

Because it is closed-source, I don’t have any special insight into how Gurobi chooses to handle start values. Let me see if I can get one of their support people to answer your question.

With step 1-4 and with the explicit warm start, depending on the variables I choose to “reset” with GRB_UNDEFINED, I get different constraints violations.
Note that the violated constraint R15282 is not one of the constraints I declared, but it is a constraint that jump automatically defines with the warm start.

This is a bit weird, can you provide an example of this. I cannot reproduce this easily.

Hi Martina, thanks for the example. You are adding new constraints after setting the MIP start, some of which are violated. The constraint names are not read in properly and this leads to the message you are seeing.
Using direct_model when declaring the model seems to help with this.

MyMod = direct_model(Gurobi.Optimizer())

With this, you will see the correct name of the violated constraint in the log.

Can you also tell me something about the case without the explicit warm start, i.e. the case without

var = all_variables(MyMod)
var_solution = value.(var)
set_start_value.(var,var_solution)

in the code?
With set_GRB_UNDEFINED = 1 (reset of the binaries values), I obtain No start values specified in MIP start.
How does the automatic warm start of variables work? Which variables does it choose?
Note that with set_GRB_UNDEFINED = 0, the log does not show violations.

For a MIP start to be considered it has to be at least a partial MIP start, i.e. you need to set the start values of a subset of variables to a value different to GRB_UNDEFINED.
If you provide a partial MIP start, the rest of the solution will be repaired, see Start Attribute.

In your example, if you set set_GRB_UNDEFINED = 1 you will correctly set a partial MIP start as some variables have start values set with

set_start_value.(var,var_solution)

and you reset the start value to GRB_UNDEFINED for a subset of them. This leads to the solution being accepted in your example.

What I meant to say is that I have observed that JuMP automatically does a warm start without me writing set_start_value.(var,var_solution).
The second solve seems to start from an automatic warm start but it is not clear how juMP sets the warm start, which variables considers.

Oh, thanks for the clarification, I see what you mean!

I think, since we now have a direct model, we are re-optimizing the model after changes. By default, this uses the previous solution as a MIP start (along with a few other things, see ATTR format for a complete list).

If you simply call optimize! twice you will see something like: