From 652605dcdee4f83d9fb1749048bc4e3b83eb08be Mon Sep 17 00:00:00 2001 From: Robert Gottlieb Date: Fri, 31 May 2024 15:47:46 -0400 Subject: [PATCH] Update Tests and README --- .gitignore | 1 + LICENSE => LICENSE.md | 0 README.md | 24 ++- test/addition.jl | 36 +++++ test/division.jl | 324 +++++++++++++++++++++++++++++++++++++++++ test/exp.jl | 45 ++++++ test/multiplication.jl | 322 ++++++++++++++++++++++++++++++++++++++++ test/power.jl | 48 ++++++ test/runtests.jl | 227 ++--------------------------- 9 files changed, 805 insertions(+), 222 deletions(-) rename LICENSE => LICENSE.md (100%) create mode 100644 test/addition.jl create mode 100644 test/division.jl create mode 100644 test/exp.jl create mode 100644 test/multiplication.jl create mode 100644 test/power.jl diff --git a/.gitignore b/.gitignore index 23dfb0b..cdc5b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ +src/transform/storage/ Manifest.toml \ No newline at end of file diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/README.md b/README.md index acceb0f..20bcbe5 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,21 @@ # SourceCodeMcCormick.jl -This package is an experimental approach to use source-code transformation to apply McCormick relaxations -to symbolic functions for use in deterministic global optimization. While packages like `McCormick.jl` [1] -take set-valued McCormick objects and utilize McCormick relaxation rules to overload standard math operations, -`SourceCodeMcCormick.jl` (SCMC) aims to interpret symbolic expressions, apply generalized McCormick rules, -create source code that computes the McCormick relaxations and natural interval extension of the input, -and compile the source code into functions that return pointwise values of the natural interval extension -and convex/concave relaxations. This functionality is designed to be used for both algebraic and dynamic -(in development) systems. +| **PSOR Lab** | **Build Status** | +|:------------:|:-----------------------------------------------------------------------------------------------:| +| [![](https://img.shields.io/badge/Developed_by-PSOR_Lab-342674)](https://psor.uconn.edu/) | [![Build Status](https://github.com/PSORLab/SourceCodeMcCormick.jl/workflows/CI/badge.svg?branch=master)](https://github.com/PSORLab/SourceCodeMcCormick.jl/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/PSORLab/SourceCodeMcCormick.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/PSORLab/SourceCodeMcCormick.jl)| + + +This package uses source-code transformation to construct McCormick-based relaxations. Expressions composed +of `Symbolics.jl`-type variables can be passed into `SourceCodeMcCormick.jl` (`SCMC`) functions, after which +the expressions are factored, generalized McCormick relaxation rules and inclusion monotonic interval +extensions are applied to the factors, and the factors are recombined symbolically to create expressions +representing convex and concave relaxations and inclusion monotonic interval extensions of the original +expression. The new expressions are compiled into functions that return pointwise values of these +four elements, which can be used in, e.g., a branch-and-bound routine. These functions can be used with +floating-point values, vectors of floating-point values, or CUDA arrays of floating point values (using +`CUDA.jl`) to return outputs of the same type. + + ## Algebraic Systems diff --git a/test/addition.jl b/test/addition.jl new file mode 100644 index 0000000..141f060 --- /dev/null +++ b/test/addition.jl @@ -0,0 +1,36 @@ + +@testset "Addition" begin + @variables x, y + + to_compute = y+5 + posreal_add_cv, posreal_add_cc, posreal_add_lo, posreal_add_hi, posreal_order = all_evaluators(to_compute) + + to_compute = y-5 + negreal_add_cv, negreal_add_cc, negreal_add_lo, negreal_add_hi, negreal_order = all_evaluators(to_compute) + + to_compute = x+y + add_cv, add_cc, add_lo, add_hi, add_order = all_evaluators(to_compute) + + # Addition rules are very simple; each component of the McCormick expansion + # is added separately. No need to test more than one type of McCormick object + # from McCormick.jl + y_1D = MC{1,NS}(1.0, 2.0, Interval(0.0, 3.0), SVector{1, Float64}(0.5), SVector{1,Float64}(2.5), false) + + xMC = MC{2,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + yMC = MC{2,NS}(-0.5, 0.5, Interval(-2.0, 2.0), SVector{2, Float64}(-0.5, 0.5), SVector{2, Float64}(1.0, -1.0), false) + + @test abs(eval_check(posreal_add_cv, y_1D) - (y_1D+5).cv) <= 1E-15 + @test abs(eval_check(posreal_add_cc, y_1D) - (y_1D+5).cc) <= 1E-15 + @test abs(eval_check(posreal_add_lo, y_1D) - (y_1D+5).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_add_hi, y_1D) - (y_1D+5).Intv.hi) <= 1E-15 + + @test abs(eval_check(negreal_add_cv, y_1D) - (y_1D-5).cv) <= 1E-15 + @test abs(eval_check(negreal_add_cc, y_1D) - (y_1D-5).cc) <= 1E-15 + @test abs(eval_check(negreal_add_lo, y_1D) - (y_1D-5).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_add_hi, y_1D) - (y_1D-5).Intv.hi) <= 1E-15 + + @test abs(eval_check(add_cv, xMC, yMC) - (xMC+yMC).cv) <= 1E-15 + @test abs(eval_check(add_cc, xMC, yMC) - (xMC+yMC).cc) <= 1E-15 + @test abs(eval_check(add_lo, xMC, yMC) - (xMC+yMC).Intv.lo) <= 1E-15 + @test abs(eval_check(add_hi, xMC, yMC) - (xMC+yMC).Intv.hi) <= 1E-15 +end diff --git a/test/division.jl b/test/division.jl new file mode 100644 index 0000000..148b432 --- /dev/null +++ b/test/division.jl @@ -0,0 +1,324 @@ +mid_expr(x, y, z) = IfElse.ifelse(x >= y, IfElse.ifelse(y >= z, y, IfElse.ifelse(y == x, y, IfElse.ifelse(z >= x, x, z))), + IfElse.ifelse(z >= y, y, IfElse.ifelse(x >= z, x, z))) + +function div_cv_case(xcv, xcc, xL, xU, ycv, ycc, yL, yU) + yL_inv = inv(yU) + yU_inv = inv(yL) + ycv_inv = ifelse(yL > 0.0, ifelse(yU <= ycv, 1.0 ./ ycv, + ifelse(yU >= ycc, 1.0 ./ ycc, 1.0 ./ yU)), + ifelse(yU < 0.0, ifelse(yL == yU, mid_expr(ycc, ycv, yL).^(-1), + ((yL.^(-1))*(yU - mid_expr(ycc, ycv, yL)) + (yU.^(-1))*(mid_expr(ycc, ycv, yL) - yL))./(yU - yL)), + NaN)) + ycc_inv = ifelse(yL > 0.0, ifelse(yL <= ycv, (yU + yL - ycv)./(yL*yU), + ifelse(yL >= ycc, (yU + yL - ycc)./(yL*yU), 1.0 ./ yL)), + ifelse(yU < 0.0, mid_expr(ycc, ycv, yU).^(-1), + NaN)) + if xL >= 0.0 + if yL >= 0.0 + if yU_inv*xcv + xU*ycv_inv - xU*yU_inv > yL_inv*xcv + xL*ycv_inv - xL*yL_inv + return 1 + else + return 2 + end + elseif yU <= 0.0 + if (-yU_inv)*xcc + xU*(-ycv_inv) - xU*(-yU_inv) > (-yL_inv)*xcc + xL*(-ycv_inv) - xL*(-yL_inv) + return 3 + else + return 4 + end + else + return 5 + end + elseif xU <= 0.0 + if yL >= 0.0 + if yL_inv*(-xcv) + (-xL)*ycc_inv - (-xL)*yL_inv > yU_inv*(-xcv) + (-xU)*ycc_inv - (-xU)*yU_inv + return 6 + else + return 7 + end + elseif yU <= 0.0 + if yL_inv*xcc + xL*ycc_inv - xL*yL_inv > yU_inv*xcc + xU*ycc_inv - xU*yU_inv + return 8 + else + return 9 + end + else + return 10 + end + else + if yL >= 0.0 + if xU*ycv_inv + yU_inv*xcv - yU_inv*xU > xL*ycc_inv + yL_inv*xcv - yL_inv*xL + return 11 + else + return 12 + end + elseif yU <= 0.0 + if xL*(-ycc_inv) + (-yL_inv)*xcc - (-yL_inv)*xL > xU*(-ycv_inv) + (-yU_inv)*xcc - (-yU_inv)*xU + return 13 + else + return 14 + end + else + return 15 + end + end +end +function div_cc_case(xcv, xcc, xL, xU, ycv, ycc, yL, yU) + yL_inv = inv(yU) + yU_inv = inv(yL) + ycv_inv = ifelse(yL > 0.0, ifelse(yU <= ycv, 1.0 ./ ycv, + ifelse(yU >= ycc, 1.0 ./ ycc, 1.0 ./ yU)), + ifelse(yU < 0.0, ifelse(yL == yU, mid_expr(ycc, ycv, yL).^(-1), + ((yL.^(-1))*(yU - mid_expr(ycc, ycv, yL)) + (yU.^(-1))*(mid_expr(ycc, ycv, yL) - yL))./(yU - yL)), + NaN)) + ycc_inv = ifelse(yL > 0.0, ifelse(yL <= ycv, (yU + yL - ycv)./(yL*yU), + ifelse(yL >= ycc, (yU + yL - ycc)./(yL*yU), 1.0 ./ yL)), + ifelse(yU < 0.0, mid_expr(ycc, ycv, yU).^(-1), + NaN)) + if xL >= 0.0 + if yL >= 0.0 + if yL_inv*xcc + xU*ycc_inv - xU*yL_inv > yU_inv*xcc + xL*ycc_inv - xL*yU_inv + return 1 + else + return 2 + end + elseif yU <= 0.0 + if (-yL_inv)*xcv + xU*(-ycc_inv) - xU*(-yL_inv) > (-yU_inv)*xcv + xL*(-ycc_inv) - xL*(-yU_inv) + return 3 + else + return 4 + end + else + return 5 + end + elseif xU <= 0.0 + if yL >= 0.0 + if yU_inv*(-xcc) + (-xL)*ycv_inv - (-xL)*yU_inv > yL_inv*(-xcc) + (-xU)*ycv_inv - (-xU)*yL_inv + return 6 + else + return 7 + end + elseif yU <= 0.0 + if yU_inv*xcv + xL*ycv_inv - xL*yU_inv > yL_inv*xcv + xU*ycv_inv - xU*yL_inv + return 8 + else + return 9 + end + else + return 10 + end + else + if yL >= 0.0 + if xL*ycv_inv + yU_inv*xcc - yU_inv*xL > xU*ycc_inv + yL_inv*xcc - yL_inv*xU + return 11 + else + return 12 + end + elseif yU <= 0.0 + if xU*(-ycc_inv) + (-yL_inv)*xcv - (-yL_inv)*xU > xL*(-ycv_inv) + (-yU_inv)*xcv - (-yU_inv)*xL + return 13 + else + return 14 + end + else + return 15 + end + end +end +div_cv_case(A::MC, B::MC) = div_cv_case(A.cv, A.cc, A.Intv.lo, A.Intv.hi, B.cv, B.cc, B.Intv.lo, B.Intv.hi) +div_cc_case(A::MC, B::MC) = div_cv_case(A.cv, A.cc, A.Intv.lo, A.Intv.hi, B.cv, B.cc, B.Intv.lo, B.Intv.hi) + +# For division, need to test all cases with MC*MC, then several cases with Real types +@testset "Division" begin + @variables x, y + + # Real divided by a McCormick object + pos = MC{1,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + mix = MC{1,NS}(-0.5, 0.5, Interval(-2.0, 2.0), SVector{1, Float64}(-0.5), SVector{1, Float64}(1.0), false) + neg = -pos + + to_compute = 5.0/y + posreal_div_cv, posreal_div_cc, posreal_div_lo, posreal_div_hi, posreal_order = all_evaluators(to_compute) + + to_compute = -5.0/y + negreal_div_cv, negreal_div_cc, negreal_div_lo, negreal_div_hi, negreal_order = all_evaluators(to_compute) + + # pos/pos + @test abs(eval_check(posreal_div_cv, pos) - (5.0/pos).cv) <= 1E-15 + @test abs(eval_check(posreal_div_cc, pos) - (5.0/pos).cc) <= 1E-15 + @test abs(eval_check(posreal_div_lo, pos) - (5.0/pos).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_div_hi, pos) - (5.0/pos).Intv.hi) <= 1E-15 + + # pos/mix + @test isnan(eval_check(posreal_div_cv, mix)) + @test isnan((5.0/mix).cv) + @test isnan(eval_check(posreal_div_cc, mix)) + @test isnan((5.0/mix).cc) + @test isnan(eval_check(posreal_div_lo, mix)) + @test isnan((5.0/mix).Intv.lo) + @test isnan(eval_check(posreal_div_hi, mix)) + @test isnan((5.0/mix).Intv.hi) + + # pos/neg + @test abs(eval_check(posreal_div_cv, neg) - (5.0/neg).cv) <= 1E-15 + @test abs(eval_check(posreal_div_cc, neg) - (5.0/neg).cc) <= 1E-15 + @test abs(eval_check(posreal_div_lo, neg) - (5.0/neg).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_div_hi, neg) - (5.0/neg).Intv.hi) <= 1E-15 + + # neg/pos + @test abs(eval_check(negreal_div_cv, pos) - (-5.0/pos).cv) <= 1E-15 + @test abs(eval_check(negreal_div_cc, pos) - (-5.0/pos).cc) <= 1E-15 + @test abs(eval_check(negreal_div_lo, pos) - (-5.0/pos).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_div_hi, pos) - (-5.0/pos).Intv.hi) <= 1E-15 + + # neg/mix + @test isnan(eval_check(negreal_div_cv, mix)) + @test isnan((-5.0/mix).cv) + @test isnan(eval_check(negreal_div_cc, mix)) + @test isnan((-5.0/mix).cc) + @test isnan(eval_check(negreal_div_lo, mix)) + @test isnan((-5.0/mix).Intv.lo) + @test isnan(eval_check(negreal_div_hi, mix)) + @test isnan((-5.0/mix).Intv.hi) + + # neg/neg + @test abs(eval_check(negreal_div_cv, neg) - (-5.0/neg).cv) <= 1E-15 + @test abs(eval_check(negreal_div_cc, neg) - (-5.0/neg).cc) <= 1E-15 + @test abs(eval_check(negreal_div_lo, neg) - (-5.0/neg).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_div_hi, neg) - (-5.0/neg).Intv.hi) <= 1E-15 + + # Note: McCormick object divided by a real automatically converts from (MC/real) to (MC*(real^-1)) + # through Symbolics.jl, so this is simply multiplication. + + + # McCormick object divided by a McCormick object. Verify that cases are satisfied before + # checking the solution using the cv/cc case checker functions + pos = MC{2,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + pos_lo = MC{2,NS}(0.5, 0.5, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + pos_hi = MC{2,NS}(1.8, 1.9, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + mix = MC{2,NS}(-0.5, 0.5, Interval(-2.0, 2.0), SVector{2, Float64}(-0.5, 0.5), SVector{2, Float64}(1.0, -1.0), false) + neg = -pos + neg_lo = -pos_lo + neg_hi = -pos_hi + + @variables x, y + to_compute = x/y + div_cv, div_cc, div_lo, div_hi, order = all_evaluators(to_compute) + + @test div_cv_case(pos, pos_lo) == 1 + @test div_cc_case(pos, pos_lo) == 1 + @test abs(eval_check(div_cv, pos, pos_lo) - (pos/pos_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, pos, pos_lo) - (pos/pos_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, pos, pos_lo) - (pos/pos_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, pos, pos_lo) - (pos/pos_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(pos, pos_hi) == 2 + @test div_cc_case(pos, pos_hi) == 2 + @test abs(eval_check(div_cv, pos, pos_hi) - (pos/pos_hi).cv) <= 1E-15 + @test abs(eval_check(div_cc, pos, pos_hi) - (pos/pos_hi).cc) <= 1E-15 + @test abs(eval_check(div_lo, pos, pos_hi) - (pos/pos_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, pos, pos_hi) - (pos/pos_hi).Intv.hi) <= 1E-15 + + @test div_cv_case(pos, neg_lo) == 3 + @test div_cc_case(pos, neg_lo) == 3 + @test abs(eval_check(div_cv, pos, neg_lo) - (pos/neg_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, pos, neg_lo) - (pos/neg_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, pos, neg_lo) - (pos/neg_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, pos, neg_lo) - (pos/neg_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(pos, neg) == 4 + @test div_cc_case(pos, neg) == 4 + @test abs(eval_check(div_cv, pos, neg) - (pos/neg).cv) <= 1E-15 + @test abs(eval_check(div_cc, pos, neg) - (pos/neg).cc) <= 1E-15 + @test abs(eval_check(div_lo, pos, neg) - (pos/neg).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, pos, neg) - (pos/neg).Intv.hi) <= 1E-15 + + @test div_cv_case(pos_hi, mix) == 5 + @test div_cc_case(pos_hi, mix) == 5 + @test isnan(eval_check(div_cv, pos_hi, mix)) + @test isnan((pos_hi/mix).cv) + @test isnan(eval_check(div_cc, pos_hi, mix)) + @test isnan((pos_hi/mix).cc) + @test isnan(eval_check(div_lo, pos_hi, mix)) + @test isnan((pos_hi/mix).Intv.lo) + @test isnan(eval_check(div_hi, pos_hi, mix)) + @test isnan((pos_hi/mix).Intv.hi) + + @test div_cv_case(neg, pos_lo) == 6 + @test div_cc_case(neg, pos_lo) == 6 + @test abs(eval_check(div_cv, neg, pos_lo) - (neg/pos_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, neg, pos_lo) - (neg/pos_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, neg, pos_lo) - (neg/pos_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, neg, pos_lo) - (neg/pos_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(neg, pos_hi) == 7 + @test div_cc_case(neg, pos_hi) == 7 + @test abs(eval_check(div_cv, neg, pos_hi) - (neg/pos_hi).cv) <= 1E-15 + @test abs(eval_check(div_cc, neg, pos_hi) - (neg/pos_hi).cc) <= 1E-15 + @test abs(eval_check(div_lo, neg, pos_hi) - (neg/pos_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, neg, pos_hi) - (neg/pos_hi).Intv.hi) <= 1E-15 + + @test div_cv_case(neg, neg_lo) == 8 + @test div_cc_case(neg, neg_lo) == 8 + @test abs(eval_check(div_cv, neg, neg_lo) - (neg/neg_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, neg, neg_lo) - (neg/neg_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, neg, neg_lo) - (neg/neg_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, neg, neg_lo) - (neg/neg_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(neg, neg_hi) == 9 + @test div_cc_case(neg, neg_hi) == 9 + @test abs(eval_check(div_cv, neg, neg_hi) - (neg/neg_hi).cv) <= 1E-15 + @test abs(eval_check(div_cc, neg, neg_hi) - (neg/neg_hi).cc) <= 1E-15 + @test abs(eval_check(div_lo, neg, neg_hi) - (neg/neg_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, neg, neg_hi) - (neg/neg_hi).Intv.hi) <= 1E-15 + + @test div_cv_case(neg, mix) == 10 + @test div_cc_case(neg, mix) == 10 + @test isnan(eval_check(div_cv, neg, mix)) + @test isnan((neg/mix).cv) + @test isnan(eval_check(div_cc, neg, mix)) + @test isnan((neg/mix).cc) + @test isnan(eval_check(div_lo, neg, mix)) + @test isnan((neg/mix).Intv.lo) + @test isnan(eval_check(div_hi, neg, mix)) + @test isnan((neg/mix).Intv.hi) + + @test div_cv_case(mix, pos_lo) == 11 + @test div_cc_case(mix, pos_lo) == 11 + @test abs(eval_check(div_cv, mix, pos_lo) - (mix/pos_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, mix, pos_lo) - (mix/pos_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, mix, pos_lo) - (mix/pos_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, mix, pos_lo) - (mix/pos_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(mix, pos) == 12 + @test div_cc_case(mix, pos) == 12 + @test abs(eval_check(div_cv, mix, pos) - (mix/pos).cv) <= 1E-15 + @test abs(eval_check(div_cc, mix, pos) - (mix/pos).cc) <= 1E-15 + @test abs(eval_check(div_lo, mix, pos) - (mix/pos).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, mix, pos) - (mix/pos).Intv.hi) <= 1E-15 + + @test div_cv_case(mix, neg) == 13 + @test div_cc_case(mix, neg) == 13 + @test abs(eval_check(div_cv, mix, neg) - (mix/neg).cv) <= 1E-15 + @test abs(eval_check(div_cc, mix, neg) - (mix/neg).cc) <= 1E-15 + @test abs(eval_check(div_lo, mix, neg) - (mix/neg).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, mix, neg) - (mix/neg).Intv.hi) <= 1E-15 + + @test div_cv_case(mix, neg_lo) == 14 + @test div_cc_case(mix, neg_lo) == 14 + @test abs(eval_check(div_cv, mix, neg_lo) - (mix/neg_lo).cv) <= 1E-15 + @test abs(eval_check(div_cc, mix, neg_lo) - (mix/neg_lo).cc) <= 1E-15 + @test abs(eval_check(div_lo, mix, neg_lo) - (mix/neg_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(div_hi, mix, neg_lo) - (mix/neg_lo).Intv.hi) <= 1E-15 + + @test div_cv_case(mix, mix) == 15 + @test div_cc_case(mix, mix) == 15 + @test isnan(eval_check(div_cv, mix, mix)) + @test isnan((mix/mix).cv) + @test isnan(eval_check(div_cc, mix, mix)) + @test isnan((mix/mix).cc) + @test isnan(eval_check(div_lo, mix, mix)) + @test isnan((mix/mix).Intv.lo) + @test isnan(eval_check(div_hi, mix, mix)) + @test isnan((mix/mix).Intv.hi) +end \ No newline at end of file diff --git a/test/exp.jl b/test/exp.jl new file mode 100644 index 0000000..2915208 --- /dev/null +++ b/test/exp.jl @@ -0,0 +1,45 @@ + +@testset "Exponential" begin + @variables x + + to_compute = exp(x) + exp_cv, exp_cc, exp_lo, exp_hi, exp_order = all_evaluators(to_compute) + + # Check positive/negative/mixed cases, as well as some unique cases where values are the same + pos = MC{1,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + mix = MC{1,NS}(-0.5, 0.5, Interval(-2.0, 3.0), SVector{1, Float64}(-0.5), SVector{1, Float64}(1.0), false) + neg = -pos + pos_same1 = MC{1,NS}(1.0, 1.0, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + pos_same2 = MC{1,NS}(1.0, 1.0, Interval(1.0, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + pos_same3 = MC{1,NS}(1.0, 1.0, Interval(1.0, 1.0), SVector{1, Float64}(1.0), SVector{1, Float64}(1.0), false) + + @test abs(eval_check(exp_cv, pos) - exp(pos).cv) <= 1E-15 + @test abs(eval_check(exp_cc, pos) - exp(pos).cc) <= 1E-15 + @test abs(eval_check(exp_lo, pos) - exp(pos).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, pos) - exp(pos).Intv.hi) <= 1E-15 + + @test abs(eval_check(exp_cv, mix) - exp(mix).cv) <= 1E-15 + @test abs(eval_check(exp_cc, mix) - exp(mix).cc) <= 1E-15 + @test abs(eval_check(exp_lo, mix) - exp(mix).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, mix) - exp(mix).Intv.hi) <= 1E-15 + + @test abs(eval_check(exp_cv, neg) - exp(neg).cv) <= 1E-15 + @test abs(eval_check(exp_cc, neg) - exp(neg).cc) <= 1E-15 + @test abs(eval_check(exp_lo, neg) - exp(neg).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, neg) - exp(neg).Intv.hi) <= 1E-15 + + @test abs(eval_check(exp_cv, pos_same1) - exp(pos_same1).cv) <= 1E-15 + @test abs(eval_check(exp_cc, pos_same1) - exp(pos_same1).cc) <= 1E-15 + @test abs(eval_check(exp_lo, pos_same1) - exp(pos_same1).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, pos_same1) - exp(pos_same1).Intv.hi) <= 1E-15 + + @test abs(eval_check(exp_cv, pos_same2) - exp(pos_same2).cv) <= 1E-15 + @test abs(eval_check(exp_cc, pos_same2) - exp(pos_same2).cc) <= 1E-15 + @test abs(eval_check(exp_lo, pos_same2) - exp(pos_same2).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, pos_same2) - exp(pos_same2).Intv.hi) <= 1E-15 + + @test abs(eval_check(exp_cv, pos_same3) - exp(pos_same3).cv) <= 1E-15 + @test abs(eval_check(exp_cc, pos_same3) - exp(pos_same3).cc) <= 1E-15 + @test abs(eval_check(exp_lo, pos_same3) - exp(pos_same3).Intv.lo) <= 1E-15 + @test abs(eval_check(exp_hi, pos_same3) - exp(pos_same3).Intv.hi) <= 1E-15 +end diff --git a/test/multiplication.jl b/test/multiplication.jl new file mode 100644 index 0000000..344a1d5 --- /dev/null +++ b/test/multiplication.jl @@ -0,0 +1,322 @@ + +function mult_cv_case(xcv, xcc, xL, xU, ycv, ycc, yL, yU) + if xL >= 0.0 + if yL >= 0.0 + if yU*xcv + xU*ycv - xU*yU > yL*xcv + xL*ycv - xL*yL + return 1 + else + return 2 + end + elseif yU <= 0.0 + if (-yU)*xcc + xU*(-ycv) - xU*(-yU) > (-yL)*xcc + xL*(-ycv) - xL*(-yL) + return 3 + else + return 4 + end + else + if yU*xcv + xU*ycv - xU*yU > yL*xcc + xL*ycv - xL*yL + return 5 + else + return 6 + end + end + elseif xU <= 0.0 + if yL >= 0.0 + if yL*(-xcv) + (-xL)*ycc - (-xL)*yL > yU*(-xcv) + (-xU)*ycc - (-xU)*yU + return 7 + else + return 8 + end + elseif yU <= 0.0 + if yL*xcc + xL*ycc - xL*yL > yU*xcc + xU*ycc - xU*yU + return 9 + else + return 10 + end + else + if yL*(-xcc) + (-xL)*ycc - (-xL)*yL > yU*(-xcv) + (-xU)*ycc - (-xU)*yU + return 11 + else + return 12 + end + end + else + if yL >= 0.0 + if xU*ycv + yU*xcv - yU*xU > xL*ycc + yL*xcv - yL*xL + return 13 + else + return 14 + end + elseif yU <= 0.0 + if xL*(-ycc) + (-yL)*xcc - (-yL)*xL > xU*(-ycv) + (-yU)*xcc - (-yU)*xU + return 15 + else + return 16 + end + else + if yU*xcv + xU*ycv - xU*yU > yL*xcc + xL*ycc - xL*yL + return 17 + else + return 18 + end + end + end +end +function mult_cc_case(xcv, xcc, xL, xU, ycv, ycc, yL, yU) + if xL >= 0.0 + if yL >= 0.0 + if yL*xcc + xU*ycc - xU*yL > yU*xcc + xL*ycc - xL*yU + return 1 + else + return 2 + end + elseif yU <= 0.0 + if (-yL)*xcv + xU*(-ycc) - xU*(-yL) > (-yU)*xcv + xL*(-ycc) - xL*(-yU) + return 3 + else + return 4 + end + else + if yL*xcv + xU*ycc - xU*yL > yU*xcc + xL*ycc - xL*yU + return 5 + else + return 6 + end + end + elseif xU <= 0.0 + if yL >= 0.0 + if yU*(-xcc) + (-xL)*ycv - (-xL)*yU > yL*(-xcc) + (-xU)*ycv - (-xU)*yL + return 7 + else + return 8 + end + elseif yU <= 0.0 + if yU*xcv + xL*ycv - xL*yU > yL*xcv + xU*ycv - xU*yL + return 9 + else + return 10 + end + else + if yU*(-xcc) + (-xL)*ycv - (-xL)*yU > yL*(-xcv) + (-xU)*ycv - (-xU)*yL + return 11 + else + return 12 + end + end + else + if yL >= 0.0 + if xL*ycv + yU*xcc - yU*xL > xU*ycc + yL*xcc - yL*xU + return 13 + else + return 14 + end + elseif yU <= 0.0 + if xU*(-ycc) + (-yL)*xcv - (-yL)*xU > xL*(-ycv) + (-yU)*xcv - (-yU)*xL + return 15 + else + return 16 + end + else + if yL*xcv + xU*ycc - xU*yL > yU*xcc + xL*ycv - xL*yU + return 17 + else + return 18 + end + end + end +end +mult_cv_case(A::MC, B::MC) = mult_cv_case(A.cv, A.cc, A.Intv.lo, A.Intv.hi, B.cv, B.cc, B.Intv.lo, B.Intv.hi) +mult_cc_case(A::MC, B::MC) = mult_cv_case(A.cv, A.cc, A.Intv.lo, A.Intv.hi, B.cv, B.cc, B.Intv.lo, B.Intv.hi) + +# For multiplication, need to test all cases with MC*MC, then several cases with Real types +@testset "Multiplication" begin + @variables x, y + + # McCormick object times a real + pos = MC{1,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + mix = MC{1,NS}(-0.5, 0.5, Interval(-2.0, 2.0), SVector{1, Float64}(-0.5), SVector{1, Float64}(1.0), false) + neg = -pos + + to_compute = 5.0*y + posreal_mult_cv, posreal_mult_cc, posreal_mult_lo, posreal_mult_hi, posreal_order = all_evaluators(to_compute) + + to_compute = -5.0*y + negreal_mult_cv, negreal_mult_cc, negreal_mult_lo, negreal_mult_hi, negreal_order = all_evaluators(to_compute) + + # pos*pos + @test abs(eval_check(posreal_mult_cv, pos) - (5.0*pos).cv) <= 1E-15 + @test abs(eval_check(posreal_mult_cc, pos) - (5.0*pos).cc) <= 1E-15 + @test abs(eval_check(posreal_mult_lo, pos) - (5.0*pos).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_mult_hi, pos) - (5.0*pos).Intv.hi) <= 1E-15 + + # pos*mix + @test abs(eval_check(posreal_mult_cv, mix) - (5.0*mix).cv) <= 1E-15 + @test abs(eval_check(posreal_mult_cc, mix) - (5.0*mix).cc) <= 1E-15 + @test abs(eval_check(posreal_mult_lo, mix) - (5.0*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_mult_hi, mix) - (5.0*mix).Intv.hi) <= 1E-15 + + # pos*neg + @test abs(eval_check(posreal_mult_cv, neg) - (5.0*neg).cv) <= 1E-15 + @test abs(eval_check(posreal_mult_cc, neg) - (5.0*neg).cc) <= 1E-15 + @test abs(eval_check(posreal_mult_lo, neg) - (5.0*neg).Intv.lo) <= 1E-15 + @test abs(eval_check(posreal_mult_hi, neg) - (5.0*neg).Intv.hi) <= 1E-15 + + # neg*pos + @test abs(eval_check(negreal_mult_cv, pos) - (-5.0*pos).cv) <= 1E-15 + @test abs(eval_check(negreal_mult_cc, pos) - (-5.0*pos).cc) <= 1E-15 + @test abs(eval_check(negreal_mult_lo, pos) - (-5.0*pos).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_mult_hi, pos) - (-5.0*pos).Intv.hi) <= 1E-15 + + # neg*mix + @test abs(eval_check(negreal_mult_cv, mix) - (-5.0*mix).cv) <= 1E-15 + @test abs(eval_check(negreal_mult_cc, mix) - (-5.0*mix).cc) <= 1E-15 + @test abs(eval_check(negreal_mult_lo, mix) - (-5.0*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_mult_hi, mix) - (-5.0*mix).Intv.hi) <= 1E-15 + + # neg*neg + @test abs(eval_check(negreal_mult_cv, neg) - (-5.0*neg).cv) <= 1E-15 + @test abs(eval_check(negreal_mult_cc, neg) - (-5.0*neg).cc) <= 1E-15 + @test abs(eval_check(negreal_mult_lo, neg) - (-5.0*neg).Intv.lo) <= 1E-15 + @test abs(eval_check(negreal_mult_hi, neg) - (-5.0*neg).Intv.hi) <= 1E-15 + + + # McCormick object times a McCormick object. Verify that cases are satisfied before + # checking the solution using the cv/cc case checker functions + pos = MC{2,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + pos_hi = MC{2,NS}(1.8, 1.9, Interval(0.5, 2.0), SVector{2, Float64}(1.0, 2.0), SVector{2, Float64}(3.0, 4.0), false) + mix = MC{2,NS}(-0.5, 0.5, Interval(-2.0, 2.0), SVector{2, Float64}(-0.5, 0.5), SVector{2, Float64}(1.0, -1.0), false) + mix_lo = MC{2,NS}(-0.5, 0.5, Interval(-2.0, 1.0), SVector{2, Float64}(-0.5, 0.5), SVector{2, Float64}(1.0, -1.0), false) + mix_hi = MC{2,NS}(-0.5, 0.5, Interval(-2.0, 5.0), SVector{2, Float64}(-0.5, 0.5), SVector{2, Float64}(1.0, -1.0), false) + neg = -pos + neg_hi = -pos_hi + + @variables x, y + to_compute = x*y + mult_cv, mult_cc, mult_lo, mult_hi, order = all_evaluators(to_compute) + + @test mult_cv_case(pos, pos_hi) == 1 + @test mult_cc_case(pos, pos_hi) == 1 + @test abs(eval_check(mult_cv, pos, pos_hi) - (pos*pos_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos, pos_hi) - (pos*pos_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos, pos_hi) - (pos*pos_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos, pos_hi) - (pos*pos_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(pos, pos) == 2 + @test mult_cc_case(pos, pos) == 2 + @test abs(eval_check(mult_cv, pos, pos) - (pos*pos).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos, pos) - (pos*pos).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos, pos) - (pos*pos).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos, pos) - (pos*pos).Intv.hi) <= 1E-15 + + @test mult_cv_case(pos, neg_hi) == 3 + @test mult_cc_case(pos, neg_hi) == 3 + @test abs(eval_check(mult_cv, pos, neg_hi) - (pos*neg_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos, neg_hi) - (pos*neg_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos, neg_hi) - (pos*neg_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos, neg_hi) - (pos*neg_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(pos, neg) == 4 + @test mult_cc_case(pos, neg) == 4 + @test abs(eval_check(mult_cv, pos, neg) - (pos*neg).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos, neg) - (pos*neg).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos, neg) - (pos*neg).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos, neg) - (pos*neg).Intv.hi) <= 1E-15 + + @test mult_cv_case(pos_hi, mix) == 5 + @test mult_cc_case(pos_hi, mix) == 5 + @test abs(eval_check(mult_cv, pos_hi, mix) - (pos_hi*mix).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos_hi, mix) - (pos_hi*mix).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos_hi, mix) - (pos_hi*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos_hi, mix) - (pos_hi*mix).Intv.hi) <= 1E-15 + + @test mult_cv_case(pos, mix) == 6 + @test mult_cc_case(pos, mix) == 6 + @test abs(eval_check(mult_cv, pos, mix) - (pos*mix).cv) <= 1E-15 + @test abs(eval_check(mult_cc, pos, mix) - (pos*mix).cc) <= 1E-15 + @test abs(eval_check(mult_lo, pos, mix) - (pos*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, pos, mix) - (pos*mix).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg, pos_hi) == 7 + @test mult_cc_case(neg, pos_hi) == 7 + @test abs(eval_check(mult_cv, neg, pos_hi) - (neg*pos_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg, pos_hi) - (neg*pos_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg, pos_hi) - (neg*pos_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg, pos_hi) - (neg*pos_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg, pos) == 8 + @test mult_cc_case(neg, pos) == 8 + @test abs(eval_check(mult_cv, neg, pos) - (neg*pos).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg, pos) - (neg*pos).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg, pos) - (neg*pos).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg, pos) - (neg*pos).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg, neg_hi) == 9 + @test mult_cc_case(neg, neg_hi) == 9 + @test abs(eval_check(mult_cv, neg, neg_hi) - (neg*neg_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg, neg_hi) - (neg*neg_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg, neg_hi) - (neg*neg_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg, neg_hi) - (neg*neg_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg, neg) == 10 + @test mult_cc_case(neg, neg) == 10 + @test abs(eval_check(mult_cv, neg, neg) - (neg*neg).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg, neg) - (neg*neg).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg, neg) - (neg*neg).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg, neg) - (neg*neg).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg, mix) == 11 + @test mult_cc_case(neg, mix) == 11 + @test abs(eval_check(mult_cv, neg, mix) - (neg*mix).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg, mix) - (neg*mix).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg, mix) - (neg*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg, mix) - (neg*mix).Intv.hi) <= 1E-15 + + @test mult_cv_case(neg_hi, mix) == 12 + @test mult_cc_case(neg_hi, mix) == 12 + @test abs(eval_check(mult_cv, neg_hi, mix) - (neg_hi*mix).cv) <= 1E-15 + @test abs(eval_check(mult_cc, neg_hi, mix) - (neg_hi*mix).cc) <= 1E-15 + @test abs(eval_check(mult_lo, neg_hi, mix) - (neg_hi*mix).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, neg_hi, mix) - (neg_hi*mix).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, pos_hi) == 13 + @test mult_cc_case(mix, pos_hi) == 13 + @test abs(eval_check(mult_cv, mix, pos_hi) - (mix*pos_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, pos_hi) - (mix*pos_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, pos_hi) - (mix*pos_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, pos_hi) - (mix*pos_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, pos) == 14 + @test mult_cc_case(mix, pos) == 14 + @test abs(eval_check(mult_cv, mix, pos) - (mix*pos).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, pos) - (mix*pos).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, pos) - (mix*pos).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, pos) - (mix*pos).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, neg) == 15 + @test mult_cc_case(mix, neg) == 15 + @test abs(eval_check(mult_cv, mix, neg) - (mix*neg).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, neg) - (mix*neg).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, neg) - (mix*neg).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, neg) - (mix*neg).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, neg_hi) == 16 + @test mult_cc_case(mix, neg_hi) == 16 + @test abs(eval_check(mult_cv, mix, neg_hi) - (mix*neg_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, neg_hi) - (mix*neg_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, neg_hi) - (mix*neg_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, neg_hi) - (mix*neg_hi).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, mix_lo) == 17 + @test mult_cc_case(mix, mix_lo) == 17 + @test abs(eval_check(mult_cv, mix, mix_lo) - (mix*mix_lo).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, mix_lo) - (mix*mix_lo).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, mix_lo) - (mix*mix_lo).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, mix_lo) - (mix*mix_lo).Intv.hi) <= 1E-15 + + @test mult_cv_case(mix, mix_hi) == 18 + @test mult_cc_case(mix, mix_hi) == 18 + @test abs(eval_check(mult_cv, mix, mix_hi) - (mix*mix_hi).cv) <= 1E-15 + @test abs(eval_check(mult_cc, mix, mix_hi) - (mix*mix_hi).cc) <= 1E-15 + @test abs(eval_check(mult_lo, mix, mix_hi) - (mix*mix_hi).Intv.lo) <= 1E-15 + @test abs(eval_check(mult_hi, mix, mix_hi) - (mix*mix_hi).Intv.hi) <= 1E-15 +end diff --git a/test/power.jl b/test/power.jl new file mode 100644 index 0000000..db205b4 --- /dev/null +++ b/test/power.jl @@ -0,0 +1,48 @@ + +@testset "Power" begin + # NOTE: Currently only includes ^2 + @variables x + + to_compute = x^2 + pow2_cv, pow2_cc, pow2_lo, pow2_hi, pow2_order = all_evaluators(to_compute) + + # All cases for ^2 are very similar; check positive/negative/mixed, as well as some unique cases where values are the same + pos = MC{1,NS}(1.0, 1.5, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + mix = MC{1,NS}(-0.5, 0.5, Interval(-2.0, 3.0), SVector{1, Float64}(-0.5), SVector{1, Float64}(1.0), false) + neg = -pos + pos_same1 = MC{1,NS}(1.0, 1.0, Interval(0.5, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + pos_same2 = MC{1,NS}(1.0, 1.0, Interval(1.0, 2.0), SVector{1, Float64}(1.0), SVector{1, Float64}(3.0), false) + pos_same3 = MC{1,NS}(1.0, 1.0, Interval(1.0, 1.0), SVector{1, Float64}(1.0), SVector{1, Float64}(1.0), false) + + # ADD DEGENERATE POSITIVE/NEGATIVE/ZERO?? + + @test abs(eval_check(pow2_cv, pos) - (pos^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, pos) - (pos^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, pos) - (pos^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, pos) - (pos^2).Intv.hi) <= 1E-15 + + @test abs(eval_check(pow2_cv, mix) - (mix^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, mix) - (mix^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, mix) - (mix^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, mix) - (mix^2).Intv.hi) <= 1E-15 + + @test abs(eval_check(pow2_cv, neg) - (neg^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, neg) - (neg^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, neg) - (neg^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, neg) - (neg^2).Intv.hi) <= 1E-15 + + @test abs(eval_check(pow2_cv, pos_same1) - (pos_same1^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, pos_same1) - (pos_same1^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, pos_same1) - (pos_same1^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, pos_same1) - (pos_same1^2).Intv.hi) <= 1E-15 + + @test abs(eval_check(pow2_cv, pos_same2) - (pos_same2^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, pos_same2) - (pos_same2^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, pos_same2) - (pos_same2^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, pos_same2) - (pos_same2^2).Intv.hi) <= 1E-15 + + @test abs(eval_check(pow2_cv, pos_same3) - (pos_same3^2).cv) <= 1E-15 + @test abs(eval_check(pow2_cc, pos_same3) - (pos_same3^2).cc) <= 1E-15 + @test abs(eval_check(pow2_lo, pos_same3) - (pos_same3^2).Intv.lo) <= 1E-15 + @test abs(eval_check(pow2_hi, pos_same3) - (pos_same3^2).Intv.hi) <= 1E-15 +end diff --git a/test/runtests.jl b/test/runtests.jl index dcb15e8..683603a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,216 +1,15 @@ -using SourceCodeMcCormick, McCormick, Symbolics, Test +using SourceCodeMcCormick, McCormick, Symbolics, Test, IfElse function eval_check(eval_func, MC1, MC2) - return eval_func(MC1.cc, MC1.cv, MC1.Intv.hi, - MC1.Intv.lo, MC2.cc, MC2.cv, MC2.Intv.hi, MC2.Intv.lo) -end - -@testset "Multiplication" begin - @variables x, y - to_compute = x*y - mult_lo, mult_hi, mult_cv, mult_cc, order = all_evaluators(to_compute) - - xMC = MC{2,NS}(-2.0, Interval(-3.0, -1.0), 1) - yMC = MC{2,NS}(4.0, Interval(2.0, 6.0), 2) - zMC = 0.5*xMC*yMC - neg = zMC - mix = zMC + 4.0 - pos = zMC + 10.0 - - @test eval_check(mult_lo, neg, neg) == (neg*neg).Intv.lo - @test eval_check(mult_lo, neg, mix) == (neg*mix).Intv.lo - @test eval_check(mult_lo, neg, pos) == (neg*pos).Intv.lo - @test eval_check(mult_lo, mix, neg) == (mix*neg).Intv.lo - @test eval_check(mult_lo, mix, mix) == (mix*mix).Intv.lo - @test eval_check(mult_lo, mix, pos) == (mix*pos).Intv.lo - @test eval_check(mult_lo, pos, neg) == (pos*neg).Intv.lo - @test eval_check(mult_lo, pos, mix) == (pos*mix).Intv.lo - @test eval_check(mult_lo, pos, pos) == (pos*pos).Intv.lo - - @test eval_check(mult_hi, neg, neg) == (neg*neg).Intv.hi - @test eval_check(mult_hi, neg, mix) == (neg*mix).Intv.hi - @test eval_check(mult_hi, neg, pos) == (neg*pos).Intv.hi - @test eval_check(mult_hi, mix, neg) == (mix*neg).Intv.hi - @test eval_check(mult_hi, mix, mix) == (mix*mix).Intv.hi - @test eval_check(mult_hi, mix, pos) == (mix*pos).Intv.hi - @test eval_check(mult_hi, pos, neg) == (pos*neg).Intv.hi - @test eval_check(mult_hi, pos, mix) == (pos*mix).Intv.hi - @test eval_check(mult_hi, pos, pos) == (pos*pos).Intv.hi - - @test eval_check(mult_cv, neg, neg) == (neg*neg).cv - @test eval_check(mult_cv, neg, mix) == (neg*mix).cv - @test eval_check(mult_cv, neg, pos) == (neg*pos).cv - @test eval_check(mult_cv, mix, neg) == (mix*neg).cv - @test eval_check(mult_cv, mix, mix) == (mix*mix).cv - @test eval_check(mult_cv, mix, pos) == (mix*pos).cv - @test eval_check(mult_cv, pos, neg) == (pos*neg).cv - @test eval_check(mult_cv, pos, mix) == (pos*mix).cv - @test eval_check(mult_cv, pos, pos) == (pos*pos).cv - - @test eval_check(mult_cc, neg, neg) == (neg*neg).cc - @test eval_check(mult_cc, neg, mix) == (neg*mix).cc - @test eval_check(mult_cc, neg, pos) == (neg*pos).cc - @test eval_check(mult_cc, mix, neg) == (mix*neg).cc - @test eval_check(mult_cc, mix, mix) == (mix*mix).cc - @test eval_check(mult_cc, mix, pos) == (mix*pos).cc - @test eval_check(mult_cc, pos, neg) == (pos*neg).cc - @test eval_check(mult_cc, pos, mix) == (pos*mix).cc - @test eval_check(mult_cc, pos, pos) == (pos*pos).cc -end - -@testset "Addition" begin - @variables x, y - to_compute = x+y - add_lo, add_hi, add_cv, add_cc, order = all_evaluators(to_compute) - - xMC = MC{2,NS}(-2.0, Interval(-3.0, -1.0), 1) - yMC = MC{2,NS}(4.0, Interval(2.0, 6.0), 2) - zMC = 0.5*xMC*yMC - neg = zMC - mix = zMC + 4.0 - pos = zMC + 10.0 - - @test eval_check(add_lo, neg, neg) == (neg+neg).Intv.lo - @test eval_check(add_lo, neg, mix) == (neg+mix).Intv.lo - @test eval_check(add_lo, neg, pos) == (neg+pos).Intv.lo - @test eval_check(add_lo, mix, neg) == (mix+neg).Intv.lo - @test eval_check(add_lo, mix, mix) == (mix+mix).Intv.lo - @test eval_check(add_lo, mix, pos) == (mix+pos).Intv.lo - @test eval_check(add_lo, pos, neg) == (pos+neg).Intv.lo - @test eval_check(add_lo, pos, mix) == (pos+mix).Intv.lo - @test eval_check(add_lo, pos, pos) == (pos+pos).Intv.lo - - @test eval_check(add_hi, neg, neg) == (neg+neg).Intv.hi - @test eval_check(add_hi, neg, mix) == (neg+mix).Intv.hi - @test eval_check(add_hi, neg, pos) == (neg+pos).Intv.hi - @test eval_check(add_hi, mix, neg) == (mix+neg).Intv.hi - @test eval_check(add_hi, mix, mix) == (mix+mix).Intv.hi - @test eval_check(add_hi, mix, pos) == (mix+pos).Intv.hi - @test eval_check(add_hi, pos, neg) == (pos+neg).Intv.hi - @test eval_check(add_hi, pos, mix) == (pos+mix).Intv.hi - @test eval_check(add_hi, pos, pos) == (pos+pos).Intv.hi - - @test eval_check(add_cv, neg, neg) == (neg+neg).cv - @test eval_check(add_cv, neg, mix) == (neg+mix).cv - @test eval_check(add_cv, neg, pos) == (neg+pos).cv - @test eval_check(add_cv, mix, neg) == (mix+neg).cv - @test eval_check(add_cv, mix, mix) == (mix+mix).cv - @test eval_check(add_cv, mix, pos) == (mix+pos).cv - @test eval_check(add_cv, pos, neg) == (pos+neg).cv - @test eval_check(add_cv, pos, mix) == (pos+mix).cv - @test eval_check(add_cv, pos, pos) == (pos+pos).cv - - @test eval_check(add_cc, neg, neg) == (neg+neg).cc - @test eval_check(add_cc, neg, mix) == (neg+mix).cc - @test eval_check(add_cc, neg, pos) == (neg+pos).cc - @test eval_check(add_cc, mix, neg) == (mix+neg).cc - @test eval_check(add_cc, mix, mix) == (mix+mix).cc - @test eval_check(add_cc, mix, pos) == (mix+pos).cc - @test eval_check(add_cc, pos, neg) == (pos+neg).cc - @test eval_check(add_cc, pos, mix) == (pos+mix).cc - @test eval_check(add_cc, pos, pos) == (pos+pos).cc -end - - -@testset "Subtraction" begin - @variables x, y - to_compute = x-y - sub_lo, sub_hi, sub_cv, sub_cc, order = all_evaluators(to_compute) - - xMC = MC{2,NS}(-2.0, Interval(-3.0, -1.0), 1) - yMC = MC{2,NS}(4.0, Interval(2.0, 6.0), 2) - zMC = 0.5*xMC*yMC - neg = zMC - mix = zMC + 4.0 - pos = zMC + 10.0 - - @test eval_check(sub_lo, neg, neg) == (neg-neg).Intv.lo - @test eval_check(sub_lo, neg, mix) == (neg-mix).Intv.lo - @test eval_check(sub_lo, neg, pos) == (neg-pos).Intv.lo - @test eval_check(sub_lo, mix, neg) == (mix-neg).Intv.lo - @test eval_check(sub_lo, mix, mix) == (mix-mix).Intv.lo - @test eval_check(sub_lo, mix, pos) == (mix-pos).Intv.lo - @test eval_check(sub_lo, pos, neg) == (pos-neg).Intv.lo - @test eval_check(sub_lo, pos, mix) == (pos-mix).Intv.lo - @test eval_check(sub_lo, pos, pos) == (pos-pos).Intv.lo - - @test eval_check(sub_hi, neg, neg) == (neg-neg).Intv.hi - @test eval_check(sub_hi, neg, mix) == (neg-mix).Intv.hi - @test eval_check(sub_hi, neg, pos) == (neg-pos).Intv.hi - @test eval_check(sub_hi, mix, neg) == (mix-neg).Intv.hi - @test eval_check(sub_hi, mix, mix) == (mix-mix).Intv.hi - @test eval_check(sub_hi, mix, pos) == (mix-pos).Intv.hi - @test eval_check(sub_hi, pos, neg) == (pos-neg).Intv.hi - @test eval_check(sub_hi, pos, mix) == (pos-mix).Intv.hi - @test eval_check(sub_hi, pos, pos) == (pos-pos).Intv.hi - - @test eval_check(sub_cv, neg, neg) == (neg-neg).cv - @test eval_check(sub_cv, neg, mix) == (neg-mix).cv - @test eval_check(sub_cv, neg, pos) == (neg-pos).cv - @test eval_check(sub_cv, mix, neg) == (mix-neg).cv - @test eval_check(sub_cv, mix, mix) == (mix-mix).cv - @test eval_check(sub_cv, mix, pos) == (mix-pos).cv - @test eval_check(sub_cv, pos, neg) == (pos-neg).cv - @test eval_check(sub_cv, pos, mix) == (pos-mix).cv - @test eval_check(sub_cv, pos, pos) == (pos-pos).cv - - @test eval_check(sub_cc, neg, neg) == (neg-neg).cc - @test eval_check(sub_cc, neg, mix) == (neg-mix).cc - @test eval_check(sub_cc, neg, pos) == (neg-pos).cc - @test eval_check(sub_cc, mix, neg) == (mix-neg).cc - @test eval_check(sub_cc, mix, mix) == (mix-mix).cc - @test eval_check(sub_cc, mix, pos) == (mix-pos).cc - @test eval_check(sub_cc, pos, neg) == (pos-neg).cc - @test eval_check(sub_cc, pos, mix) == (pos-mix).cc - @test eval_check(sub_cc, pos, pos) == (pos-pos).cc -end - - -@testset "Division" begin - @variables x, y - to_compute = x/y - div_lo, div_hi, div_cv, div_cc, order = all_evaluators(to_compute) - - xMC = MC{2,NS}(-2.0, Interval(-3.0, -1.0), 1) - yMC = MC{2,NS}(4.0, Interval(2.0, 6.0), 2) - zMC = 0.5*xMC*yMC - neg = zMC - mix = zMC + 4.0 - pos = zMC + 10.0 - - @test abs(eval_check(div_lo, 0.99*neg, neg) - (0.99*neg/neg).Intv.lo) < 1e-15 - @test isnan(eval_check(div_lo, neg, mix)) - @test eval_check(div_lo, neg, pos) == (neg/pos).Intv.lo - @test eval_check(div_lo, mix, neg) == (mix/neg).Intv.lo - @test eval_check(div_lo, mix, pos) == (mix/pos).Intv.lo - @test eval_check(div_lo, pos, neg) == (pos/neg).Intv.lo - @test isnan(eval_check(div_lo, pos, mix)) - @test abs(eval_check(div_lo, 0.99*pos, pos) - (0.99*pos/pos).Intv.lo) < 1e-15 - - @test abs(eval_check(div_hi, 0.99*neg, neg) - (0.99*neg/neg).Intv.hi) < 1e-15 - @test isnan(eval_check(div_hi, neg, mix)) - @test eval_check(div_hi, neg, pos) == (neg/pos).Intv.hi - @test eval_check(div_hi, mix, neg) == (mix/neg).Intv.hi - @test eval_check(div_hi, mix, pos) == (mix/pos).Intv.hi - @test eval_check(div_hi, pos, neg) == (pos/neg).Intv.hi - @test isnan(eval_check(div_hi, pos, mix)) - @test abs(eval_check(div_hi, 0.99*pos, pos) - (0.99*pos/pos).Intv.hi) < 1e-15 - - @test abs(eval_check(div_cv, 0.99*neg, neg) - (0.99*neg/neg).cv) < 1e-15 - @test isnan(eval_check(div_cv, neg, mix)) - @test eval_check(div_cv, neg, pos) == (neg/pos).cv - @test eval_check(div_cv, mix, neg) == (mix/neg).cv - @test eval_check(div_cv, mix, pos) == (mix/pos).cv - @test eval_check(div_cv, pos, neg) == (pos/neg).cv - @test isnan(eval_check(div_cv, pos, mix)) - @test abs(eval_check(div_cv, 0.99*pos, pos) - (0.99*pos/pos).cv) < 1e-15 - - @test abs(eval_check(div_cc, 0.99*neg, neg) - (0.99*neg/neg).cc) < 1e-15 - @test isnan(eval_check(div_cc, neg, mix)) - @test eval_check(div_cc, neg, pos) == (neg/pos).cc - @test eval_check(div_cc, mix, neg) == (mix/neg).cc - @test eval_check(div_cc, mix, pos) == (mix/pos).cc - @test abs(eval_check(div_cc, pos, neg) - (pos/neg).cc) < 1e-15 - @test isnan(eval_check(div_cc, pos, mix)) - @test abs(eval_check(div_cc, 0.99*pos, pos) - (0.99*pos/pos).cc) < 1e-15 -end + return eval_func(MC1.cv, MC1.cc, MC1.Intv.lo, MC1.Intv.hi, + MC2.cv, MC2.cc, MC2.Intv.lo, MC2.Intv.hi) +end +function eval_check(eval_func, MC1) + return eval_func(MC1.cv, MC1.cc, MC1.Intv.lo, MC1.Intv.hi) +end +include("multiplication.jl") +include("division.jl") +include("addition.jl") +include("exp.jl") +include("power.jl") #NOTE: Currently only includes ^2 +# include("minmax.jl") \ No newline at end of file