Skip to content
Open
Show file tree
Hide file tree
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
85 changes: 85 additions & 0 deletions .github/julia/check_auto_preset.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright (c) 2026: Charlie Vanaret and contributors
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

using UnoSolver, JuMP

function test_hs015()
model = Model(() -> UnoSolver.Optimizer(; logger = "INFO"))

@variable(model, x, start = -2.)
@variable(model, y, start = 1.)

@objective(model, Min, 100*(y - x^2)^2 + (1 - x)^2)

c1 = @constraint(model, x*y >= 1)
c2 = @constraint(model, x + y^2 >= 0)
c3 = @constraint(model, x <= 0.5)

optimize!(model)

tolerance = 1e-6
@assert abs(objective_value(model) - 306.5) <= tolerance
@assert abs(value(x) - 0.5) <= tolerance
@assert abs(value(y) - 2.) <= tolerance
@assert abs(dual(c1) - 700.) <= tolerance
@assert abs(dual(c2) - 0.) <= tolerance
@assert abs(dual(c3) - (-1751.)) <= tolerance
# check the preset
optimizer = unsafe_backend(model)
uno_method = uno_get_method_description(optimizer.solver)
@assert occursin("SQP", uno_method)
end

function test_camshape_6400()
model = Model(() -> UnoSolver.Optimizer(; logger = "INFO"))

n = 6400 # number of discretization points
R_v = 1.0 # design parameter related to the valve shape
R_min = 1.0 # minimum allowed radius of the cam
R_max = 2.0 # maximum allowed radius of the cam
alpha = 1.5 # curvature limit parameter
d_theta = 2 * pi / (5 * (n + 1)) # angle between discretization points
cos_dt = cos(d_theta) # constant: d_theta is a parameter, not a variable

# radius of the cam at discretization points; start at the circle of radius (R_min+R_max)/2
@variable(model, R_min <= r[1:n] <= R_max, start = (R_min + R_max) / 2)

@objective(model, Max, (pi * R_v / n) * sum(r[i] for i in 1:n))

# Convexity (bilinear in r; cos_dt is a precomputed constant)
@constraint(model, convexity[i in 2:n-1],
-r[i-1] * r[i] - r[i] * r[i+1] + 2 * r[i-1] * r[i+1] * cos_dt <= 0)
@constraint(model, convex_edge1,
-R_min * r[1] - r[1] * r[2] + 2 * R_min * r[2] * cos_dt <= 0)
@constraint(model, convex_edge2,
-R_min^2 - R_min * r[1] + 2 * R_min * r[1] * cos_dt <= 0)
@constraint(model, convex_edge3,
-r[n-1] * r[n] - r[n] * R_max + 2 * r[n-1] * R_max * cos_dt <= 0)
@constraint(model, convex_edge4,
-2 * R_max * r[n] + 2 * r[n]^2 * cos_dt <= 0)

# Curvature, lower bound
@constraint(model, curvature[i in 1:n-1], -alpha * d_theta <= r[i+1] - r[i])
@constraint(model, curvature_edge1, -alpha * d_theta <= r[1] - R_min)
@constraint(model, curvature_edge2, -alpha * d_theta <= R_max - r[n])

# Curvature, upper bound
@constraint(model, curvature1[i in 1:n-1], r[i+1] - r[i] <= alpha * d_theta)
@constraint(model, curvature_edge11, r[1] - R_min <= alpha * d_theta)
@constraint(model, curvature_edge21, R_max - r[n] <= alpha * d_theta)


optimize!(model)

tolerance = 1e-6
@assert abs(objective_value(model) - 4.448378) <= tolerance
# check the preset
optimizer = unsafe_backend(model)
uno_method = uno_get_method_description(optimizer.solver)
@assert occursin("interior-point", uno_method)
end

test_hs015()
test_camshape_6400()
3 changes: 3 additions & 0 deletions .github/workflows/julia-tests-unosolver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:

steps:
- uses: actions/checkout@v4

# Install Julia 1.7 for BinaryBuilder. Note that this is an old version of
# Julia, but it is required for compatibility with BinaryBuilder.
- uses: julia-actions/setup-julia@v2
Expand Down Expand Up @@ -65,6 +66,7 @@ jobs:
run: |
julia --color=yes -e 'using Pkg; Pkg.add("BinaryBuilder")'
julia --color=yes .github/julia/build_tarballs_yggdrasil.jl x86_64-linux-gnu-cxx11 --verbose --deploy="local"

