diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 69cb7601..00000000 --- a/.codecov.yml +++ /dev/null @@ -1 +0,0 @@ -comment: false diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c912406a..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "julia", - "request": "launch", - "name": "Run active Julia file", - "program": "${file}", - "stopOnEntry": false, - "cwd": "${workspaceFolder}", - "juliaEnv": "${command:activeJuliaEnvironment}" - } - ] -} \ No newline at end of file diff --git a/NEWS.md b/NEWS.md index cd06bdca..e0a722fb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,11 @@ # News for EAGO Releases +## [v0.8.2](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.2) (October 27, 2024) +- Added support for `MOI.ScalarNonlinearFunction`. + - Users can now define all constraints using `@constraint` instead of needing to use `@NLconstraint`. This applies to `@objective` as well. +- Added support for variable names. +- Updated display. + ## [v0.8.1](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.1) (June 15, 2023) - Resolved an issue where integer and binary variables would sometimes throw a `MathOptInterface.UpperBoundAlreadySet` error. diff --git a/Project.toml b/Project.toml index 77730958..2138bc25 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "EAGO" uuid = "bb8be931-2a91-5aca-9f87-79e1cb69959a" authors = ["Matthew Wilhelm , Robert Gottlieb , Dimitri Alston , and Matthew Stuber "] -version = "0.8.1" +version = "0.8.2" [deps] Cassette = "7057c7e9-c182-5462-911a-8362d720325c" diff --git a/README.md b/README.md index dcafb478..9446ef85 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,12 @@ The EAGO package has numerous features: a solver accessible from JuMP/MathOptInt ## Recent News +### [v0.8.2](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.2) (October 27, 2024) +- Added support for `MOI.ScalarNonlinearFunction`. + - Users can now define all constraints using `@constraint` instead of needing to use `@NLconstraint`. This applies to `@objective` as well. +- Added support for variable names. +- Updated display. + ### [v0.8.1](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.1) (June 15, 2023) - Resolved an issue where integer and binary variables would sometimes throw a `MathOptInterface.UpperBoundAlreadySet` error. diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index ee59d2da..00000000 --- a/REQUIRE +++ /dev/null @@ -1,12 +0,0 @@ -julia 0.7 -JuMP 0.19.0 -MathOptInterface 0.8.1 -IntervalArithmetic 0.15+ -IntervalContractors 0.3+ -StaticArrays 0.8.3 -DiffRules 0.0.5+ -Ipopt 0.5.1 -Reexport 0.2.0 -GLPK 0.9.1 -Calculus 0.4.1+ -Cassette 0.2.1 diff --git a/docs/src/index.md b/docs/src/index.md index 9c67386c..d6d1196d 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -11,7 +11,7 @@ A development environment for robust and global optimization in Julia. - Current Position: Alexion Pharmaceuticals - [Robert Gottlieb](https://psor.uconn.edu/person/robert-gottlieb/), Department of Chemical and Biomolecular Engineering, University of Connecticut (UConn) - [Dimitri Alston](https://psor.uconn.edu/person/dimitri-alston/), Department of Chemical and Biomolecular Engineering, University of Connecticut (UConn) -- [Matthew Stuber](https://chemical-biomolecular.engr.uconn.edu/people/faculty/stuber-matthew/), Associate Professor, University of Connecticut (UConn) +- [Matthew Stuber](https://chemical-biomolecular.engr.uconn.edu/people/faculty/stuber-matthew/), Pratt & Whitney Associate Professor in Advanced Systems Engineering, University of Connecticut (UConn) If you would like to contribute, [contact us](@ref "How to Contribute to EAGO"). diff --git a/docs/src/news.md b/docs/src/news.md index a26fcfd0..cad9153d 100644 --- a/docs/src/news.md +++ b/docs/src/news.md @@ -1,5 +1,11 @@ # News for EAGO Releases +## [v0.8.2](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.2) (October 27, 2024) +- Added support for `MOI.ScalarNonlinearFunction`. + - Users can now define all constraints using `@constraint` instead of needing to use `@NLconstraint`. This applies to `@objective` as well. +- Added support for variable names. +- Updated display. + ## [v0.8.1](https://github.com/PSORLab/EAGO.jl/releases/tag/v0.8.1) (June 15, 2023) - Resolved an issue where integer and binary variables would sometimes throw a `MathOptInterface.UpperBoundAlreadySet` error. diff --git a/src/eago_optimizer/optimize/nonconvex/configure_subsolver.jl b/src/eago_optimizer/optimize/nonconvex/configure_subsolver.jl index ea3380d4..7537b798 100644 --- a/src/eago_optimizer/optimize/nonconvex/configure_subsolver.jl +++ b/src/eago_optimizer/optimize/nonconvex/configure_subsolver.jl @@ -14,15 +14,16 @@ function set_default_config_udf!(s, m::MOI.AbstractOptimizer, verbosity::Int) if verbosity > 0 - println("EAGO lacks a specialized configuration routine for the subsolver ($(MOI.get(m, MOI.SolverName())))") - println("you selected. As a result, EAGO cannot set the subsolver tolerances based on the") - println("absolute_tolerance, relative_tolerance, and absolute_constraint_feas_tolerance") - println("parameters passed to the EAGO optimizer. Consequently, need to ensure that the tolerances") - println("set in the provided subsolver are appropriate (for instance if the absolute_tolerance = 1E-3") - println("then the absolute tolerance for a subsolver should be < 1E-4 and any feasibility tolerances") - println("should be as conservative as the absolute_constraint_feas_tolerance). If you see this message") - println("please submit an issue at https://github.com/PSORLab/EAGO.jl/issues/new/choose requesting") - println("that a configuration routine be added for this subsolver.") + @warn(""" + EAGO lacks a specialized configuration routine for the subsolver ($(MOI.get(m, MOI.SolverName()))) + you selected. As a result, EAGO cannot set the subsolver tolerances based on the + absolute_tolerance, relative_tolerance, and absolute_constraint_feas_tolerance + parameters passed to the EAGO optimizer. Consequently, you need to ensure that the tolerances + set in the provided subsolver are appropriate (for instance if the absolute_tolerance = 1E-3 + then the absolute tolerance for a subsolver should be < 1E-4 and any feasibility tolerances + should be as conservative as the absolute_constraint_feas_tolerance). If you see this message + please submit an issue at https://github.com/PSORLab/EAGO.jl/issues/new/choose requesting + that a configuration routine be added for this subsolver.""") end return end diff --git a/src/eago_optimizer/optimize/nonconvex/display.jl b/src/eago_optimizer/optimize/nonconvex/display.jl index 7497fd3d..d4fb8588 100644 --- a/src/eago_optimizer/optimize/nonconvex/display.jl +++ b/src/eago_optimizer/optimize/nonconvex/display.jl @@ -29,7 +29,7 @@ function print_solution!(m::GlobalOptimizer) elseif m._end_state == GS_NODE_LIMIT println("Node Limit Exceeded") elseif m._end_state == GS_ITERATION_LIMIT - println("Maximum Iteration Exceeded") + println("Iteration Limit Exceeded") elseif m._end_state == GS_RELATIVE_TOL println("Relative Tolerance Achieved") elseif m._end_state == GS_ABSOLUTE_TOL @@ -39,24 +39,14 @@ function print_solution!(m::GlobalOptimizer) end if m._end_state == GS_OPTIMAL || m._end_state == GS_RELATIVE_TOL || m._end_state == GS_ABSOLUTE_TOL println("Optimal Solution Found at Node $(m._solution_node)") - if !_is_input_min(m) - println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") - println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") - else - println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") - println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") - end + println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") + println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") elseif m._end_state == GS_INFEASIBLE println("No Solution Found") else println("Best Solution Found at Node $(m._solution_node)") - if !_is_input_min(m) - println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") - println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") - else - println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") - println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") - end + println("Lower Bound: $(MOI.get(m, MOI.ObjectiveBound()))") + println("Upper Bound: $(MOI.get(m, MOI.ObjectiveValue()))") end if m._feasible_solution_found println("Solution:") @@ -68,7 +58,7 @@ function print_solution!(m::GlobalOptimizer) addlen = maxlen .- length.(variable_names) print_list = " ".^addlen.*variable_names for i = 1:m._input_problem._variable_count - println(" $(print_list[i]) = $(m._continuous_solution[i])") + println(" $(print_list[i]) = $(m._continuous_solution[i])") end end println(" ") @@ -106,22 +96,22 @@ Print status information based on iteration count. The header print frequency is based on the `header_iterations` setting, and the data print frequency is based on the `output_iterations` setting. """ -function print_iteration!(m::GlobalOptimizer) +function print_iteration!(m::GlobalOptimizer, end_flag::Bool) if _verbosity(m) > 0 # Print header line every `header_iterations` times and print iteration summary every `output_iterations` times - if mod(m._iteration_count, m._parameters.output_iterations) === 0 + if m._last_printed_iteration != m._iteration_count && (mod(m._iteration_count, m._parameters.output_iterations) === 0 || end_flag) if m._iteration_count == m._parameters.output_iterations || mod(m._iteration_count, m._parameters.header_iterations) < m._parameters.output_iterations println("---------------------------------------------------------------------------------------------------------------------------------") println("| Iteration # | Nodes | Lower Bound | Upper Bound | Gap | Ratio | Timer | Time Left |") println("---------------------------------------------------------------------------------------------------------------------------------") end # Print start - print_str = "| " + print_str = "| " # Print iteration number - max_len = 12 + max_len = 13 temp_str = string(m._iteration_count) len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" | " @@ -132,24 +122,15 @@ function print_iteration!(m::GlobalOptimizer) len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" | " - # Determine lower and upper bound - if _is_input_min(m) - lower = m._global_lower_bound - upper = m._global_upper_bound - else - lower = m._global_lower_bound - upper = m._global_upper_bound - end - # Print lower bound max_len = 13 - temp_str = @sprintf "%.3E" lower + temp_str = @sprintf "%.3E" m._global_lower_bound len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" | " # Print upper bound max_len = 13 - temp_str = @sprintf "%.3E" upper + temp_str = @sprintf "%.3E" m._global_upper_bound len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" | " @@ -167,17 +148,23 @@ function print_iteration!(m::GlobalOptimizer) # Print run time max_len = 13 - temp_str = @sprintf "%.3E" m._run_time + temp_str = @sprintf "%.2F" m._run_time len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" | " # Print time remaining max_len = 13 - temp_str = @sprintf "%.3E" m._time_left + temp_str = @sprintf "%.2F" m._time_left len_str = length(temp_str) print_str *= (" "^(max_len - len_str))*temp_str*" |" println(print_str) + + # Update printed iteration + m._last_printed_iteration = m._iteration_count + end + if end_flag + println("---------------------------------------------------------------------------------------------------------------------------------") end end @@ -225,8 +212,8 @@ end $(FUNCTIONNAME) Print noteworthy information prior to running branch-and-bound. Currently prints -a note about flipping `max(f)` to `-min(-f)` internally, if a maximization problem -is inputted and `verbosity>=3`. +a note about flipping `max(f)` to `-min(-f)` internally, if the input is a +maximization problem and `verbosity>=3`. """ function print_preamble!(m::GlobalOptimizer) if _verbosity(m) >= 3 diff --git a/src/eago_optimizer/optimize/optimize_nonconvex.jl b/src/eago_optimizer/optimize/optimize_nonconvex.jl index 56abc6b5..a3a70874 100644 --- a/src/eago_optimizer/optimize/optimize_nonconvex.jl +++ b/src/eago_optimizer/optimize/optimize_nonconvex.jl @@ -372,8 +372,7 @@ Pseudocode description of the algorithm, as implemented here: """ function global_solve!(m::GlobalOptimizer) - # Set counts to 1 - m._iteration_count = 1 + # Set initial node count m._node_count = 1 # Prepare to run branch-and-bound @@ -384,6 +383,8 @@ function global_solve!(m::GlobalOptimizer) # Run branch and bound; terminate when the stack is empty or when some # tolerance or limit is hit while !termination_check(m) + # Update iteration counter + m._iteration_count += 1 # Fathom nodes from the stack, then pick a node and temporarily remove # it from the stack @@ -440,10 +441,9 @@ function global_solve!(m::GlobalOptimizer) m._run_time = time() - m._start_time m._time_left = m._parameters.time_limit - m._run_time - # Log and print information as needed and update the iteration counter + # Log and print information as needed log_iteration!(m) - print_iteration!(m) - m._iteration_count += 1 + print_iteration!(m, false) end # Since the algorithm has terminated, convert EAGO's end status into @@ -452,6 +452,7 @@ function global_solve!(m::GlobalOptimizer) set_result_status!(m) # Print final information about the solution + print_iteration!(m, true) print_solution!(m) end diff --git a/src/eago_optimizer/types/global_optimizer.jl b/src/eago_optimizer/types/global_optimizer.jl index 6cdcbf3f..c5c4eb6e 100644 --- a/src/eago_optimizer/types/global_optimizer.jl +++ b/src/eago_optimizer/types/global_optimizer.jl @@ -706,6 +706,8 @@ Base.@kwdef mutable struct GlobalOptimizer{Q,S,T<:ExtensionType} <: MOI.Abstract _last_upper_problem_time::Float64 = 0.0 "Updated each iteration to track the time of the postprocess step" _last_postprocessing_time::Float64 = 0.0 + "Updated each time an iteration is printed" + _last_printed_iteration::Int = 0 # Reset in initial_parse! in parse.jl "A field to track convergence progress across iterations" diff --git a/test/optimizer.jl b/test/optimizer.jl index 1b5b672e..461acb97 100644 --- a/test/optimizer.jl +++ b/test/optimizer.jl @@ -716,7 +716,8 @@ end end @test_nowarn EAGO.print_results!(m, true) @test_nowarn EAGO.print_results!(m, false) - @test_nowarn EAGO.print_iteration!(m) + @test_nowarn EAGO.print_iteration!(m, false) + @test_nowarn EAGO.print_iteration!(m, true) @test_nowarn EAGO.print_node!(m) @test_nowarn EAGO.print_problem_summary!(d, "Display Test") end \ No newline at end of file