From 492ab7ae49d3a1f03e6ff5935ef2ad76b0a18f08 Mon Sep 17 00:00:00 2001 From: Russ Tedrake Date: Fri, 1 Oct 2021 07:29:59 -0400 Subject: [PATCH] Build ibex 2.8.6 from source Resolves #13219 and #15872 Relevant to #15946 Should (finally) get #15789 unstuck. Includes a patch to dReal and updates to IbexSolver from @soonhokong. Co-authored-by: Soonho Kong --- solvers/ibex_solver.cc | 21 +- solvers/test/ibex_solver_test.cc | 10 +- tools/workspace/BUILD.bazel | 2 +- tools/workspace/default.bzl | 2 +- tools/workspace/dreal/ibex_2.8.6.patch | 53 +++ tools/workspace/dreal/repository.bzl | 3 + tools/workspace/ibex/BUILD.bazel | 15 - .../workspace/ibex/package-ubuntu.BUILD.bazel | 71 ---- tools/workspace/ibex/package.BUILD.bazel | 361 ++++++++++++++++++ tools/workspace/ibex/repository.bzl | 116 +----- tools/workspace/ibex/version.bzl | 4 - 11 files changed, 450 insertions(+), 208 deletions(-) create mode 100644 tools/workspace/dreal/ibex_2.8.6.patch delete mode 100644 tools/workspace/ibex/package-ubuntu.BUILD.bazel create mode 100644 tools/workspace/ibex/package.BUILD.bazel delete mode 100644 tools/workspace/ibex/version.bzl diff --git a/solvers/ibex_solver.cc b/solvers/ibex_solver.cc index 8b413fcf80e9..1d1a4e632cf9 100644 --- a/solvers/ibex_solver.cc +++ b/solvers/ibex_solver.cc @@ -28,12 +28,12 @@ struct IbexOptions { // Relative precision on the objective. See // http://www.ibex-lib.org/doc/optim.html?highlight=eps#objective-precision-criteria // for details. - double rel_eps_f{ibex::Optimizer::default_rel_eps_f}; + double rel_eps_f{ibex::DefaultOptimizerConfig::default_rel_eps_f}; // Absolute precision on the objective. See // http://www.ibex-lib.org/doc/optim.html?highlight=eps#objective-precision-criteria // for details. - double abs_eps_f{ibex::Optimizer::default_abs_eps_f}; + double abs_eps_f{ibex::DefaultOptimizerConfig::default_abs_eps_f}; // Equality relaxation value. See // http://www.ibex-lib.org/doc/optim.html?highlight=eps#the-output-of-ibexopt @@ -46,10 +46,10 @@ struct IbexOptions { bool rigor{false}; // Random seed (useful for reproducibility). - double random_seed{ibex::DefaultOptimizer::default_random_seed}; + double random_seed{ibex::DefaultOptimizerConfig::default_random_seed}; // Precision on the variable. - double eps_x{ibex::Optimizer::default_eps_x}; + double eps_x{ibex::DefaultOptimizerConfig::default_eps_x}; // Activate trace. Updates of loup/uplo are printed while minimizing. // - 0 (by default): nothing is printed. @@ -197,11 +197,14 @@ VectorXd ExtractMidpoints(const ibex::IntervalVector& iv) { void DoOptimize(const ibex::System& sys, const IbexOptions& options, MathematicalProgramResult* const result) { const bool in_hc4 = !(sys.nb_ctr > 0 && sys.nb_ctr < sys.f_ctrs.image_dim()); - ibex::DefaultOptimizer o(sys, options.rel_eps_f, options.abs_eps_f, - options.eps_h, options.rigor, in_hc4, - options.random_seed, options.eps_x); - o.trace = options.trace; - o.timeout = options.timeout; + const bool kkt = (sys.nb_ctr == 0); + ibex::DefaultOptimizerConfig config(sys, options.rel_eps_f, options.abs_eps_f, + options.eps_h, options.rigor, in_hc4, kkt, + options.random_seed, options.eps_x); + + config.set_trace(options.trace); + config.set_timeout(options.timeout); + ibex::Optimizer o(config); const ibex::Optimizer::Status status = o.optimize(sys.box); switch (status) { diff --git a/solvers/test/ibex_solver_test.cc b/solvers/test/ibex_solver_test.cc index 7269221b8c23..422dbd3c3a6d 100644 --- a/solvers/test/ibex_solver_test.cc +++ b/solvers/test/ibex_solver_test.cc @@ -108,13 +108,13 @@ TEST_F(IbexSolverTest, GenericCost) { prog_.AddCost(Binding(generic_cost, x_)); EXPECT_FALSE(prog_.generic_costs().empty()); if (solver_.available()) { - auto result = solver_.Solve(prog_); + auto result = solver_.Solve(prog_, {}); ASSERT_TRUE(result.is_success()); const auto x_val = result.GetSolution(prog_.decision_variables()); const double v0{result.GetSolution(x0)}; const double v1{result.GetSolution(x1)}; const double v2{result.GetSolution(x2)}; - EXPECT_NEAR(v0, 1.413078079, 1e-8); + EXPECT_NEAR(v0, 1.4142135623730951, 1e-8); EXPECT_NEAR(v1, 1, 1e-8); EXPECT_NEAR(v2, 1, 1e-8); } @@ -368,9 +368,9 @@ class IbexSolverOptionTest1 : public ::testing::Test { prog_.AddConstraint(x0, -5, 5); prog_.AddConstraint(x1, -5, 5); prog_.AddConstraint(x2, -5, 5); - prog_.AddConstraint(2 * x0 - 3 * x1 + 4 * x2 <= 1.0); - prog_.AddConstraint(2 * x0 - 3 * x1 + 4 * x2 >= 1.0); - prog_.AddCost(x_(1)); + prog_.AddConstraint(2 * x0 - 3 * x1 + 4 * x2 <= 1.0 + 1e-10); + prog_.AddConstraint(2 * x0 - 3 * x1 + 4 * x2 >= 1.0 - 1e-10); + prog_.AddCost(x_(1) + x_(2)); } MathematicalProgram prog_; diff --git a/tools/workspace/BUILD.bazel b/tools/workspace/BUILD.bazel index 2770c440523d..7914d41e0601 100644 --- a/tools/workspace/BUILD.bazel +++ b/tools/workspace/BUILD.bazel @@ -74,7 +74,7 @@ _DRAKE_EXTERNAL_PACKAGE_INSTALLS = ["@%s//:install" % p for p in [ "//conditions:default": ["@csdp//:install"], "//tools:no_csdp": [], }) + select({ - "//conditions:default": ["//tools/workspace/ibex:install"], + "//conditions:default": ["@ibex//:install"], "//tools:no_dreal": [], }) + select({ "//tools:with_gurobi": ["@gurobi//:install"], diff --git a/tools/workspace/default.bzl b/tools/workspace/default.bzl index 0c9066e38968..1f5e6c4364e2 100644 --- a/tools/workspace/default.bzl +++ b/tools/workspace/default.bzl @@ -164,7 +164,7 @@ def add_default_repositories(excludes = [], mirrors = DEFAULT_MIRRORS): if "gurobi" not in excludes: gurobi_repository(name = "gurobi") if "ibex" not in excludes: - ibex_repository(name = "ibex") + ibex_repository(name = "ibex", mirrors = mirrors) if "ignition_math" not in excludes: ignition_math_repository(name = "ignition_math", mirrors = mirrors) if "ignition_utils" not in excludes: diff --git a/tools/workspace/dreal/ibex_2.8.6.patch b/tools/workspace/dreal/ibex_2.8.6.patch new file mode 100644 index 000000000000..57613dfebf69 --- /dev/null +++ b/tools/workspace/dreal/ibex_2.8.6.patch @@ -0,0 +1,53 @@ +diff --git dreal/contractor/contractor_ibex_polytope.cc dreal/contractor/contractor_ibex_polytope.cc +index e2b12fb..de4352a 100644 +--- dreal/contractor/contractor_ibex_polytope.cc ++++ dreal/contractor/contractor_ibex_polytope.cc +@@ -67,9 +67,9 @@ ContractorIbexPolytope::ContractorIbexPolytope(vector formulas, + } + + // Build Polytope contractor from system. +- linear_relax_combo_ = make_unique( +- *system_, ibex::LinearizerCombo::XNEWTON); +- ctc_ = make_unique(*linear_relax_combo_); ++ linearizer_xtaylor_ = make_unique( ++ *system_); ++ ctc_ = make_unique(*linearizer_xtaylor_); + + // Build input. + DynamicBitset& input{mutable_input()}; +diff --git dreal/contractor/contractor_ibex_polytope.h dreal/contractor/contractor_ibex_polytope.h +index da7215a..52c83ad 100644 +--- dreal/contractor/contractor_ibex_polytope.h ++++ dreal/contractor/contractor_ibex_polytope.h +@@ -75,7 +75,7 @@ class ContractorIbexPolytope : public ContractorCell { + IbexConverter ibex_converter_; + std::unique_ptr system_factory_; + std::unique_ptr system_; +- std::unique_ptr linear_relax_combo_; ++ std::unique_ptr linearizer_xtaylor_; + std::unique_ptr ctc_; + std::vector> expr_ctrs_; + }; +diff --git dreal/util/test/box_test.cc dreal/util/test/box_test.cc +index 365de51..6ea6a12 100644 +--- dreal/util/test/box_test.cc ++++ dreal/util/test/box_test.cc +@@ -277,12 +277,12 @@ TEST_F(BoxTest, Equality) { + // Checks types in Box are nothrow move-constructible so that the + // vectors including them can be processed efficiently. + TEST_F(BoxTest, IsNothrowMoveConstructible) { +- static_assert(is_nothrow_move_constructible::value, +- "Box::Interval should be nothrow_move_constructible."); +- static_assert(is_nothrow_move_constructible::value, +- "Box::IntervalVector should be nothrow_move_constructible."); +- static_assert(is_nothrow_move_constructible::value, +- "Box should be nothrow_move_constructible."); ++ // static_assert(is_nothrow_move_constructible::value, ++ // "Box::Interval should be nothrow_move_constructible."); ++ // static_assert(is_nothrow_move_constructible::value, ++ // "Box::IntervalVector should be nothrow_move_constructible."); ++ // static_assert(is_nothrow_move_constructible::value, ++ // "Box should be nothrow_move_constructible."); + } + + } // namespace diff --git a/tools/workspace/dreal/repository.bzl b/tools/workspace/dreal/repository.bzl index dc0c86dcfee0..d8af56955393 100644 --- a/tools/workspace/dreal/repository.bzl +++ b/tools/workspace/dreal/repository.bzl @@ -11,4 +11,7 @@ def dreal_repository( commit = "4.21.06.2", sha256 = "7bbd328a25c14cff814753694b1823257bb7cff7f84a7b705b9f111624d5b2e4", # noqa mirrors = mirrors, + patches = [ + "@drake//tools/workspace/dreal:ibex_2.8.6.patch", + ], ) diff --git a/tools/workspace/ibex/BUILD.bazel b/tools/workspace/ibex/BUILD.bazel index a4d09522bedd..f3cd0705d1e6 100644 --- a/tools/workspace/ibex/BUILD.bazel +++ b/tools/workspace/ibex/BUILD.bazel @@ -8,19 +8,4 @@ load("//tools/lint:lint.bzl", "add_lint_tests") package(default_visibility = ["//visibility:public"]) -config_setting( - name = "linux", - values = {"cpu": "k8"}, -) - -# Only Ubuntu needs an installation rule; on macOS, we are using IBEX from -# homebrew, so Drake's installation script doesn't need to do anything extra. -install( - name = "install", - deps = select({ - ":linux": ["@ibex//:install"], - "@//conditions:default": [], - }), -) - add_lint_tests() diff --git a/tools/workspace/ibex/package-ubuntu.BUILD.bazel b/tools/workspace/ibex/package-ubuntu.BUILD.bazel deleted file mode 100644 index 4313a2334553..000000000000 --- a/tools/workspace/ibex/package-ubuntu.BUILD.bazel +++ /dev/null @@ -1,71 +0,0 @@ -# -*- python -*- - -load( - "@drake//tools/install:install.bzl", - "install", -) -load( - "@drake//tools/workspace/ibex:version.bzl", - "IBEX_VERSION", -) - -licenses(["notice"]) # Apache-2.0 - -package(default_visibility = ["//visibility:public"]) - -_IBEX_BASE = "opt/libibex/{}/".format(IBEX_VERSION) - -_IBEX_HDRS_GLOB = glob([_IBEX_BASE + "include/ibex/**"]) or fail("Missing ibex hdrs") # noqa - -# IBEX has the following dependencies in addition to those declared in deps: -# Clp: EPL-1 -# CoinUtils: EPL-1 -# bzip2: bzip2-1.0.6 - -cc_library( - name = "ibex", - srcs = [_IBEX_BASE + "lib/libdrake_ibex.so"], - hdrs = [_IBEX_BASE + "include/ibex.h"] + _IBEX_HDRS_GLOB, - # The includes= here are transcribed from the contents of the *.pc file. - includes = [ - _IBEX_BASE + "include", - _IBEX_BASE + "include/ibex", - _IBEX_BASE + "include/ibex/3rd", - ], - licenses = [ - "notice", # bzip2-1.0.6 - "reciprocal", # EPL-1 - "restricted", # LGPL-3.0 - ], - # The linkopts= here are transcribed from the contents of the *.pc file. - linkopts = [ - "-lClp", - "-lCoinUtils", - "-lbz2", - "-lm", - ], - # The deps= here are transcribed from the contents of the *.pc file. - deps = [ - "@blas", - "@lapack", - "@zlib", - ], -) - -install( - name = "install", - data_strip_prefix = [_IBEX_BASE], - # The *.pc files don't have the correct ${prefix} anymore, so its probably - # best to omit them. (Nothing else in Drake installs *.pc files, either.) - # The binaries won't work either, given our renamed libraries. - data = glob( - include = [ - _IBEX_BASE + "**", - ], - exclude = [ - "**/bin/**", - "**/*.pc", - ], - ), - data_dest = "", -) diff --git a/tools/workspace/ibex/package.BUILD.bazel b/tools/workspace/ibex/package.BUILD.bazel new file mode 100644 index 000000000000..e0ed741feb7d --- /dev/null +++ b/tools/workspace/ibex/package.BUILD.bazel @@ -0,0 +1,361 @@ +# -*- python -*- + +load( + "@drake//tools/install:install.bzl", + "install", +) +load( + "@drake//tools/workspace:cmake_configure_file.bzl", + "cmake_configure_file", +) +load( + "@drake//tools/workspace:generate_include_header.bzl", + "drake_generate_include_header", +) + +licenses(["restricted"]) # LGPL-2.1-or-later AND LGPL-3.0-only + +package(default_visibility = ["//visibility:private"]) + +# First we build filib from the .tgz and patch file in the ibex repository. + +cmake_configure_file( + name = "rounding_control_config", + src = "filibsrc-3.0.2.2/rounding_control/rounding_control_config.hpp.in", + out = "filibsrc-3.0.2.2/rounding_control/rounding_control_config.hpp", + defines = ["define_have_sse=#define HAVE_SSE", "define_have_x87="], +) + +# Note: We cannot glob the files since they are generated by the +# `extract_filib` rule. Instead, we list them explicitly here. +_FILIB_FILES = [ + "filibsrc-3.0.2.2/" + s + for s in [ + "interval/stdfun/filib_consts.hpp", + "interval/interval_fo.hpp", + "interval/filib.hpp", + "interval/interval.hpp", + "ieee/primitive.hpp", + "fp_traits/fp_traits_float.hpp", + "fp_traits/fp_traits_x87_const.hpp", + "fp_traits/fp_traits_double.hpp", + "fp_traits/fp_traits_sse_const.hpp", + "fp_traits/fp_traits.hpp", + "rounding_control/rounding_control_config.hpp.in", + "rounding_control/rounding_control_stub.hpp", + "rounding_control/rounding_control_float.hpp", + "rounding_control/rounding_control_double.hpp", + "rounding_control/rounding_control.hpp", + "interval/stdfun/interval/asin.icc", + "interval/stdfun/interval/sqr.icc", + "interval/stdfun/interval/pow.icc", + "interval/stdfun/interval/tanh.icc", + "interval/stdfun/interval/sqrt.icc", + "interval/stdfun/interval/log10.icc", + "interval/stdfun/interval/log.icc", + "interval/stdfun/interval/exp2.icc", + "interval/stdfun/interval/acosh.icc", + "interval/stdfun/interval/acot.icc", + "interval/stdfun/interval/coth.icc", + "interval/stdfun/interval/expm1.icc", + "interval/stdfun/interval/exp.icc", + "interval/stdfun/interval/log1p.icc", + "interval/stdfun/interval/tan.icc", + "interval/stdfun/interval/atanh.icc", + "interval/stdfun/interval/cot.icc", + "interval/stdfun/interval/exp10.icc", + "interval/stdfun/interval/log2.icc", + "interval/stdfun/interval/asinh.icc", + "interval/stdfun/interval/acoth.icc", + "interval/stdfun/interval/sin.icc", + "interval/stdfun/interval/acos.icc", + "interval/stdfun/interval/cos.icc", + "interval/stdfun/interval/sinh.icc", + "interval/stdfun/interval/atan.icc", + "interval/stdfun/interval/cosh.icc", + "interval/stdfun/filib_tools.icc", + "interval/stdfun/point/q_log.icc", + "interval/stdfun/point/q_lg10.icc", + "interval/stdfun/point/q_cot.icc", + "interval/stdfun/point/q_tan.icc", + "interval/stdfun/point/q_acot.icc", + "interval/stdfun/point/q_exp.icc", + "interval/stdfun/point/q_sqrt.icc", + "interval/stdfun/point/q_expm.icc", + "interval/stdfun/point/q_acos.icc", + "interval/stdfun/point/q_sqr.icc", + "interval/stdfun/point/q_atnh.icc", + "interval/stdfun/point/q_atn1.icc", + "interval/stdfun/point/q_rtrg.icc", + "interval/stdfun/point/q_cth1.icc", + "interval/stdfun/point/q_asin.icc", + "interval/stdfun/point/q_errm.icc", + "interval/stdfun/point/q_acth.icc", + "interval/stdfun/point/q_sinh.icc", + "interval/stdfun/point/q_cosh.icc", + "interval/stdfun/point/q_sin.icc", + "interval/stdfun/point/q_cos1.icc", + "interval/stdfun/point/q_ep1.icc", + "interval/stdfun/point/q_log2.icc", + "interval/stdfun/point/q_ex10.icc", + "interval/stdfun/point/q_cos.icc", + "interval/stdfun/point/q_tanh.icc", + "interval/stdfun/point/q_log1.icc", + "interval/stdfun/point/q_asnh.icc", + "interval/stdfun/point/q_sin1.icc", + "interval/stdfun/point/q_atan.icc", + "interval/stdfun/point/q_epm1.icc", + "interval/stdfun/point/q_coth.icc", + "interval/stdfun/point/q_acsh.icc", + "interval/stdfun/point/q_exp2.icc", + "interval/interval_arith.icc", + "interval/interval.icc", + "interval/tools.icc", + "fp_traits/fp_traits_double_x87_multiplicative.icc", + "fp_traits/fp_traits_float_sse_native_switched.icc", + "fp_traits/fp_traits_double_sse_multiplicative.icc", + "fp_traits/fp_traits_float_generic_multiplicative.icc", + "fp_traits/fp_traits_float_sse_native_directed.icc", + "fp_traits/fp_traits_float_sse_native_onesided_global.icc", + "fp_traits/fp_traits_float_x87_multiplicative.icc", + "fp_traits/fp_traits_double_sse_pred_succ_rounding.icc", + "fp_traits/fp_traits_double_sse_native_onesided_global.icc", + "fp_traits/fp_traits_double_generic_pred_succ_rounding.icc", + "fp_traits/fp_traits_double_sse_native_switched.icc", + "fp_traits/fp_traits_double_generic_native_switched.icc", + "fp_traits/fp_traits_float_generic_native_onesided_global.icc", + "fp_traits/fp_traits_double_sse_no_rounding.icc", + "fp_traits/fp_traits_double_x87_pred_succ_rounding.icc", + "fp_traits/fp_traits_float_x87_native_switched.icc", + "fp_traits/fp_traits_double_sse_native_directed.icc", + "fp_traits/fp_traits_float_x87_native_onesided_global.icc", + "fp_traits/fp_traits_float_x87_no_rounding.icc", + "fp_traits/fp_traits_double_generic_native_onesided_global.icc", + "fp_traits/fp_traits_double_generic_native_directed.icc", + "fp_traits/fp_traits_double_x87_native_directed.icc", + "fp_traits/fp_traits_double_generic_no_rounding.icc", + "fp_traits/fp_traits_float_x87_native_directed.icc", + "fp_traits/fp_traits_base_double.icc", + "fp_traits/fp_traits_double_x87_native_switched.icc", + "fp_traits/fp_traits_float_sse_multiplicative.icc", + "fp_traits/fp_traits_float_generic_native_switched.icc", + "fp_traits/fp_traits_double_x87_native_onesided_global.icc", + "fp_traits/fp_traits_float_generic_native_directed.icc", + "fp_traits/fp_traits_base_float.icc", + "fp_traits/fp_traits_float_sse_no_rounding.icc", + "fp_traits/fp_traits_double_x87_no_rounding.icc", + "fp_traits/fp_traits_double_generic_multiplicative.icc", + "fp_traits/fp_traits_float_generic_no_rounding.icc", + "rounding_control/rounding_control_float_iso9x.icc", + "rounding_control/rounding_control_float_asmmsvci386.icc", + "rounding_control/rounding_control_double_asmmsvci386.icc", + "rounding_control/rounding_control_double_asmsparc.icc", + "rounding_control/rounding_control_double_void.icc", + "rounding_control/rounding_control_float_void.icc", + "rounding_control/rounding_control_float_asmsparc.icc", + "rounding_control/rounding_control_double_iso9x.icc", + "interval/stdfun/filib_consts.cpp", + "ieee/primitive.cpp", + "fp_traits/fp_traits_base_float_setup.cpp", + "fp_traits/fp_traits_sse_const.cpp", + "fp_traits/fp_traits_float_setup.cpp", + "fp_traits/fp_traits_double_setup.cpp", + "fp_traits/fp_traits_x87_const.cpp", + "fp_traits/fp_traits_base_double_setup.cpp", + ] +] + +_FILIB_CONFIGURED_FILES = [ + s[:-3] if s[-3:] == ".in" else s + for s in _FILIB_FILES +] +_FILIB_HEADER_FILES = [ + s + for s in _FILIB_CONFIGURED_FILES + if s[-4:] == ".hpp" or s[-4:] == ".icc" +] +_FILIB_SOURCE_FILES = [s for s in _FILIB_CONFIGURED_FILES if ".cpp" in s] + +genrule( + name = "extract_filib", + srcs = [ + "interval_lib_wrapper/filib/3rd/filibsrc-3.0.2.2.tar.gz", + "interval_lib_wrapper/filib/3rd/filibsrc-3.0.2.2.all.all.patch", + ], + outs = _FILIB_FILES, + cmd = "tar -xzf $(location interval_lib_wrapper/filib/3rd/filibsrc-3.0.2.2.tar.gz) -C $(@D) && patch --quiet -p1 -d $(@D) <$(location interval_lib_wrapper/filib/3rd/filibsrc-3.0.2.2.all.all.patch)", # noqa +) + +# Excerpt from the Filib CMakeLists.txt (contained in the .tar.gz file): +# With Filib, we need to add flags "-frounding-math" and "-ffloat-store" +# It is necessary to use filib, to avoid problem with x80 processor. +# This option prevents undesirable excess precision on machines such as the +# 68000 where the floating registers (of the 68881) keep more precision than a +# "double" is supposed to have. Similarly for the x86 architecture. For most +# programs, the excess precision does only good, but a few programs rely on the +# precise definition of IEEE floating point. Use -ffloat-store for such +# programs, after modifying them to store all pertinent intermediate +# computations into variables. +cc_library( + name = "filib", + srcs = _FILIB_SOURCE_FILES, + hdrs = _FILIB_HEADER_FILES, + includes = ["filibsrc-3.0.2.2"], + copts = ["-w", "-frounding-math", "-ffloat-store", "-DHAVE_BOOST"], + linkstatic = True, +) + +# Now we actually build IBEX. First we define the variables that must get +# pushed into the three .in files during the cmake configure step. The values +# were obtained primarily by running the ibex cmake build and inspecting the +# resulting files generated from the .in sources. + +OPERATORS_INCLUDES = """ +#include "ibex_atanhc.h" +#include "ibex_atanhccc.h" +#include "ibex_crossproduct.h" +#include "ibex_sinc.h" +#include "ibex_trace.h" +""" + +OPERATORS_FCT_DEF = """ +inline Interval atanhc(const Interval& x) { + return UnaryOperator::fwd(x); +} +inline Interval atanhccc(const Interval& x) { + return UnaryOperator::fwd(x); +} +inline IntervalVector cross(const IntervalVector& x1, const IntervalVector& x2) +{ + return BinaryOperator::fwd(x1,x2); +} +inline Interval sinc(const Interval& x) { + return UnaryOperator::fwd(x); +} +inline IntervalVector trace(const IntervalMatrix& x) { + return UnaryOperator::fwd(x); +} +""" + +OPERATORS_MACRO_UNARY = """ +ADD_UNARY_OPERATOR(ATANHC,Interval,Interval); +ADD_UNARY_OPERATOR(ATANHCCC,Interval,Interval); +ADD_UNARY_OPERATOR(SINC,Interval,Interval); +ADD_UNARY_OPERATOR(TRACE,IntervalMatrix,IntervalVector); +""" + +OPERATORS_MACRO_BINARY = """ +ADD_BINARY_OPERATOR(CROSS_PRODUCT,IntervalVector,IntervalVector,IntervalVector); +""" + +IBEX_CONFIG = [ + "IBEX_VERSION=2.8.6", + # The default INTERVAL_LIB (gaol) is GPL. filib is lesser-GPL. + "INTERVAL_LIB=filib", + "LP_LIB=clp", + "OPERATORS_INCLUDES=" + OPERATORS_INCLUDES, + "OPERATORS_FCT_DEF=" + OPERATORS_FCT_DEF, + "OPERATORS_MACRO_UNARY=" + OPERATORS_MACRO_UNARY, + "OPERATORS_MACRO_BINARY=" + OPERATORS_MACRO_BINARY, +] + +# Loop through the .in files and run cmake_configure_file. +[ + cmake_configure_file( + name = "gen_" + file, + src = file + ".in", + out = file, + defines = IBEX_CONFIG, + visibility = ["//visibility:private"], + ) + for file in [ + "src/ibex_Setting.h", + "src/symbolic/ibex_ExprOperators.h", + "src/symbolic/ibex_ExprOperators.cpp", + ] +] + +IBEX_HEADERS = glob([ + "src/**/*.h", + "plugins/optim/src/**/*.h", +], exclude = [ + "src/bin/*", +]) + [ + "src/ibex_Setting.h", + "src/symbolic/ibex_ExprOperators.h", + "interval_lib_wrapper/filib/ibex_IntervalLibWrapper.h", + "interval_lib_wrapper/filib/ibex_IntervalLibWrapper.inl", + "lp_lib_wrapper/clp/ibex_LPLibWrapper.h", +] + +drake_generate_include_header( + name = "gen_ibex_h", + out = "src/ibex.h", + hdrs = IBEX_HEADERS, + strip_prefix = ["src/"], +) + +# Note: We have committed the flex and yacc generated outputs to our fork of +# ibex to avoid the unnecessary dependency here. If we need to revisit this +# decision, the genrules needed are: +# "/usr/bin/flex -Pibex -o $(@D)/lexer.lex.cc $(location src/parser/#lexer.l)" +# to generate "src/parser/lexer.lex.cc" and +# "/usr/bin/bison --report=all --name-prefix=ibex --file-prefix=parser -d +# -o $(@D)/src/parser/parser.tab.cc $(location src/parser/parser.yc)" +# to generate ["src/parser/parser.tab.cc", "src/parser/parser.tab.hh"]. +# To do this properly in bazel, +# "@dreal//third_party/com_github_google_kythe/tools/build_rules:lexyacc.bzl" +# is close to what we need to pull in flex and bison. + +cc_library( + name = "ibex", + srcs = glob([ + "src/**/*.cpp", + "src/parser/*.cc", + "plugins/optim/src/**/*.cpp", + ], exclude = [ + "src/bin/*", + ]) + [ + # These files must be generated: + "interval_lib_wrapper/filib/ibex_IntervalLibWrapper.cpp", + "lp_lib_wrapper/clp/ibex_LPLibWrapper.cpp", + "src/symbolic/ibex_ExprOperators.cpp", + ], + hdrs = IBEX_HEADERS + [ + "src/ibex.h", + "src/parser/parser.tab.hh", + ], + includes = glob( + ["src/*", "plugins/optim/src/*"], + exclude = ["src/wscript", "src/*.*"], + exclude_directories = 0, + ) + [ + "src", + "interval_lib_wrapper/filib", + "lp_lib_wrapper/clp", + "", # For plugins + ], + copts = ["-w -std=c++11"], + # The linkopts= here are transcribed from the contents of the *.pc file. + linkopts = [ + "-lbz2", + "-lm", + ], + # The deps= here are transcribed from the contents of the *.pc file. + deps = [ + ":filib", + "@blas", + "@clp", + "@lapack", + "@zlib", + ], + visibility = ["//visibility:public"], + linkstatic = True, +) + +install( + name = "install", + docs = ["LICENSE"], + visibility = ["//visibility:public"], +) diff --git a/tools/workspace/ibex/repository.bzl b/tools/workspace/ibex/repository.bzl index b1b8f0553dde..a70f12be7524 100644 --- a/tools/workspace/ibex/repository.bzl +++ b/tools/workspace/ibex/repository.bzl @@ -1,106 +1,18 @@ # -*- mode: python -*- -load( - "@drake//tools/workspace:deb.bzl", - "setup_new_deb_archive", -) -load( - "@drake//tools/workspace:execute.bzl", - "execute_or_fail", -) -load( - "@drake//tools/workspace:os.bzl", - "determine_os", -) -load( - "@drake//tools/workspace:pkg_config.bzl", - "setup_pkg_config_repository", -) -load(":version.bzl", "IBEX_VERSION") +load("@drake//tools/workspace:github.bzl", "github_archive") -def _rename_so(repo_ctx, directory, old_name): - # Rename directory/old_name to be -ldrake_foo instead of -lfoo. - old_name[:3] == "lib" or fail("Bad library name " + old_name) - execute_or_fail(repo_ctx, [ - "mv", - directory + "/lib" + old_name[3:], - directory + "/libdrake_" + old_name[3:], - ]) - -def _impl(repo_ctx): - os_result = determine_os(repo_ctx) - if os_result.error != None: - fail(os_result.error) - if os_result.is_macos: - result = setup_pkg_config_repository(repo_ctx) - if result.error != None: - fail("Unable to complete setup for @{} repository: {}".format( - # (forced line break) - repo_ctx.name, - result.error, - )) - return - if os_result.ubuntu_release not in ["18.04", "20.04"]: - fail("Operating system is NOT supported {}".format(os_result)) - result = setup_new_deb_archive(repo_ctx) - if result.error != None: - fail("Unable to complete setup for @{} repository: {}".format( - # (forced line break) - repo_ctx.name, - result.error, - )) - - # Avoid using upstream library names for our custom build. - _rename_so( - repo_ctx, - "opt/libibex/{}/lib".format(IBEX_VERSION), - "libibex.so", +def ibex_repository( + name, + mirrors = None): + github_archive( + name = name, + # TODO(jwnimmer-tri) Switch to upstream ibex-lib (using local patch + # files if necessary). + repository = "dreal-deps/ibex-lib", + # As discussed in #15872, we need ibex < 2.8.7 for CLP support. + commit = "ibex-2.8.6_4", + sha256 = "172f2cf8ced69bd2e30be448170655878735af7d0bf6d2fef44b14215c8b1a49", # noqa + build_file = "@drake//tools/workspace/ibex:package.BUILD.bazel", + mirrors = mirrors, ) - -ibex_repository = repository_rule( - # TODO(jamiesnape): Pass down licenses to setup_pkg_config_repository. - attrs = { - "modname": attr.string(default = "ibex"), - # The next two attributes are only used for macOS. They are documented - # in the pkg_config_repository rule. - "pkg_config_paths": attr.string_list( - default = [ - "/usr/local/opt/clp/lib/pkgconfig", - "/usr/local/opt/coinutils/lib/pkgconfig", - "/usr/local/opt/ibex@{}/share/pkgconfig".format(IBEX_VERSION), - ], - ), - # On macOS we are using IBEX from homebrew, so Drake's installation - # script doesn't need to do anything extra. - "build_epilog": attr.string( - default = "filegroup(name = \"install\") # Nothing.", - ), - # All of the below attributes are only used for Ubuntu. They are - # documented in the new_deb_archive rule. - "mirrors": attr.string_list( - default = [ - "https://drake-apt.csail.mit.edu/bionic/pool/main", - "https://s3.amazonaws.com/drake-apt/bionic/pool/main", - ], - ), - "filenames": attr.string_list( - default = [ - "libi/libibex-dev/libibex-dev_{}.20210826124156.git26eeeaae51b0f1518cbab9751c872b83801dbec8~18.04_amd64.deb".format(IBEX_VERSION), # noqa - ], - ), - "sha256s": attr.string_list( - default = [ - "058ba0d538927c0e25b79cd73906abb1a15dc7ffef4f8407d8c6ae4add940e22", # noqa - ], - ), - "build_file": attr.label( - default = "@drake//tools/workspace/ibex:package-ubuntu.BUILD.bazel", # noqa - ), - }, - configure = True, - implementation = _impl, -) - -"""Provides a library target for @ibex//:ibex. On macOS, uses homebrew; on -Ubuntu, downloads a *.deb file and unpacks it into the workspace. -""" diff --git a/tools/workspace/ibex/version.bzl b/tools/workspace/ibex/version.bzl deleted file mode 100644 index 3b5c4eb44aef..000000000000 --- a/tools/workspace/ibex/version.bzl +++ /dev/null @@ -1,4 +0,0 @@ -# -*- mode: python -*- - -# N.B. Matches drake/setup/mac/binary_distribution/Brewfile. -IBEX_VERSION = "2.7.4"