# Copyright (C) 2010-14 Jelmer Ypma. All Rights Reserved. # SPDX-License-Identifier: LGPL-3.0-or-later # # File: test-example.R # Author: Jelmer Ypma # Date: 10 June 2010 # # Maintenance assumed by Avraham Adler (AA) on 2023-02-10 # # Example showing how to solve the problem from the NLopt tutorial. # # min sqrt(x2) # s.t. x2 >= 0 # x2 >= (a1*x1 + b1)^3 # x2 >= (a2*x1 + b2)^3 # where # a1 = 2, b1 = 0, a2 = -1, b2 = 1 # # re-formulate constraints to be of form g(x) <= 0 # (a1*x1 + b1)^3 - x2 <= 0 # (a2*x1 + b2)^3 - x2 <= 0 # # Optimal solution: (1/3, 8/27) # # CHANGELOG: # 2014-05-03: Changed example to use unit testing framework testthat. # 2019-12-12: Corrected warnings and using updated testtthat framework (AA) # 2023-02-07: Remove wrapping tests in "test_that" to reduce duplication. (AA) library(nloptr) tol <- sqrt(.Machine$double.eps) # objective function eval_f0 <- function(x, a, b) sqrt(x[2]) # constraint function eval_g0 <- function(x, a, b) (a * x[1] + b) ^ 3 - x[2] # gradient of objective function eval_grad_f0 <- function(x, a, b) c(0, 0.5 / sqrt(x[2])) # jacobian of constraint eval_jac_g0 <- function(x, a, b) { rbind(c(3 * a[1] * (a[1] * x[1] + b[1]) ^ 2, -1), c(3 * a[2] * (a[2] * x[1] + b[2]) ^ 2, -1)) } # Functions with gradients in objective and constraint function. This can be # useful if the same calculations are needed for the function value and the # gradient. eval_f1 <- function(x, a, b) { list("objective" = sqrt(x[2]), "gradient" = c(0, 0.5 / sqrt(x[2]))) } eval_g1 <- function(x, a, b) { list("constraints" = (a * x[1] + b) ^ 3 - x[2], "jacobian" = rbind(c(3 * a[1] * (a[1] * x[1] + b[1]) ^ 2, -1), c(3 * a[2] * (a[2] * x[1] + b[2]) ^ 2, -1))) } # Define parameters. a <- c(2, -1) b <- c(0, 1) # Define optimal solution. solution.opt <- c(1 / 3, 8 / 27) # Test NLopt tutorial example with NLOPT_LD_MMA with gradient information. Solve # using NLOPT_LD_MMA with gradient information supplied in separate function. res0 <- nloptr( x0 = c(1.234, 5.678), eval_f = eval_f0, eval_grad_f = eval_grad_f0, lb = c(-Inf, 0), ub = c(Inf, Inf), eval_g_ineq = eval_g0, eval_jac_g_ineq = eval_jac_g0, opts = list("xtol_rel" = 1e-4, "algorithm" = "NLOPT_LD_MMA"), a = a, b = b ) expect_equal(res0$solution, solution.opt, tolerance = tol) # Test NLopt tutorial example with NLOPT_LN_COBYLA with gradient information. # Solve using NLOPT_LN_COBYLA without gradient information A tighter convergence # tolerance is used here (1e-6), to ensure that the final solution is equal to # the optimal solution (within some tolerance). res1 <- nloptr( x0 = c(1.234, 5.678), eval_f = eval_f0, lb = c(-Inf, 0), ub = c(Inf, Inf), eval_g_ineq = eval_g0, opts = list("xtol_rel" = 1e-6, "algorithm" = "NLOPT_LN_COBYLA"), a = a, b = b ) expect_equal(res1$solution, solution.opt, tolerance = tol) # Test NLopt tutorial example with NLOPT_LN_COBYLA with gradient information # using combined function. # Solve using NLOPT_LD_MMA with gradient information in objective function res2 <- nloptr( x0 = c(1.234, 5.678), eval_f = eval_f1, lb = c(-Inf, 0), ub = c(Inf, Inf), eval_g_ineq = eval_g1, opts = list("xtol_rel" = 1e-4, "algorithm" = "NLOPT_LD_MMA"), a = a, b = b ) expect_equal(res2$solution, solution.opt, tolerance = tol)