diff --git a/README.md b/README.md index 009fb7d0..933a25a1 100644 --- a/README.md +++ b/README.md @@ -93,18 +93,19 @@ See the [INSTALL](INSTALL.md) file. At the moment, Uno only reads models from [.nl files](https://en.wikipedia.org/wiki/Nl_(format)). A couple of CUTEst instances are available in the `/examples` directory. -To solve an AMPL model, type in the `build` directory: ```./uno_ampl path_to_file/file.nl``` +To solve an AMPL model, type in the `build` directory: ```./uno_ampl model.nl -AMPL [key=value ...]``` +where ```[key=value ...]``` is a list of options. To use Uno with Julia/JuMP, a solution in the short term is to use the package [AmplNLWriter.jl](https://juliahub.com/ui/Packages/General/AmplNLWriter.jl) to dump JuMP models into .nl files. ### Combination of ingredients -To pick a globalization mechanism, use the argument (choose one of the possible options in brackets): ```-globalization_mechanism [LS|TR]``` -To pick a constraint relaxation strategy, use the argument: ```-constraint_relaxation_strategy [feasibility_restoration|l1_relaxation]``` -To pick a globalization strategy, use the argument: ```-globalization_strategy [l1_merit|fletcher_filter_method|waechter_filter_method|funnel_method]``` -To pick a subproblem method, use the argument: ```-subproblem [QP|LP|primal_dual_interior_point]``` +To pick a globalization mechanism, use the argument (choose one of the possible options in brackets): ```globalization_mechanism=[LS|TR]``` +To pick a constraint relaxation strategy, use the argument: ```constraint_relaxation_strategy=[feasibility_restoration|l1_relaxation]``` +To pick a globalization strategy, use the argument: ```globalization_strategy=[l1_merit|fletcher_filter_method|waechter_filter_method|funnel_method]``` +To pick a subproblem method, use the argument: ```subproblem=[QP|LP|primal_dual_interior_point]``` The options can be combined in the same command line. For an overview of the available strategies, type: ```./uno_ampl --strategies``` -To pick a preset, use the argument: ```-preset [filtersqp|ipopt|byrd]``` +To pick a preset, use the argument: ```preset=[filtersqp|ipopt|byrd]``` diff --git a/bindings/AMPL/uno_ampl.cpp b/bindings/AMPL/uno_ampl.cpp index 66ac1a15..39e9c278 100644 --- a/bindings/AMPL/uno_ampl.cpp +++ b/bindings/AMPL/uno_ampl.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2018-2024 Charlie Vanaret // Licensed under the MIT license. See LICENSE file in the project directory for details. +#include +#include #include "ingredients/globalization_mechanisms/GlobalizationMechanism.hpp" #include "ingredients/globalization_mechanisms/GlobalizationMechanismFactory.hpp" #include "ingredients/constraint_relaxation_strategies/ConstraintRelaxationStrategy.hpp" @@ -51,32 +53,85 @@ namespace uno { ERROR << exception.what() << '\n'; } } + + // argv[0] is ./uno_ampl + // argv[1] is the model name + // argv[2] should be -AMPL + // argv[i] for i = 3..argc-1 are options + void get_command_line_options(Options& options, int argc, char* argv[]) { + static const std::string delimiter = "="; + + // build the (name, value) map + for (int i = 3; i < argc; i++) { + const std::string argument = std::string(argv[i]); + size_t position = argument.find_first_of(delimiter); + if (position == std::string::npos) { + throw std::runtime_error("The option " + argument + " does not contain the delimiter " + delimiter + "."); + } + const std::string key = argument.substr(0, position); + const std::string value = argument.substr(position + 1); + if (key == "preset") { + options.find_preset(value); + } + else { + options[key] = value; + } + } + options.print(false); + } + + void print_uno_instructions() { + std::cout << "Welcome in Uno 1.1.0\n"; + std::cout << "To solve an AMPL model, type ./uno_ampl model.nl -AMPL [key=value ...]\n"; + std::cout << "To choose a constraint relaxation strategy, use the argument constraint_relaxation_strategy=" + "[feasibility_restoration|l1_relaxation]\n"; + std::cout << "To choose a subproblem method, use the argument subproblem=[QP|LP|primal_dual_interior_point]\n"; + std::cout << "To choose a globalization mechanism, use the argument globalization_mechanism=[LS|TR]\n"; + std::cout << "To choose a globalization strategy, use the argument globalization_strategy=" + "[l1_merit|fletcher_filter_method|waechter_filter_method]\n"; + std::cout << "To choose a preset, use the argument preset=[filtersqp|ipopt|byrd]\n"; + std::cout << "The options can be combined in the same command line.\n"; + //std::cout << "Autocompletion is possible (see README).\n"; + } } // namespace int main(int argc, char* argv[]) { using namespace uno; - - if (1 < argc) { - // get the default options - Options options = Options::get_default_options("uno.options"); - // override them with the command line arguments - options.get_command_line_arguments(argc, argv); - Logger::set_logger(options.get_string("logger")); - if (std::string(argv[1]) == "-v") { - Uno::print_uno_version(); + if (argc == 1) { + print_uno_instructions(); + } + else if (argc == 2) { + if (std::string(argv[1]) == "--v") { + print_uno_instructions(); } else if (std::string(argv[1]) == "--strategies") { Uno::print_available_strategies(); } else { - // run Uno on the .nl file (last command line argument) - std::string model_name = std::string(argv[argc - 1]); - run_uno_ampl(model_name, options); + throw std::runtime_error("The second command line argument should be -AMPL."); } } - else { - Uno::print_uno_version(); + else if (argc >= 3) { + // get the default options + Options options = Options::get_default_options("uno.options"); + + // AMPL expects: ./uno_ampl model.nl -AMPL [key=value, ...] + // model name + std::string model_name = std::string(argv[1]); + std::cout << "Model " << model_name << '\n'; + + // -AMPL + if (std::string(argv[2]) != "-AMPL") { + throw std::runtime_error("The second command line argument should be -AMPL."); + } + + // override them with the command line arguments + get_command_line_options(options, argc, argv); + + // solve the model + Logger::set_logger(options.get_string("logger")); + run_uno_ampl(model_name, options); } return EXIT_SUCCESS; } diff --git a/uno/Uno.cpp b/uno/Uno.cpp index 24cc64a0..b54842b4 100644 --- a/uno/Uno.cpp +++ b/uno/Uno.cpp @@ -127,19 +127,6 @@ namespace uno { return result; } - void Uno::print_uno_version() { - std::cout << "Welcome in Uno 1.0\n"; - std::cout << "To solve an AMPL model, type ./uno_ampl path_to_file/file.nl\n"; - std::cout << "To choose a constraint relaxation strategy, use the argument -constraint_relaxation_strategy " - "[feasibility_restoration|l1_relaxation]\n"; - std::cout << "To choose a subproblem method, use the argument -subproblem [QP|LP|primal_dual_interior_point]\n"; - std::cout << "To choose a globalization mechanism, use the argument -globalization_mechanism [LS|TR]\n"; - std::cout << "To choose a globalization strategy, use the argument -globalization_strategy " - "[l1_merit|fletcher_filter_method|waechter_filter_method]\n"; - std::cout << "To choose a preset, use the argument -preset [filtersqp|ipopt|byrd]\n"; - std::cout << "The options can be combined in the same command line. Autocompletion is possible (see README).\n"; - } - void Uno::print_available_strategies() { std::cout << "Available strategies:\n"; std::cout << "- Constraint relaxation strategies: " << join(ConstraintRelaxationStrategyFactory::available_strategies(), ", ") << '\n'; diff --git a/uno/Uno.hpp b/uno/Uno.hpp index f80af1c6..fd100e0e 100644 --- a/uno/Uno.hpp +++ b/uno/Uno.hpp @@ -29,7 +29,6 @@ namespace uno { [[nodiscard]] Result solve(const Model& model, Iterate& initial_iterate, const Options& options); - static void print_uno_version(); static void print_available_strategies(); static void print_strategy_combination(const Options& options); static void print_optimization_summary(const Options& options, const Result& result); diff --git a/uno/ingredients/subproblems/Subproblem.hpp b/uno/ingredients/subproblems/Subproblem.hpp index 9ecbbd05..8ff9ce92 100644 --- a/uno/ingredients/subproblems/Subproblem.hpp +++ b/uno/ingredients/subproblems/Subproblem.hpp @@ -12,7 +12,6 @@ namespace uno { // forward declarations class Direction; - class HessianModel; class Iterate; class l1RelaxedProblem; class Model; diff --git a/uno/tools/Options.cpp b/uno/tools/Options.cpp index 5ea18ba4..71c43513 100644 --- a/uno/tools/Options.cpp +++ b/uno/tools/Options.cpp @@ -5,7 +5,6 @@ #include #include #include "Options.hpp" -#include "Logger.hpp" namespace uno { std::string& Options::operator[](const std::string& key) { @@ -80,128 +79,104 @@ namespace uno { } } - void find_preset(const std::string& preset_name, Options& options) { + void Options::find_preset(const std::string& preset_name) { // shortcuts for state-of-the-art combinations if (preset_name == "ipopt") { - options["constraint_relaxation_strategy"] = "feasibility_restoration"; - options["subproblem"] = "primal_dual_interior_point"; - options["globalization_mechanism"] = "LS"; - options["globalization_strategy"] = "waechter_filter_method"; - options["filter_type"] = "standard"; - options["filter_beta"] = "0.99999"; - options["filter_gamma"] = "1e-8"; - options["switching_delta"] = "1"; - options["filter_ubd"] = "1e4"; - options["filter_fact"] = "1e4"; - options["filter_switching_infeasibility_exponent"] = "1.1"; - options["armijo_decrease_fraction"] = "1e-8"; - options["LS_backtracking_ratio"] = "0.5"; - options["LS_min_step_length"] = "5e-7"; - options["barrier_tau_min"] = "0.99"; - options["barrier_damping_factor"] = "1e-5"; - options["l1_constraint_violation_coefficient"] = "1000."; - options["progress_norm"] = "L1"; - options["residual_norm"] = "INF"; - options["scale_functions"] = "yes"; - options["sparse_format"] = "COO"; - options["tolerance"] = "1e-8"; - options["loose_tolerance"] = "1e-6"; - options["loose_tolerance_consecutive_iteration_threshold"] = "15"; - options["switch_to_optimality_requires_linearized_feasibility"] = "no"; - options["LS_scale_duals_with_step_length"] = "yes"; - options["protect_actual_reduction_against_roundoff"] = "yes"; + (*this)["constraint_relaxation_strategy"] = "feasibility_restoration"; + (*this)["subproblem"] = "primal_dual_interior_point"; + (*this)["globalization_mechanism"] = "LS"; + (*this)["globalization_strategy"] = "waechter_filter_method"; + (*this)["filter_type"] = "standard"; + (*this)["filter_beta"] = "0.99999"; + (*this)["filter_gamma"] = "1e-8"; + (*this)["switching_delta"] = "1"; + (*this)["filter_ubd"] = "1e4"; + (*this)["filter_fact"] = "1e4"; + (*this)["filter_switching_infeasibility_exponent"] = "1.1"; + (*this)["armijo_decrease_fraction"] = "1e-8"; + (*this)["LS_backtracking_ratio"] = "0.5"; + (*this)["LS_min_step_length"] = "5e-7"; + (*this)["barrier_tau_min"] = "0.99"; + (*this)["barrier_damping_factor"] = "1e-5"; + (*this)["l1_constraint_violation_coefficient"] = "1000."; + (*this)["progress_norm"] = "L1"; + (*this)["residual_norm"] = "INF"; + (*this)["scale_functions"] = "yes"; + (*this)["sparse_format"] = "COO"; + (*this)["tolerance"] = "1e-8"; + (*this)["loose_tolerance"] = "1e-6"; + (*this)["loose_tolerance_consecutive_iteration_threshold"] = "15"; + (*this)["switch_to_optimality_requires_linearized_feasibility"] = "no"; + (*this)["LS_scale_duals_with_step_length"] = "yes"; + (*this)["protect_actual_reduction_against_roundoff"] = "yes"; } else if (preset_name == "filtersqp") { - options["constraint_relaxation_strategy"] = "feasibility_restoration"; - options["subproblem"] = "QP"; - options["globalization_mechanism"] = "TR"; - options["globalization_strategy"] = "fletcher_filter_method"; - options["filter_type"] = "standard"; - options["progress_norm"] = "L1"; - options["residual_norm"] = "L2"; - options["sparse_format"] = "CSC"; - options["TR_radius"] = "10"; - options["l1_constraint_violation_coefficient"] = "1."; - options["enforce_linear_constraints"] = "yes"; - options["tolerance"] = "1e-6"; - options["loose_tolerance"] = "1e-6"; - options["TR_min_radius"] = "1e-8"; - options["switch_to_optimality_requires_linearized_feasibility"] = "yes"; - options["protect_actual_reduction_against_roundoff"] = "no"; + (*this)["constraint_relaxation_strategy"] = "feasibility_restoration"; + (*this)["subproblem"] = "QP"; + (*this)["globalization_mechanism"] = "TR"; + (*this)["globalization_strategy"] = "fletcher_filter_method"; + (*this)["filter_type"] = "standard"; + (*this)["progress_norm"] = "L1"; + (*this)["residual_norm"] = "L2"; + (*this)["sparse_format"] = "CSC"; + (*this)["TR_radius"] = "10"; + (*this)["l1_constraint_violation_coefficient"] = "1."; + (*this)["enforce_linear_constraints"] = "yes"; + (*this)["tolerance"] = "1e-6"; + (*this)["loose_tolerance"] = "1e-6"; + (*this)["TR_min_radius"] = "1e-8"; + (*this)["switch_to_optimality_requires_linearized_feasibility"] = "yes"; + (*this)["protect_actual_reduction_against_roundoff"] = "no"; } else if (preset_name == "byrd") { - options["constraint_relaxation_strategy"] = "l1_relaxation"; - options["subproblem"] = "QP"; - options["globalization_mechanism"] = "LS"; - options["globalization_strategy"] = "l1_merit"; - options["l1_relaxation_initial_parameter"] = "1"; - options["LS_backtracking_ratio"] = "0.5"; - options["armijo_decrease_fraction"] = "1e-8"; - options["l1_relaxation_epsilon1"] = "0.1"; - options["l1_relaxation_epsilon2"] = "0.1"; - options["l1_constraint_violation_coefficient"] = "1."; - options["tolerance"] = "1e-6"; - options["loose_tolerance"] = "1e-6"; - options["progress_norm"] = "L1"; - options["residual_norm"] = "L1"; - options["sparse_format"] = "CSC"; - options["LS_scale_duals_with_step_length"] = "no"; - options["protect_actual_reduction_against_roundoff"] = "no"; + (*this)["constraint_relaxation_strategy"] = "l1_relaxation"; + (*this)["subproblem"] = "QP"; + (*this)["globalization_mechanism"] = "LS"; + (*this)["globalization_strategy"] = "l1_merit"; + (*this)["l1_relaxation_initial_parameter"] = "1"; + (*this)["LS_backtracking_ratio"] = "0.5"; + (*this)["armijo_decrease_fraction"] = "1e-8"; + (*this)["l1_relaxation_epsilon1"] = "0.1"; + (*this)["l1_relaxation_epsilon2"] = "0.1"; + (*this)["l1_constraint_violation_coefficient"] = "1."; + (*this)["tolerance"] = "1e-6"; + (*this)["loose_tolerance"] = "1e-6"; + (*this)["progress_norm"] = "L1"; + (*this)["residual_norm"] = "L1"; + (*this)["sparse_format"] = "CSC"; + (*this)["LS_scale_duals_with_step_length"] = "no"; + (*this)["protect_actual_reduction_against_roundoff"] = "no"; } else if (preset_name == "funnelsqp") { - options["constraint_relaxation_strategy"] = "feasibility_restoration"; - options["subproblem"] = "QP"; - options["globalization_mechanism"] = "TR"; - options["globalization_strategy"] = "funnel_method"; - options["progress_norm"] = "L1"; - options["residual_norm"] = "L2"; - options["sparse_format"] = "CSC"; - options["TR_radius"] = "10"; - options["l1_constraint_violation_coefficient"] = "1."; - options["enforce_linear_constraints"] = "yes"; - options["tolerance"] = "1e-6"; - options["loose_tolerance"] = "1e-6"; - options["TR_min_radius"] = "1e-8"; - options["switch_to_optimality_requires_acceptance"] = "no"; - options["switch_to_optimality_requires_linearized_feasibility"] = "yes"; + (*this)["constraint_relaxation_strategy"] = "feasibility_restoration"; + (*this)["subproblem"] = "QP"; + (*this)["globalization_mechanism"] = "TR"; + (*this)["globalization_strategy"] = "funnel_method"; + (*this)["progress_norm"] = "L1"; + (*this)["residual_norm"] = "L2"; + (*this)["sparse_format"] = "CSC"; + (*this)["TR_radius"] = "10"; + (*this)["l1_constraint_violation_coefficient"] = "1."; + (*this)["enforce_linear_constraints"] = "yes"; + (*this)["tolerance"] = "1e-6"; + (*this)["loose_tolerance"] = "1e-6"; + (*this)["TR_min_radius"] = "1e-8"; + (*this)["switch_to_optimality_requires_acceptance"] = "no"; + (*this)["switch_to_optimality_requires_linearized_feasibility"] = "yes"; - options["funnel_beta"] = "0.9999"; - options["funnel_gamma"] = "0.001"; - options["switching_delta"] = "0.999"; - options["funnel_kappa"] = "0.5"; - options["funnel_ubd"] = "1.0"; - options["funnel_fact"] = "1.5"; - options["funnel_switching_infeasibility_exponent"] = "2"; - options["funnel_update_strategy"] = "2"; + (*this)["funnel_beta"] = "0.9999"; + (*this)["funnel_gamma"] = "0.001"; + (*this)["switching_delta"] = "0.999"; + (*this)["funnel_kappa"] = "0.5"; + (*this)["funnel_ubd"] = "1.0"; + (*this)["funnel_fact"] = "1.5"; + (*this)["funnel_switching_infeasibility_exponent"] = "2"; + (*this)["funnel_update_strategy"] = "2"; } else { throw std::runtime_error("The preset " + preset_name + " is not known."); } } - void Options::get_command_line_arguments(int argc, char* argv[]) { - // build the (name, value) map - int i = 1; - while (i < argc - 1) { - std::string argument = std::string(argv[i]); - if (argument[0] == '-') { - if (i < argc - 1) { - // remove the '-' - const std::string name = argument.substr(1); - const std::string value = std::string(argv[i + 1]); - if (name == "preset") { - find_preset(value, *this); - } - else { - this->operator[](name) = value; - } - i += 2; - } - } - else { - WARNING << "Argument " << argument << " was ignored\n"; - i++; - } - } - } + } // namespace diff --git a/uno/tools/Options.hpp b/uno/tools/Options.hpp index 8e96b0ac..26c2b26b 100644 --- a/uno/tools/Options.hpp +++ b/uno/tools/Options.hpp @@ -20,11 +20,10 @@ namespace uno { [[nodiscard]] size_t get_unsigned_int(const std::string& key) const; [[nodiscard]] bool get_bool(const std::string& key) const; - void get_command_line_arguments(int argc, char* argv[]); - void print(bool only_used) const; static Options get_default_options(const std::string& file_name); + void find_preset(const std::string& preset_name); private: std::map options{};