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 assert_is_solved_and_feasible #3925

Merged
merged 8 commits into from
Jan 28, 2025
Merged
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
20 changes: 10 additions & 10 deletions docs/src/tutorials/algorithms/benders_decomposition.jl
Original file line number Diff line number Diff line change
@@ -166,7 +166,7 @@ set_silent(model)
@constraint(model, [i = 2:n-1], sum(y[i, :]) == sum(y[:, i]))
@objective(model, Min, 0.1 * sum(x) - sum(y[1, :]))
optimize!(model)
Test.@test is_solved_and_feasible(model) #src
assert_is_solved_and_feasible(model) #src
solution_summary(model)

# The optimal objective value is -5.1:
@@ -218,7 +218,7 @@ function solve_subproblem(x_bar)
@constraint(model, [i = 2:n-1], sum(y[i, :]) == sum(y[:, i]))
@objective(model, Min, -sum(y[1, :]))
optimize!(model)
@assert is_solved_and_feasible(model; dual = true)
assert_is_solved_and_feasible(model; dual = true)
return (obj = objective_value(model), y = value.(y), π = reduced_cost.(x))
end

@@ -249,7 +249,7 @@ ABSOLUTE_OPTIMALITY_GAP = 1e-6
println("Iteration Lower Bound Upper Bound Gap")
for k in 1:MAXIMUM_ITERATIONS
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
lower_bound = objective_value(model)
x_k = value.(x)
ret = solve_subproblem(x_k)
@@ -267,7 +267,7 @@ end
# Finally, we can obtain the optimal solution:

optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
x_optimal = value.(x)
optimal_ret = solve_subproblem(x_optimal)
iterative_solution = optimal_flows(optimal_ret.y)
@@ -338,7 +338,7 @@ if !HAS_GUROBI #hide
set_attribute(lazy_model, MOI.LazyConstraintCallback(), nothing) #hide
end #hide
optimize!(lazy_model)
@assert is_solved_and_feasible(lazy_model)
assert_is_solved_and_feasible(lazy_model)

# For this model, the callback algorithm required more solves of the subproblem:

