Skip to content

Commit 3f60770

Browse files
authored
Merge pull request #102 from SCIP-Interfaces/rs/more_nl_ops
Support more operators in nonlinear expressions.
2 parents 9ec6257 + 33f7e15 commit 3f60770

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

README.md

+12-4
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ Only Linux is tested and officially supported. Contributions to supporting other
3030
operating systems are welcome.
3131

3232
We recommend using one of the provided installers, e.g.,
33-
`SCIPOptSuite-6.0.0-Linux.deb` for systems based on Debian. Adding the SCIP.jl
33+
`SCIPOptSuite-6.0.1-Linux.deb` for systems based on Debian. Adding the SCIP.jl
3434
package should then work out of the box:
3535

3636
pkg> add SCIP
3737

38-
If you [build SCIP from source](https://scip.zib.de/doc-6.0.0/html/CMAKE.php)
38+
If you [build SCIP from source](https://scip.zib.de/doc-6.0.1/html/CMAKE.php)
3939
you should set the environment variable `SCIPOPTDIR` to point the the
4040
**installation path**. That is, `$SCIPOPTDIR/lib/libscip.so` should exist.
4141

@@ -95,10 +95,18 @@ constraints by name (`SingleVariable`-set constraints are not stored as SCIP
9595
constraints explicitly).
9696

9797
Support for more constraint types (quadratic/SOC, SOS1/2, nonlinear expression)
98-
is planned, but SCIP itself only supports affine objective functions, so we will
99-
stick with that. More general objective functions could be implented via a
98+
is implemented, but SCIP itself only supports affine objective functions, so we
99+
will stick with that. More general objective functions could be implented via a
100100
[bridge](https://github.com/JuliaOpt/MathOptInterface.jl/issues/529).
101101

102+
Supported operators in nonlinear expressions are as follows:
103+
104+
- unary: `-`, `sqrt`, `exp`, `log`, `abs`
105+
- binary: `-`, `/`, `^`, `min`, `max`
106+
- n-ary: `+`, `*`
107+
108+
In particular, trigonometric functions are not supported.
109+
102110
## Old Interface Implementation
103111

104112
A previous implementation of SCIP.jl supported

src/nonlinear.jl

+20-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11

22
# Mapping from Julia (as given by MOI) to SCIP operators
33
const OPMAP = Dict{Symbol, SCIP_ExprOp}(
4-
:+ => SCIP_EXPR_SUM,
5-
:* => SCIP_EXPR_PRODUCT,
6-
:- => SCIP_EXPR_MINUS,
7-
:/ => SCIP_EXPR_DIV,
8-
:^ => SCIP_EXPR_REALPOWER,
9-
:sqrt => SCIP_EXPR_SQRT,
10-
:exp => SCIP_EXPR_EXP,
11-
:log => SCIP_EXPR_LOG,
4+
:+ => SCIP_EXPR_SUM, # n-ary
5+
:* => SCIP_EXPR_PRODUCT, # n-ary
6+
:- => SCIP_EXPR_MINUS, # unary, binary
7+
:/ => SCIP_EXPR_DIV, # unary
8+
:^ => SCIP_EXPR_REALPOWER, # binary (or INTPOWER)
9+
:sqrt => SCIP_EXPR_SQRT, # unary
10+
:exp => SCIP_EXPR_EXP, # unary
11+
:log => SCIP_EXPR_LOG, # unary
12+
:abs => SCIP_EXPR_ABS, # unary
13+
:min => SCIP_EXPR_MIN, # binary
14+
:max => SCIP_EXPR_MAX, # binary
1215
)
1316

1417
"""Subexpressions and variables referenced in an expression tree.
@@ -56,10 +59,13 @@ function push_expr!(nonlin::NonlinExpr, mscip::ManagedSCIP, expr::Expr)
5659

5760
# Exponent (second child) is stored as value.
5861
@assert isa(expr.args[3], Number)
59-
exponent = Cdouble(expr.args[3])
60-
61-
# Create SCIP expression
62-
@SC SCIPexprCreate(SCIPblkmem(mscip), expr__, OPMAP[op], base, exponent)
62+
if isa(expr.args[3], Integer)
63+
exponent = Cint(expr.args[3])
64+
@SC SCIPexprCreate(SCIPblkmem(mscip), expr__, SCIP_EXPR_INTPOWER, base, exponent)
65+
else
66+
exponent = Cdouble(expr.args[3])
67+
@SC SCIPexprCreate(SCIPblkmem(mscip), expr__, SCIP_EXPR_REALPOWER, base, exponent)
68+
end
6369

6470
elseif op == :- && num_children == 1
6571
# Special case: unary version of minus. SCIP only supports binary
@@ -74,7 +80,7 @@ function push_expr!(nonlin::NonlinExpr, mscip::ManagedSCIP, expr::Expr)
7480
# Finally, add the (binary) minus:
7581
@SC SCIPexprCreate(SCIPblkmem(mscip), expr__, OPMAP[op], left, right)
7682

77-
elseif op in [:sqrt, :exp, :log]
83+
elseif op in [:sqrt, :exp, :log, :abs]
7884
# Unary operators
7985
@assert num_children == 1
8086

@@ -84,7 +90,7 @@ function push_expr!(nonlin::NonlinExpr, mscip::ManagedSCIP, expr::Expr)
8490
# Add this operator on top
8591
@SC SCIPexprCreate(SCIPblkmem(mscip), expr__, OPMAP[op], child)
8692

87-
elseif op in [:-, :/]
93+
elseif op in [:-, :/, :min, :max]
8894
# Binary operators
8995
@assert num_children == 2
9096

src/wrapper/expr_manual.jl

+12-1
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,20 @@ end
6161
function SCIPexprCreate(blkmem, expr, op, base::Ptr{SCIP_EXPR}, exponent::Cdouble)
6262
# WARNING: In the actual C function, the last two arguments given here are
6363
# part of the variadic arguments. But since Julia only supports variadic
64-
# arguments of the same type, we specify the first one (nchildren::Cint)
64+
# arguments of the same type, we specify the first one (base::Ptr{SCIP_EXPR})
6565
# directly, and pretend that only the last one is variadic.
6666
ccall((:SCIPexprCreate, libscip), SCIP_RETCODE,
6767
(Ptr{BMS_BLKMEM}, Ptr{Ptr{SCIP_EXPR}}, SCIP_EXPROP, Ptr{SCIP_EXPR}, Cdouble...),
6868
blkmem, expr, op, base, exponent)
6969
end
70+
71+
# SCIP_EXPR_INTPOWER (mixed binary op)
72+
function SCIPexprCreate(blkmem, expr, op, base::Ptr{SCIP_EXPR}, exponent::Cint)
73+
# WARNING: In the actual C function, the last two arguments given here are
74+
# part of the variadic arguments. But since Julia only supports variadic
75+
# arguments of the same type, we specify the first one (base::Ptr{SCIP_EXPR})
76+
# directly, and pretend that only the last one is variadic.
77+
ccall((:SCIPexprCreate, libscip), SCIP_RETCODE,
78+
(Ptr{BMS_BLKMEM}, Ptr{Ptr{SCIP_EXPR}}, SCIP_EXPROP, Ptr{SCIP_EXPR}, Cint...),
79+
blkmem, expr, op, base, exponent)
80+
end

test/MOI_nonlinear_exprs.jl

+11-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ end
6060

6161
@test MOI.supports(optimizer, MOI.NLPBlock()) == true
6262

63-
num_vars = 17
63+
num_vars = 20
6464
x = MOI.add_variables(optimizer, num_vars)
6565
for i in 1:num_vars
6666
MOI.add_constraint(optimizer, x[i], MOI.Interval(0.1, 10.0))
@@ -74,11 +74,15 @@ end
7474
:(-x[$(x[4])] + 4.0 == rhs), # MINUS (unary)
7575
:(x[$(x[5])] + x[$(x[6])] + x[$(x[7])] == rhs), # SUM
7676
:(x[$(x[8])] * x[$(x[9])] * x[$(x[10])] == rhs), # PRODUCT
77-
:((x[$(x[11])] + x[$(x[12])])^0.8 == rhs), # REALPOWER
77+
:((x[$(x[11])])^3 == rhs), # INTPOWER
78+
:((x[$(x[12])])^0.8 == rhs), # REALPOWER
7879
:(x[$(x[13])] / x[$(x[14])] == rhs), # DIV
7980
:(sqrt(x[$(x[15])]) == rhs), # SQRT
8081
:(exp(x[$(x[16])]) == rhs), # EXP
8182
:(log(x[$(x[17])]) == rhs), # LOG
83+
:(abs(x[$(x[18])] - 11) == rhs), # ABS
84+
:(min(x[$(x[19])], x[$(x[20])]) + 1 == rhs), # MIN
85+
:(max(x[$(x[19])], x[$(x[20])]) - 1 == rhs), # MAX
8286
]
8387

8488
data = MOI.NLPBlockData(
@@ -101,9 +105,13 @@ end
101105
@test -sol[4] + 4.0 rhs atol=atol rtol=rtol
102106
@test sol[5] + sol[6] + sol[7] rhs atol=atol rtol=rtol
103107
@test sol[8] * sol[9] * sol[10] rhs atol=atol rtol=rtol
104-
@test (sol[11] + sol[12])^0.8 rhs atol=atol rtol=rtol
108+
@test (sol[11])^3 rhs atol=atol rtol=rtol
109+
@test (sol[12])^0.8 rhs atol=atol rtol=rtol
105110
@test sol[13] / sol[14] rhs atol=atol rtol=rtol
106111
@test sqrt(sol[15]) rhs atol=atol rtol=rtol
107112
@test exp(sol[16]) rhs atol=atol rtol=rtol
108113
@test log(sol[17]) rhs atol=atol rtol=rtol
114+
@test abs(sol[18] - 11) rhs atol=atol rtol=rtol
115+
@test min(sol[19], sol[20]) 1.0 atol=atol rtol=rtol
116+
@test max(sol[19], sol[20]) 3.0 atol=atol rtol=rtol
109117
end

0 commit comments

Comments
 (0)