# Now install a newer version of Julia to actually test Uno_jll.jl.
# We choose v1.10 because it is the current Long-Term Support (LTS).
- uses: julia-actions/setup-julia@v2
Expand All @@ -81,4 +83,5 @@ jobs:
Pkg.instantiate()
Pkg.develop(path="/home/runner/.julia/dev/Uno_jll")
Pkg.develop(path="/home/runner/work/Uno/Uno/interfaces/Julia")
include("/home/runner/work/Uno/Uno/.github/julia/check_auto_preset.jl")
Pkg.test("UnoSolver")
38 changes: 19 additions & 19 deletions interfaces/AMPL/uno_ampl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ void* operator new(size_t size) {
*/

namespace uno {
void run_uno_ampl(const std::string& model_name, Options& options) {
const AMPLModel model(model_name);
void run_uno_ampl(const AMPLModel& model, Options& options) {
Uno uno{};
Result result = uno.solve(model, options);
if (options.get_bool("write_solution_to_file")) {
Expand All @@ -49,12 +48,11 @@ int main(int argc, char* argv[]) {
// AMPL expects: ./uno_ampl model.nl [-AMPL] [option_name=option_value, ...]
// model name
const char* model_name = argv[1];
const AMPLModel model(model_name);

// gather the options
// set the default options
Options options;
DefaultOptions::load(options);
// set default preset
Presets::set_default(options);

// the -AMPL flag indicates that the solution should be written to the AMPL solution file
size_t offset = 2;
Expand All @@ -68,37 +66,39 @@ int main(int argc, char* argv[]) {
try {
// get the command line arguments (options start at index offset)
const auto command_line_options = Options::get_command_line_options(argc, argv, offset);

// [optional] read an option file
std::optional<std::string> optional_option_file{};
std::optional<std::string> optional_preset{};
for (const auto& [option_name, option_value]: command_line_options) {
if (option_name == "option_file") {
optional_option_file = option_value;
}
else if (option_name == "preset") {
optional_preset = option_value;
}
}

// [optional] set options from an option file
if (optional_option_file.has_value()) {
Options::load_option_file(options, *optional_option_file);
}

// [optional] set a preset
if (optional_preset.has_value()) {
Presets::set(options, *optional_preset);
for (const auto& [option_name, option_value]: command_line_options) {
if (option_name == "preset") {
options.set_string("preset", option_value);
}
else if (option_name == "logger") {
options.set_string("logger", option_value);
}
}
Logger::set_logger(options.get_string("logger"));

// set the preset (default: auto)
Presets::set(model, options, options.get_string("preset"));

// set the rest of the command line options (note: we add preset to the options for debugging purposes)
// set the rest of the command line options
for (const auto& [option_name, option_value]: command_line_options) {
if (option_name != "option_file") {
if (option_name != "option_file" && option_name != "preset" && option_name != "logger") {
options.set(option_name, option_value);
}
}

// solve the model
Logger::set_logger(options.get_string("logger"));
run_uno_ampl(model_name, options);
run_uno_ampl(model, options);
}
catch (const std::exception& e) {
std::cout << "uno_ampl failed with the following error: " << e.what() << '\n';
Expand Down
72 changes: 35 additions & 37 deletions interfaces/C/Uno_C_API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ COStream* c_ostream = nullptr;

struct Solver {
Uno* solver;
Options* options;
Options* user_options;
UserCallbacks* user_callbacks;
Result* result;
};
Expand Down Expand Up @@ -836,8 +836,6 @@ void* uno_create_solver() {
// default options
Options* options = new Options;
DefaultOptions::load(*options);
// set default preset
Presets::set_default(*options);

// default user callbacks
UserCallbacks* user_callbacks = new NoUserCallbacks;
Expand All @@ -854,7 +852,7 @@ bool uno_set_solver_integer_option(void* solver, const char* option_name, uno_in
return false;
}
Solver* uno_solver = static_cast<Solver*>(solver);
uno_solver->options->set_integer(option_name, option_value);
uno_solver->user_options->set_integer(option_name, option_value);
return true;
}

Expand All @@ -864,7 +862,7 @@ bool uno_set_solver_double_option(void* solver, const char* option_name, double
return false;
}
Solver* uno_solver = static_cast<Solver*>(solver);
uno_solver->options->set_double(option_name, option_value);
uno_solver->user_options->set_double(option_name, option_value);
return true;
}

Expand All @@ -874,7 +872,7 @@ bool uno_set_solver_bool_option(void* solver, const char* option_name, bool opti
return false;
}
Solver* uno_solver = static_cast<Solver*>(solver);
uno_solver->options->set_bool(option_name, option_value);
uno_solver->user_options->set_bool(option_name, option_value);
return true;
}

Expand All @@ -883,19 +881,9 @@ bool uno_set_solver_string_option(void* solver, const char* option_name, const c
WARNING << "Please specify a valid solver." << std::endl;
return false;
}
// handle the preset separately
if (strcmp(option_name, "preset") == 0) {
return uno_set_solver_preset(solver, option_value);
}
// handle the option_file separately
else if (strcmp(option_name, "option_file") == 0) {
return uno_load_solver_option_file(solver, option_value);
}
else {
Solver* uno_solver = static_cast<Solver*>(solver);
uno_solver->options->set_string(option_name, option_value);
return true;
}
Solver* uno_solver = static_cast<Solver*>(solver);
uno_solver->user_options->set_string(option_name, option_value);
return true;
}

uno_int uno_get_solver_option_type(void* solver, const char* option_name) {
Expand All @@ -905,7 +893,7 @@ uno_int uno_get_solver_option_type(void* solver, const char* option_name) {
}
Solver* uno_solver = static_cast<Solver*>(solver);
try {
return static_cast<uno_int>(uno_solver->options->get_option_type(option_name));
return static_cast<uno_int>(uno_solver->user_options->get_option_type(option_name));
}
catch(const std::out_of_range&) {
return UNO_OPTION_TYPE_NOT_FOUND;
Expand Down Expand Up @@ -974,8 +962,7 @@ bool uno_load_solver_option_file(void* solver, const char* file_name) {
return false;
}
Solver* uno_solver = static_cast<Solver*>(solver);
// load_option_file() handles a possible preset separately
uno::Options::load_option_file(*uno_solver->options, file_name);
Options::load_option_file(*uno_solver->user_options, file_name);
return true;
}

Expand All @@ -985,7 +972,7 @@ bool uno_set_solver_preset(void* solver, const char* preset_name) {
return false;
}
Solver* uno_solver = static_cast<Solver*>(solver);
Presets::set(*uno_solver->options, preset_name);
Presets::set(*uno_solver->user_options, preset_name);
return true;
}

Expand All @@ -994,7 +981,8 @@ bool uno_set_solver_callbacks(void* solver, uno_notify_acceptable_iterate_callba
if (solver == nullptr) {
WARNING << "Please specify a valid solver." << std::endl;
return false;
} Solver* uno_solver = static_cast<Solver*>(solver);
}
Solver* uno_solver = static_cast<Solver*>(solver);
delete uno_solver->user_callbacks; // delete the previous callbacks
uno_solver->user_callbacks = new CUserCallbacks(notify_acceptable_iterate_callback, termination_callback, user_data);
return true;
Expand Down Expand Up @@ -1028,13 +1016,21 @@ void uno_optimize(void* solver, void* model) {
}
Solver* uno_solver = static_cast<Solver*>(solver);

// create an instance of UnoModel, a subclass of Model, and solve the model using Uno
// create an instance of UnoModel, a subclass of Model
const UnoModel uno_model(*user_model);
Logger::set_logger(uno_solver->options->get_string("logger"));
Result result = uno_solver->solver->solve(uno_model, *uno_solver->options, *uno_solver->user_callbacks);
// clean up the previous result (if any)
Logger::set_logger(uno_solver->user_options->get_string("logger"));

// set the preset (default: auto) and gather the options starting from the preset
Options full_options;
Presets::set(uno_model, full_options, uno_solver->user_options->get_string("preset"));

// copy the rest of the options
full_options.overwrite(*uno_solver->user_options);

// solve the model
Result result = uno_solver->solver->solve(uno_model, full_options, *uno_solver->user_callbacks);
// clean up the previous result (if any) and move the new result into uno_solver
delete uno_solver->result;
// move the new result into uno_solver
uno_solver->result = new Result(std::move(result));
// flush the logger
Logger::flush();
Expand All @@ -1053,23 +1049,23 @@ double uno_get_solver_double_option(void* solver, const char* option_name) {
throw std::runtime_error("Please specify a valid solver.");
}
Solver* uno_solver = static_cast<Solver*>(solver);
return uno_solver->options->get_double(option_name);
return uno_solver->user_options->get_double(option_name);
}

uno_int uno_get_solver_integer_option(void* solver, const char* option_name) {
if (solver == nullptr) {
throw std::runtime_error("Please specify a valid solver.");
}
Solver* uno_solver = static_cast<Solver*>(solver);
return uno_solver->options->get_int(option_name);
return uno_solver->user_options->get_int(option_name);
}

bool uno_get_solver_bool_option(void* solver, const char* option_name) {
if (solver == nullptr) {
throw std::runtime_error("Please specify a valid solver.");
}
Solver* uno_solver = static_cast<Solver*>(solver);
return uno_solver->options->get_bool(option_name);
return uno_solver->user_options->get_bool(option_name);
}

const char* uno_get_solver_string_option(void* solver, const char* option_name) {
Expand All @@ -1080,12 +1076,14 @@ const char* uno_get_solver_string_option(void* solver, const char* option_name)
// handle the preset and option_file separately
if (strcmp(option_name, "option_file") == 0 || strcmp(option_name, "preset") == 0) {
try {
return uno_solver->options->get_string(option_name).c_str();
} catch(const std::out_of_range&) {
return uno_solver->user_options->get_string(option_name).c_str();
}
catch(const std::out_of_range&) {
return nullptr;
}
} else {
return uno_solver->options->get_string(option_name).c_str();
}
else {
return uno_solver->user_options->get_string(option_name).c_str();
}
}

Expand Down Expand Up @@ -1260,7 +1258,7 @@ void uno_destroy_solver(void* solver) {
if (solver != nullptr) {
Solver* uno_solver = static_cast<Solver*>(solver);
delete uno_solver->solver;
delete uno_solver->options;
delete uno_solver->user_options;
delete uno_solver->user_callbacks;
if (uno_solver->result != nullptr) {
delete uno_solver->result;
Expand Down
Loading
Loading