@@ -396,7 +396,7 @@ subproblem
function solve_subproblem(model, x)
fix.(model[:x_copy], x)
optimize!(model)
@assert is_solved_and_feasible(model; dual = true)
assert_is_solved_and_feasible(model; dual = true)
return (
obj = objective_value(model),
y = value.(model[:y]),
@@ -409,7 +409,7 @@ end
println("Iteration Lower Bound Upper Bound Gap")
for k in 1:MAXIMUM_ITERATIONS
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
lower_bound = objective_value(model)
x_k = value.(x)
ret = solve_subproblem(subproblem, x_k)
@@ -427,7 +427,7 @@ end
# Finally, we can obtain the optimal solution:

optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
x_optimal = value.(x)
optimal_ret = solve_subproblem(subproblem, x_optimal)
inplace_solution = optimal_flows(optimal_ret.y)
@@ -504,7 +504,7 @@ end
println("Iteration Lower Bound Upper Bound Gap")
for k in 1:MAXIMUM_ITERATIONS
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
lower_bound = objective_value(model)
x_k = value.(x)
ret = solve_subproblem_with_feasibility(subproblem, x_k)
@@ -528,7 +528,7 @@ end
# Finally, we can obtain the optimal solution:

optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
x_optimal = value.(x)
optimal_ret = solve_subproblem(subproblem, x_optimal)
feasible_inplace_solution = optimal_flows(optimal_ret.y)
10 changes: 5 additions & 5 deletions docs/src/tutorials/algorithms/cutting_stock_column_generation.jl
Original file line number Diff line number Diff line change
@@ -235,7 +235,7 @@ set_silent(model)
@objective(model, Min, sum(x))
@constraint(model, demand[i in 1:I], patterns[i]' * x >= data.pieces[i].d)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
solution_summary(model)

# This solution requires 421 rolls. This solution is sub-optimal because the
@@ -253,7 +253,7 @@ solution_summary(model)

unset_integer.(x)
optimize!(model)
@assert is_solved_and_feasible(model; dual = true)
assert_is_solved_and_feasible(model; dual = true)
π_13 = dual(demand[13])

# Using the economic interpretation of the dual variable, we can say that a one
@@ -284,7 +284,7 @@ function solve_pricing(data::Data, π::Vector{Float64})
@constraint(model, sum(data.pieces[i].w * y[i] for i in 1:I) <= data.W)
@objective(model, Max, sum(π[i] * y[i] for i in 1:I))
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
number_of_rolls_saved = objective_value(model)
if number_of_rolls_saved > 1 + 1e-8
## Benefit of pattern is more than the cost of a new roll plus some
@@ -315,7 +315,7 @@ solve_pricing(data, zeros(I))
while true
## Solve the linear relaxation
optimize!(model)
@assert is_solved_and_feasible(model; dual = true)
assert_is_solved_and_feasible(model; dual = true)
## Obtain a new dual vector
π = dual.(demand)
## Solve the pricing problem
@@ -366,7 +366,7 @@ sum(ceil.(Int, solution.rolls))

set_integer.(x)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
solution = DataFrames.DataFrame([
(pattern = p, rolls = value(x_p)) for (p, x_p) in enumerate(x)
])
4 changes: 2 additions & 2 deletions docs/src/tutorials/algorithms/parallelism.md
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@
end
```
For more information, read
[PSA: Thread-local state is no longer recommended](https://julialang.org/blog/2023/07/PSA-dont-use-threadid/).

Check warning on line 88 in docs/src/tutorials/algorithms/parallelism.md

GitHub Actions / build

[vale] reported by reviewdog 🐶 [Google.Colons] ': T' should be in lowercase. Raw Output: {"message": "[Google.Colons] ': T' should be in lowercase.", "location": {"path": "docs/src/tutorials/algorithms/parallelism.md", "range": {"start": {"line": 88, "column": 9}}}, "severity": "WARNING"}

### Thread safety

@@ -141,7 +141,7 @@
@variable(model, x >= i)
@objective(model, Min, x)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
Threads.lock(my_lock) do
push!(solutions, i => objective_value(model))
end
@@ -357,7 +357,7 @@
@objective(model, Min, x)
set_lower_bound(x, i)
optimize!(model)
@assert is_solved_and_feasible(sudoku)
assert_is_solved_and_feasible(sudoku)
return objective_value(model)
end
end
6 changes: 3 additions & 3 deletions docs/src/tutorials/algorithms/tsp_lazy_constraints.jl
Original file line number Diff line number Diff line change
@@ -214,7 +214,7 @@ if !HAS_GUROBI #hide
end #hide
iterative_model = build_tsp_model(d, n, optimizer)
optimize!(iterative_model)
@assert is_solved_and_feasible(iterative_model)
assert_is_solved_and_feasible(iterative_model)
time_iterated = solve_time(iterative_model)
cycle = subtour(iterative_model[:x])
while 1 < length(cycle) < n
@@ -225,7 +225,7 @@ while 1 < length(cycle) < n
sum(iterative_model[:x][i, j] for (i, j) in S) <= length(cycle) - 1,
)
optimize!(iterative_model)
@assert is_solved_and_feasible(iterative_model)
assert_is_solved_and_feasible(iterative_model)
global time_iterated += solve_time(iterative_model)
global cycle = subtour(iterative_model[:x])
end
@@ -290,7 +290,7 @@ if !HAS_GUROBI #hide
set_attribute(lazy_model, MOI.LazyConstraintCallback(), nothing) #hide
end #hide
optimize!(lazy_model)
@assert is_solved_and_feasible(lazy_model)
assert_is_solved_and_feasible(lazy_model)
objective_value(lazy_model)

#-
8 changes: 4 additions & 4 deletions docs/src/tutorials/applications/optimal_power_flow.jl
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@
# This tutorial takes a matrix-oriented approach focused on network nodes
# that simplifies the construction of semidefinite programs.
# Another approach is to formulate the problem focusing on network lines
# (known as a _branch model_) where it is easier to work with flow
# (known as a _branch model_) where it is easier to work with flow
# constraints. A general approach is provided by
# [PowerModels.jl](https://lanl-ansi.github.io/PowerModels.jl/stable/),
# an open-source framework to a broad range of power flow model formulations
@@ -137,7 +137,7 @@ println("Objective value (basic lower bound) : $basic_lower_bound")

@constraint(model, sum(P_G) >= sum(P_Demand))
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
better_lower_bound = round(objective_value(model); digits = 2)
println("Objective value (better lower bound): $better_lower_bound")

@@ -281,7 +281,7 @@ P_G = real(S_G)
# We're finally ready to solve our nonlinear AC-OPF problem:

optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
Test.@test isapprox(objective_value(model), 3087.84; atol = 1e-2) #src
solution_summary(model)

@@ -420,7 +420,7 @@ optimize!(model)

#-

Test.@test is_solved_and_feasible(model; allow_almost = true)
assert_is_solved_and_feasible(model; allow_almost = true)
sdp_relaxation_lower_bound = round(objective_value(model); digits = 2)
Test.@test isapprox(sdp_relaxation_lower_bound, 2753.04; rtol = 1e-3) #src
println(
6 changes: 3 additions & 3 deletions docs/src/tutorials/applications/power_systems.jl
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ function solve_economic_dispatch(generators::Vector, wind, scenario)
@constraint(model, sum(g[i] for i in 1:N) + w == scenario.demand)
## Solve statement
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
## return the optimal value of the objective function and its minimizers
return (
g = value.(g),
@@ -217,7 +217,7 @@ function solve_economic_dispatch_inplace(
wind.variable_cost * w,
)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
push!(obj_out, objective_value(model))
push!(w_out, value(w))
push!(g1_out, value(g[1]))
@@ -528,7 +528,7 @@ function solve_nonlinear_economic_dispatch(
)
@constraint(model, sum(g[i] for i in 1:N) + sqrt(w) == scenario.demand)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
return (
g = value.(g),
w = value(w),
6 changes: 3 additions & 3 deletions docs/src/tutorials/applications/two_stage_stochastic.jl
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ set_silent(model)
@expression(model, z[ω in Ω], 5y[ω] - 0.1 * (x - y[ω]))
@objective(model, Max, -2x + sum(P[ω] * z[ω] for ω in Ω))
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
solution_summary(model)

# The optimal number of pies to make is:
@@ -159,7 +159,7 @@ function CVaR(Z::Vector{Float64}, P::Vector{Float64}; γ::Float64)
@constraint(model, [i in 1:N], z[i] >= ξ - Z[i])
@objective(model, Max, ξ - 1 / γ * sum(P[i] * z[i] for i in 1:N))
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
return objective_value(model)
end

@@ -218,7 +218,7 @@ set_silent(model)
@constraint(model, [ω in Ω], z[ω] >= ξ - Z[ω])
@objective(model, Max, -2x + ξ - 1 / γ * sum(P[ω] * z[ω] for ω in Ω))
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)

# When ``\gamma = 0.4``, the optimal number of pies to bake is:

6 changes: 3 additions & 3 deletions docs/src/tutorials/conic/arbitrary_precision.jl
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ print(model)
# Let's solve and inspect the solution:

optimize!(model)
@assert is_solved_and_feasible(model; dual = true)
assert_is_solved_and_feasible(model; dual = true)
solution_summary(model)

# The value of each decision variable is a `BigFloat`:
@@ -103,7 +103,7 @@ value.(x) .- [3 // 7, 3 // 14]
set_attribute(model, "tol_gap_abs", 1e-32)
set_attribute(model, "tol_gap_rel", 1e-32)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
value.(x) .- [3 // 7, 3 // 14]

# ## Rational arithmetic
@@ -145,7 +145,7 @@ print(model)
# Let's solve and inspect the solution:

optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
solution_summary(model)

# The optimal values are given in exact rational arithmetic:
8 changes: 4 additions & 4 deletions docs/src/tutorials/conic/dualization.jl
Original file line number Diff line number Diff line change
@@ -133,7 +133,7 @@ print(model_dual)

set_optimizer(model_primal, SCS.Optimizer)
optimize!(model_primal)
@assert is_solved_and_feasible(model_primal; dual = true)
assert_is_solved_and_feasible(model_primal; dual = true)

# (There are five rows in the constraint matrix because SCS expects problems in
# geometric conic form, and so JuMP has reformulated the `X, PSD` variable
@@ -157,7 +157,7 @@ objective_value(model_primal)

set_optimizer(model_dual, SCS.Optimizer)
optimize!(model_dual)
@assert is_solved_and_feasible(model_dual; dual = true)
assert_is_solved_and_feasible(model_dual; dual = true)

# and the solution we obtain is:

@@ -187,7 +187,7 @@ objective_value(model_dual)

set_optimizer(model_primal, Dualization.dual_optimizer(SCS.Optimizer))
optimize!(model_primal)
@assert is_solved_and_feasible(model_primal; dual = true)
assert_is_solved_and_feasible(model_primal; dual = true)

# The performance is the same as if we solved `model_dual`, and the correct
# solution is returned to `X`:
@@ -203,7 +203,7 @@ dual.(primal_c)

set_optimizer(model_dual, Dualization.dual_optimizer(SCS.Optimizer))
optimize!(model_dual)
@assert is_solved_and_feasible(model_dual; dual = true)
assert_is_solved_and_feasible(model_dual; dual = true)

#-

6 changes: 3 additions & 3 deletions docs/src/tutorials/conic/ellipse_approx.jl
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ m, n = size(S)
@constraint(model, [t; vec(Z)] in MOI.RootDetConeSquare(n))
@objective(model, Max, t)
optimize!(model)
Test.@test is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
solution_summary(model)

# ## Results
@@ -212,7 +212,7 @@ f = [1 - S[i, :]' * Z * S[i, :] + 2 * S[i, :]' * z - s for i in 1:m]
## The former @objective(model, Max, t)
@objective(model, Max, 1 * t + 0)
optimize!(model)
Test.@test is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
Test.@test isapprox(D, value.(Z); atol = 1e-3) #src
solve_time_1 = solve_time(model)

@@ -235,7 +235,7 @@ print_active_bridges(model)

remove_bridge(model, MOI.Bridges.Constraint.GeoMeanToPowerBridge)
optimize!(model)
Test.@test is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)

# This time, the solve took:

4 changes: 2 additions & 2 deletions docs/src/tutorials/conic/ellipse_fitting.jl
Original file line number Diff line number Diff line change
@@ -299,7 +299,7 @@ for (i, cluster) in enumerate(clusters)
)
@objective(model, Min, ζ)
optimize!(model)
@assert is_solved_and_feasible(model)
assert_is_solved_and_feasible(model)
Q, d, e = value.(model[:Q]), value.(model[:d]), value.(model[:e])
push!(ellipses_C1, Dict(:Q => Q, :d => d, :e => e))
end
@@ -356,7 +356,7 @@ for (i, cluster) in enumerate(clusters)
)
@objective(model, Min, ζ)
optimize!(model)
@assert is_solved_and_feasible(model; allow_almost = true)
assert_is_solved_and_feasible(model; allow_almost = true)
Q, d, e = value.(model[:Q]), value.(model[:d]), value.(model[:e])
push!(ellipses_C2, Dict(:Q => Q, :d => d, :e => e))
end
Loading
Loading