Update optimization model by changing variables to coefficients

I took a closer look. Here is your original code (which works)

using JuMP, Gurobi
begin
    model = Model(Gurobi.Optimizer)
    set_silent(model)
    @variable(model, x, Bin)
    @variable(model, y)
    @constraint(model, x * y >= 1)
    @constraint(model, c1, x + y <= 6)
    @objective(model, Max, y)
    optimize!(model) # ๐Ÿ… This is an MIQCP
    undo = fix_discrete_variables(model)
    # ๐Ÿ… At this line, `model` is continuous, convex and quadratic 
    # Therefore, we can proceed with
    set_attribute(model, "QCPDual", 1)
    optimize!(model)
    assert_is_solved_and_feasible(model; dual = true)
    shadow_price(c1)
end

Actually I retained the bounds via 0 <= x <= 1. I relaxed the integerality constraint of x to let it become a continuous QCP, which is easier than it was.

And about the bilinear constraint x * y >= 1, it is a bit subtle.

  1. This constraint is itself nonconvex
  2. However, if we restrict x >= 0 additionally (which is then equivalent to x > 0), then Gurobi can identify it as convex.

Here are 2 related examples who are both convex (and Gurobi can identify)
The first is

begin
    model = Model(Gurobi.Optimizer)
    @variable(model, x >= 0)
    @variable(model, y)
    @constraint(model, x * y >= 1)
    @constraint(model, c1, x + y <= 6)
    @objective(model, Max, y)
    set_attribute(model, "QCPDual", 1)
end
optimize!(model) # convex
assert_is_solved_and_feasible(model; dual = true)

The second is

begin
    model = Model(Gurobi.Optimizer)
    @variable(model, x <= 0)
    @variable(model, y)
    @constraint(model, x * y >= 1)
    @constraint(model, c1, x + y <= 6)
    @objective(model, Max, y)
    set_attribute(model, "QCPDual", 1)
end
optimize!(model) # convex
assert_is_solved_and_feasible(model; dual = true)

An exceptional point for me is that Gurobi can Identify this program as convex quadratic

    model = Model(Gurobi.Optimizer)
    @variable(model, y)
    @variable(model, x)
    set_lower_bound(x, 1)
    set_upper_bound(x, 1)
    @constraint(model, x * y <= 1)
    @objective(model, Max, y)
    optimize!(model)
    JuMP.solution_summary(model; verbose = true)

Although it still fails to directly identify it as a simpler linear program

    @variable(model, y)
    @constraint(model, y <= 1)
    @objective(model, Max, y)

Another exceptional point for me is that Gurobi can even provide the dual variable of non-affine constraint like x * y >= 1, although Iโ€™m unsure if it has a usage in reality.