diff --git a/docs/src/developers/extensions.md b/docs/src/developers/extensions.md index 5094d55466f..a733f04ffe0 100644 --- a/docs/src/developers/extensions.md +++ b/docs/src/developers/extensions.md @@ -199,7 +199,7 @@ julia> const MutableArithmetics = JuMP._MA; julia> model = Model(); @variable(model, x); julia> function JuMP.parse_constraint_head( - _error::Function, + error_fn::Function, ::Val{:≔}, lhs, rhs, @@ -207,7 +207,7 @@ julia> function JuMP.parse_constraint_head( println("Rewriting ≔ as ==") new_lhs, parse_code = MutableArithmetics.rewrite(lhs) build_code = :( - build_constraint($(_error), $(new_lhs), MOI.EqualTo($(rhs))) + build_constraint($(error_fn), $(new_lhs), MOI.EqualTo($(rhs))) ) return false, parse_code, build_code end @@ -227,7 +227,7 @@ julia> const MutableArithmetics = JuMP._MA; julia> model = Model(); @variable(model, x); julia> function JuMP.parse_constraint_call( - _error::Function, + error_fn::Function, is_vectorized::Bool, ::Val{:my_equal_to}, lhs, @@ -236,10 +236,10 @@ julia> function JuMP.parse_constraint_call( println("Rewriting my_equal_to to ==") new_lhs, parse_code = MutableArithmetics.rewrite(lhs) build_code = if is_vectorized - :(build_constraint($(_error), $(new_lhs), MOI.EqualTo($(rhs))) + :(build_constraint($(error_fn), $(new_lhs), MOI.EqualTo($(rhs))) ) else - :(build_constraint.($(_error), $(new_lhs), MOI.EqualTo($(rhs)))) + :(build_constraint.($(error_fn), $(new_lhs), MOI.EqualTo($(rhs)))) end return parse_code, build_code end @@ -281,14 +281,14 @@ julia> model = Model(); @variable(model, x); julia> struct MyConstrType end julia> function JuMP.build_constraint( - _error::Function, + error_fn::Function, f::JuMP.GenericAffExpr, set::MOI.EqualTo, extra::Type{MyConstrType}; d = 0, ) new_set = MOI.LessThan(set.value + d) - return JuMP.build_constraint(_error, f, new_set) + return JuMP.build_constraint(error_fn, f, new_set) end julia> @constraint(model, my_con, x == 0, MyConstrType, d = 2) @@ -322,7 +322,7 @@ julia> struct MyConstraint{S} <: AbstractConstraint end julia> function JuMP.build_constraint( - _error::Function, + error_fn::Function, f::AffExpr, set::MOI.AbstractScalarSet, extra::MyTag, diff --git a/src/Containers/macro.jl b/src/Containers/macro.jl index 0657b2b4e06..67cef0f65f2 100644 --- a/src/Containers/macro.jl +++ b/src/Containers/macro.jl @@ -67,7 +67,12 @@ function _expr_is_splat(expr) return false end -function _parse_index_sets(_error::Function, index_vars, index_sets, arg::Expr) +function _parse_index_sets( + error_fn::Function, + index_vars, + index_sets, + arg::Expr, +) index_var, index_set = gensym(), esc(arg) if isexpr(arg, :kw, 2) || isexpr(arg, :(=), 2) # Handle [i=S] and x[i=S] @@ -77,7 +82,7 @@ function _parse_index_sets(_error::Function, index_vars, index_sets, arg::Expr) index_var, index_set = arg.args[2], esc(arg.args[3]) end if index_var in index_vars - _error( + error_fn( "The index $(index_var) appears more than once. The " * "index associated with each set must be unique.", ) @@ -108,7 +113,7 @@ Takes an `Expr` that specifies the container, e.g., 3. `condition`: Expr containing any conditional imposed on indexing, or `:()` if none is present """ -function _parse_ref_sets(_error::Function, expr::Expr) +function _parse_ref_sets(error_fn::Function, expr::Expr) c = copy(expr) index_vars, index_sets, condition = Any[], Any[], :() # `:(t[i, j; k])` is a `:ref`, while `:(t[i; j])` is a `:typed_vcat`. In @@ -120,7 +125,7 @@ function _parse_ref_sets(_error::Function, expr::Expr) # An expression like `t[i; k]` or `[i; k]`. The filtering condition is # the second argument. if length(c.args) > 2 - _error( + error_fn( "Unsupported syntax $c: There can be at most one filtering " * "condition, which is separated from the indices by a single " * "`;`.", @@ -136,7 +141,7 @@ function _parse_ref_sets(_error::Function, expr::Expr) if isexpr(c.args[1], :parameters) parameters = popfirst!(c.args) if length(parameters.args) != 1 - _error( + error_fn( "Unsupported syntax $c: There can be at most one " * "filtering condition, which is separated from the " * "indices by a single `;`.", @@ -146,7 +151,7 @@ function _parse_ref_sets(_error::Function, expr::Expr) end end for arg in c.args - _parse_index_sets(_error, index_vars, index_sets, arg) + _parse_index_sets(error_fn, index_vars, index_sets, arg) end return index_vars, index_sets, condition end @@ -180,7 +185,7 @@ function _has_dependent_sets(index_vars::Vector{Any}, index_sets::Vector{Any}) end """ - build_ref_sets(_error::Function, expr) + build_ref_sets(error_fn::Function, expr) Helper function for macros to construct container objects. @@ -190,7 +195,7 @@ Helper function for macros to construct container objects. ## Arguments - * `_error`: a function that takes a `String` and throws an error, potentially + * `error_fn`: a function that takes a `String` and throws an error, potentially annotating the input string with extra information such as from which macro it was thrown from. Use `error` if you do not want a modified error message. * `expr`: an `Expr` that specifies the container, e.g., @@ -208,10 +213,10 @@ Helper function for macros to construct container objects. See [`container_code`](@ref) for a worked example. """ -function build_ref_sets(_error::Function, expr) - index_vars, index_sets, condition = _parse_ref_sets(_error, expr) +function build_ref_sets(error_fn::Function, expr) + index_vars, index_sets, condition = _parse_ref_sets(error_fn, expr) if any(_expr_is_splat, index_sets) - _error( + error_fn( "cannot use splatting operator `...` in the definition of an " * "index set.", ) diff --git a/src/constraints.jl b/src/constraints.jl index 5b6651cdd54..727ce91eb8f 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -496,7 +496,7 @@ In order to automatically add the `CustomBridge` to any model to which an `F`-in-`CustomSet` is added, add the following method: ```julia function JuMP.build_constraint( - _error::Function, + error_fn::Function, func::AbstractJuMPScalar, set::CustomSet, ) diff --git a/src/indicator.jl b/src/indicator.jl index fc54d96252e..f87126d1511 100644 --- a/src/indicator.jl +++ b/src/indicator.jl @@ -7,7 +7,7 @@ # JuMP can be extended. function _build_indicator_constraint( - _error::Function, + error_fn::Function, variable::AbstractVariableRef, constraint::ScalarConstraint, ::Type{MOI.Indicator{A}}, @@ -17,12 +17,12 @@ function _build_indicator_constraint( end function _build_indicator_constraint( - _error::Function, + error_fn::Function, lhs::F, ::ScalarConstraint, ::Type{<:MOI.Indicator}, ) where {F} - return _error( + return error_fn( "unable to build indicator constraint with the left-hand side term " * "`($lhs)::$F`. The left-hand side must be a binary decision variable.", ) @@ -32,10 +32,10 @@ function _indicator_variable_set(::Function, variable::Symbol) return variable, MOI.Indicator{MOI.ACTIVATE_ON_ONE} end -function _indicator_variable_set(_error::Function, expr::Expr) +function _indicator_variable_set(error_fn::Function, expr::Expr) if expr.args[1] == :¬ || expr.args[1] == :! if length(expr.args) != 2 - _error( + error_fn( "Invalid binary variable expression `$(expr)` for indicator constraint.", ) end @@ -45,35 +45,37 @@ function _indicator_variable_set(_error::Function, expr::Expr) end end -function parse_constraint_head(_error::Function, ::Val{:(-->)}, lhs, rhs) - code, call = parse_constraint_call(_error, false, Val(:(=>)), lhs, rhs) +function parse_constraint_head(error_fn::Function, ::Val{:(-->)}, lhs, rhs) + code, call = parse_constraint_call(error_fn, false, Val(:(=>)), lhs, rhs) return false, code, call end function parse_constraint_call( - _error::Function, + error_fn::Function, vectorized::Bool, ::Union{Val{:(=>)},Val{:⇒}}, lhs, rhs, ) - variable, S = _indicator_variable_set(_error, lhs) + variable, S = _indicator_variable_set(error_fn, lhs) if !isexpr(rhs, :braces) || length(rhs.args) != 1 - _error( + error_fn( "Invalid right-hand side `$(rhs)` of indicator constraint. Expected constraint surrounded by `{` and `}`.", ) end rhs_vectorized, rhs_parsecode, rhs_build_call = - parse_constraint(_error, rhs.args[1]) + parse_constraint(error_fn, rhs.args[1]) if vectorized != rhs_vectorized - _error("Inconsistent use of `.` in symbols to indicate vectorization.") + error_fn( + "Inconsistent use of `.` in symbols to indicate vectorization.", + ) end f, lhs_parse_code = _rewrite_expression(variable) push!(rhs_parsecode.args, lhs_parse_code) build_call = if vectorized - :(_build_indicator_constraint.($_error, $f, $rhs_build_call, $S)) + :(_build_indicator_constraint.($error_fn, $f, $rhs_build_call, $S)) else - :(_build_indicator_constraint($_error, $f, $rhs_build_call, $S)) + :(_build_indicator_constraint($error_fn, $f, $rhs_build_call, $S)) end return rhs_parsecode, build_call end diff --git a/src/macros.jl b/src/macros.jl index 2f710e9e8d8..12f5068ec22 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -161,7 +161,7 @@ function _check_vectorized(sense::Symbol) end """ - operator_to_set(_error::Function, ::Val{sense_symbol}) + operator_to_set(error_fn::Function, ::Val{sense_symbol}) Converts a sense symbol to a set `set` such that `@constraint(model, func sense_symbol 0)` is equivalent to @@ -202,12 +202,12 @@ Note that the whole function is first moved to the right-hand side, then the sign is transformed into a set with zero constant and finally the constant is moved to the set with `MOIU.shift_constant`. """ -function operator_to_set(_error::Function, ::Val{S}) where {S} - return _error("unsupported operator $S") +function operator_to_set(error_fn::Function, ::Val{S}) where {S} + return error_fn("unsupported operator $S") end -function operator_to_set(_error::Function, ::Val{:>}) - return _error( +function operator_to_set(error_fn::Function, ::Val{:>}) + return error_fn( "unsupported operator `>`.\n\n" * "JuMP does not support strict inequalities, use `>=` instead.\n\n" * "If you require a strict inequality, you will need to use a " * @@ -219,8 +219,8 @@ function operator_to_set(_error::Function, ::Val{:>}) ) end -function operator_to_set(_error::Function, ::Val{:<}) - return _error( +function operator_to_set(error_fn::Function, ::Val{:<}) + return error_fn( "unsupported operator `<`.\n\n" * "JuMP does not support strict inequalities, use `<=` instead.\n\n" * "If you require a strict inequality, you will need to use a " * @@ -384,13 +384,13 @@ _functionize(x) = x _functionize(::_MA.Zero) = false """ - parse_constraint(_error::Function, expr::Expr) + parse_constraint(error_fn::Function, expr::Expr) The entry-point for all constraint-related parsing. ## Arguments - * The `_error` function is passed everywhere to provide better error messages + * The `error_fn` function is passed everywhere to provide better error messages * `expr` comes from the `@constraint` macro. There are two possibilities: * `@constraint(model, expr)` * `@constraint(model, name[args], expr)` @@ -420,12 +420,12 @@ as well as all broadcasted variants. The infrastructure behind `parse_constraint` is extendable. See [`parse_constraint_head`](@ref) and [`parse_constraint_call`](@ref) for details. """ -function parse_constraint(_error::Function, expr::Expr) - return parse_constraint_head(_error, Val(expr.head), expr.args...) +function parse_constraint(error_fn::Function, expr::Expr) + return parse_constraint_head(error_fn, Val(expr.head), expr.args...) end """ - parse_constraint_head(_error::Function, ::Val{head}, args...) + parse_constraint_head(error_fn::Function, ::Val{head}, args...) Implement this method to intercept the parsing of an expression with head `head`. @@ -438,7 +438,7 @@ Implement this method to intercept the parsing of an expression with head ## Arguments - * `_error`: a function that accepts a `String` and throws the string as an + * `error_fn`: a function that accepts a `String` and throws the string as an error, along with some descriptive information of the macro from which it was thrown. * `head`: the `.head` field of the `Expr` to intercept @@ -464,8 +464,8 @@ JuMP currently implements: See also: [`parse_constraint_call`](@ref), [`build_constraint`](@ref) """ -function parse_constraint_head(_error::Function, ::Val{T}, args...) where {T} - return _error( +function parse_constraint_head(error_fn::Function, ::Val{T}, args...) where {T} + return error_fn( "Unsupported constraint expression: we don't know how to parse " * "constraints containing expressions of type :$T.\n\nIf you are " * "writing a JuMP extension, implement " * @@ -474,14 +474,14 @@ function parse_constraint_head(_error::Function, ::Val{T}, args...) where {T} end function parse_constraint_head( - _error::Function, + error_fn::Function, ::Val{:call}, op::Symbol, args..., ) op, is_vectorized = _check_vectorized(op) parse_code, build_call = - parse_constraint_call(_error, is_vectorized, Val(op), args...) + parse_constraint_call(error_fn, is_vectorized, Val(op), args...) return is_vectorized, parse_code, build_call end @@ -725,7 +725,7 @@ function _rewrite_expression(expr) end function parse_constraint_head( - _error::Function, + error_fn::Function, ::Val{:comparison}, lb, lsign::Symbol, @@ -736,7 +736,7 @@ function parse_constraint_head( lsign, lvectorized = _check_vectorized(lsign) rsign, rvectorized = _check_vectorized(rsign) if lvectorized != rvectorized - _error("Operators are inconsistently vectorized.") + error_fn("Operators are inconsistently vectorized.") end if lsign in (:(<=), :≤) && rsign in (:(<=), :≤) # Nothing. What we expect. @@ -744,7 +744,7 @@ function parse_constraint_head( # Flip lb and ub lb, ub = ub, lb else - _error( + error_fn( "unsupported mix of comparison operators " * "`$lb $lsign ... $rsign $ub`.\n\n" * "Two-sided rows must of the form `$lb <= ... <= $ub` or " * @@ -762,21 +762,21 @@ function parse_constraint_head( build_call = if lvectorized :( build_constraint.( - $_error, + $error_fn, _desparsify($new_aff), _desparsify($new_lb), _desparsify($new_ub), ) ) else - :(build_constraint($_error, $new_aff, $new_lb, $new_ub)) + :(build_constraint($error_fn, $new_aff, $new_lb, $new_ub)) end return lvectorized, parse_code, build_call end """ parse_constraint_call( - _error::Function, + error_fn::Function, is_vectorized::Bool, ::Val{op}, args..., @@ -793,7 +793,7 @@ operator `op`. ## Arguments - * `_error`: a function that accepts a `String` and throws the string as an + * `error_fn`: a function that accepts a `String` and throws the string as an error, along with some descriptive information of the macro from which it was thrown. * `is_vectorized`: a boolean to indicate if `op` should be broadcast or not @@ -812,12 +812,12 @@ This function must return: See also: [`parse_constraint_head`](@ref), [`build_constraint`](@ref) """ function parse_constraint_call( - _error::Function, + error_fn::Function, ::Bool, ::Val{T}, args..., ) where {T} - return _error( + return error_fn( "Unsupported constraint expression: we don't know how to parse " * "constraints containing the operator $T.\n\nIf you are writing a " * "JuMP extension, implement " * @@ -828,7 +828,7 @@ end # `@constraint(model, func in set)` # `@constraint(model, func ∈ set)` function parse_constraint_call( - _error::Function, + error_fn::Function, vectorized::Bool, ::Union{Val{:in},Val{:∈}}, func, @@ -836,16 +836,16 @@ function parse_constraint_call( ) f, parse_code = _rewrite_expression(func) build_call = if vectorized - :(build_constraint.($_error, _desparsify($f), Ref($(esc(set))))) + :(build_constraint.($error_fn, _desparsify($f), Ref($(esc(set))))) else - :(build_constraint($_error, $f, $(esc(set)))) + :(build_constraint($error_fn, $f, $(esc(set)))) end return parse_code, build_call end """ parse_constraint_call( - _error::Function, + error_fn::Function, vectorized::Bool, ::Val{op}, lhs, @@ -856,12 +856,12 @@ Fallback handler for binary operators. These might be infix operators like `@constraint(model, lhs op rhs)`, or normal operators like `@constraint(model, op(lhs, rhs))`. -In both cases, we rewrite as `lhs - rhs in operator_to_set(_error, op)`. +In both cases, we rewrite as `lhs - rhs in operator_to_set(error_fn, op)`. See [`operator_to_set`](@ref) for details. """ function parse_constraint_call( - _error::Function, + error_fn::Function, vectorized::Bool, operator::Val, lhs, @@ -869,14 +869,14 @@ function parse_constraint_call( ) func = vectorized ? :($lhs .- $rhs) : :($lhs - $rhs) f, parse_code = _rewrite_expression(func) - set = operator_to_set(_error, operator) + set = operator_to_set(error_fn, operator) # `_functionize` deals with the pathological case where the `lhs` is a # `VariableRef` and the `rhs` is a summation with no terms. f = :(_functionize($f)) build_call = if vectorized - :(build_constraint.($_error, _desparsify($f), Ref($(esc(set))))) + :(build_constraint.($error_fn, _desparsify($f), Ref($(esc(set))))) else - :(build_constraint($_error, $f, $(esc(set)))) + :(build_constraint($error_fn, $f, $(esc(set)))) end return parse_code, build_call end @@ -886,14 +886,14 @@ end ### function build_constraint( - _error::Function, + error_fn::Function, f, set::Nonnegatives, args...; kwargs..., ) return build_constraint( - _error, + error_fn, f, MOI.GreaterThan(false), args...; @@ -902,46 +902,52 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, f, set::Nonpositives, args...; kwargs..., ) - return build_constraint(_error, f, MOI.LessThan(false), args...; kwargs...) + return build_constraint( + error_fn, + f, + MOI.LessThan(false), + args...; + kwargs..., + ) end -function build_constraint(_error::Function, f, set::Zeros, args...; kwargs...) - return build_constraint(_error, f, MOI.EqualTo(false), args...; kwargs...) +function build_constraint(error_fn::Function, f, set::Zeros, args...; kwargs...) + return build_constraint(error_fn, f, MOI.EqualTo(false), args...; kwargs...) end function build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector, set::Nonnegatives, ) - return build_constraint(_error, f, MOI.Nonnegatives(length(f))) + return build_constraint(error_fn, f, MOI.Nonnegatives(length(f))) end function build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector, set::Nonpositives, ) - return build_constraint(_error, f, MOI.Nonpositives(length(f))) + return build_constraint(error_fn, f, MOI.Nonpositives(length(f))) end -function build_constraint(_error::Function, f::AbstractVector, set::Zeros) - return build_constraint(_error, f, MOI.Zeros(length(f))) +function build_constraint(error_fn::Function, f::AbstractVector, set::Zeros) + return build_constraint(error_fn, f, MOI.Zeros(length(f))) end # Generic fallback. -function build_constraint(_error::Function, func, set, args...; kwargs...) +function build_constraint(error_fn::Function, func, set, args...; kwargs...) arg_str = join(args, ", ") arg_str = isempty(arg_str) ? "" : ", " * arg_str kwarg_str = join(Tuple(string(k, " = ", v) for (k, v) in kwargs), ", ") kwarg_str = isempty(kwarg_str) ? "" : "; " * kwarg_str - return _error( + return error_fn( "Unrecognized constraint building format. Tried to invoke " * "`build_constraint(error, $(func), $(set)$(arg_str)$(kwarg_str))`, " * "but no such method exists. This is due to specifying an unrecognized " * @@ -952,11 +958,11 @@ function build_constraint(_error::Function, func, set, args...; kwargs...) end function build_constraint( - _error::Function, + error_fn::Function, func, ::Union{MOI.AbstractScalarSet,MOI.AbstractVectorSet}, ) - return _error( + return error_fn( "Unable to add the constraint because we don't recognize " * "$(func) as a valid JuMP function.", ) @@ -995,11 +1001,11 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, ::_MA.Zero, set::MOI.AbstractScalarSet, ) - return build_constraint(_error, false, set) + return build_constraint(error_fn, false, set) end function build_constraint( @@ -1011,59 +1017,59 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, x::AbstractArray, set::MOI.AbstractScalarSet, ) - return _error( + return error_fn( "Unexpected vector in scalar constraint. The left- and right-hand " * "sides of the constraint must have the same dimension.", ) end function build_constraint( - _error::Function, + error_fn::Function, ::AbstractArray, ::AbstractVector, ::AbstractVector, ) - return _error( + return error_fn( "Unexpected vectors in scalar constraint. Did you mean to use the dot ", "comparison operators `l .<= f(x) .<= u` instead?", ) end function build_constraint( - _error::Function, + error_fn::Function, x::AbstractMatrix, set::MOI.AbstractVectorSet, ) - return _error( + return error_fn( "unexpected matrix in vector constraint. Do you need to flatten the " * "matrix into a vector using `vec()`?", ) end function build_constraint( - _error::Function, + error_fn::Function, ::Matrix, T::MOI.PositiveSemidefiniteConeTriangle, ) - return _error("instead of `$(T)`, use `JuMP.PSDCone()`.") + return error_fn("instead of `$(T)`, use `JuMP.PSDCone()`.") end # three-argument build_constraint is used for two-sided constraints. function build_constraint( - _error::Function, + error_fn::Function, func::AbstractJuMPScalar, lb::Real, ub::Real, ) if isnan(lb) || isnan(ub) - _error("Invalid bounds, cannot contain NaN: [$(lb), $(ub)].") + error_fn("Invalid bounds, cannot contain NaN: [$(lb), $(ub)].") end return build_constraint( - _error, + error_fn, func, MOI.Interval( convert(value_type(variable_ref_type(func)), lb), @@ -1073,12 +1079,12 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, ::AbstractJuMPScalar, ::Union{AbstractJuMPScalar,Real}, ::Union{AbstractJuMPScalar,Real}, ) - return _error( + return error_fn( "Interval constraint contains non-constant left- or " * "right-hand sides. Reformulate as two separate " * "constraints, or move all variables into the central term.", @@ -1092,13 +1098,13 @@ end # `@constraint(model, var in MOI.Interval(lb, ub))`. We do this for consistency # with how one-sided (in)equality constraints are parsed. function build_constraint( - _error::Function, + error_fn::Function, func::AbstractVariableRef, lb::Real, ub::Real, ) return build_constraint( - _error, + error_fn, one(value_type(typeof(func))) * func, lb, ub, @@ -1251,27 +1257,27 @@ function _constraint_macro( parsefun::Function, source::LineNumberNode, ) - _error(str...) = _macro_error(macro_name, args, source, str...) + error_fn(str...) = _macro_error(macro_name, args, source, str...) - # The positional args can't be `args` otherwise `_error` excludes keyword args + # The positional args can't be `args` otherwise `error_fn` excludes keyword args pos_args, kw_args, requested_container = Containers._extract_kw_args(args) # Initial check of the positional arguments and get the model if length(pos_args) < 2 if length(kw_args) > 0 - _error( + error_fn( "No constraint expression detected. If you are trying to " * "construct an equality constraint, use `==` instead of `=`.", ) else - _error("Not enough arguments") + error_fn("Not enough arguments") end end model = esc(pos_args[1]) y = pos_args[2] extra = pos_args[3:end] if isexpr(args[2], :block) - _error("Invalid syntax. Did you mean to use `@$(macro_name)s`?") + error_fn("Invalid syntax. Did you mean to use `@$(macro_name)s`?") end # Determine if a reference/container argument was given by the user # There are six cases to consider: @@ -1284,7 +1290,7 @@ function _constraint_macro( # [i = 1:2, j = 1:2; i + j >= 3] | Expr | :vcat # a constraint expression | Expr | :call or :comparison if isa(y, Symbol) || isexpr(y, (:vect, :vcat, :ref, :typed_vcat)) - length(extra) >= 1 || _error("No constraint expression was given.") + length(extra) >= 1 || error_fn("No constraint expression was given.") c = y x = popfirst!(extra) anonvar = isexpr(y, (:vect, :vcat)) @@ -1296,7 +1302,7 @@ function _constraint_macro( # Enforce that only one extra positional argument can be given if length(extra) > 1 - _error("Cannot specify more than 1 additional positional argument.") + error_fn("Cannot specify more than 1 additional positional argument.") end # Prepare the keyword arguments @@ -1316,14 +1322,14 @@ function _constraint_macro( # Strategy: build up the code for add_constraint, and if needed we will wrap # in a function returning `ConstraintRef`s and give it to `Containers.container`. - idxvars, indices = Containers.build_ref_sets(_error, c) + idxvars, indices = Containers.build_ref_sets(error_fn, c) if pos_args[1] in idxvars - _error( + error_fn( "Index $(pos_args[1]) is the same symbol as the model. Use a " * "different name for the index.", ) end - vectorized, parsecode, buildcall = parsefun(_error, x) + vectorized, parsecode, buildcall = parsefun(error_fn, x) _add_positional_args(buildcall, extra) _add_kw_args(buildcall, extra_kw_args) if vectorized @@ -1415,8 +1421,8 @@ The recognized keyword arguments in `kw_args` are the following: ## Note for extending the constraint macro Each constraint will be created using -`add_constraint(m, build_constraint(_error, func, set))` where -* `_error` is an error function showing the constraint call in addition to the +`add_constraint(m, build_constraint(error_fn, func, set))` where +* `error_fn` is an error function showing the constraint call in addition to the error message given as argument, * `func` is the expression that is constrained * and `set` is the set in which it is constrained to belong. @@ -1425,7 +1431,7 @@ For `expr` of the first type (i.e. `@constraint(m, func in set)`), `func` and `set` are passed unchanged to `build_constraint` but for the other types, they are determined from the expressions and signs. For instance, `@constraint(m, x^2 + y^2 == 1)` is transformed into -`add_constraint(m, build_constraint(_error, x^2 + y^2, MOI.EqualTo(1.0)))`. +`add_constraint(m, build_constraint(error_fn, x^2 + y^2, MOI.EqualTo(1.0)))`. To extend JuMP to accept new constraints of this form, it is necessary to add the corresponding methods to `build_constraint`. Note that this will likely mean @@ -1437,7 +1443,7 @@ For extensions that need to create constraints with more information than just `func` and `set`, an additional positional argument can be specified to `@constraint` that will then be passed on `build_constraint`. Hence, we can enable this syntax by defining extensions of -`build_constraint(_error, func, set, my_arg; kw_args...)`. This produces the +`build_constraint(error_fn, func, set, my_arg; kw_args...)`. This produces the user syntax: `@constraint(model, ref[...], expr, my_arg, kw_args...)`. """ macro constraint(args...) @@ -1465,7 +1471,7 @@ ScalarConstraint{AffExpr, MathOptInterface.GreaterThan{Float64}}(2 x, MathOptInt ``` """ macro build_constraint(constraint_expr) - function _error(str...) + function error_fn(str...) return _macro_error( :build_constraint, (constraint_expr,), @@ -1475,14 +1481,14 @@ macro build_constraint(constraint_expr) end if isa(constraint_expr, Symbol) - _error( + error_fn( "Incomplete constraint specification $constraint_expr. " * "Are you missing a comparison (<=, >=, or ==)?", ) end is_vectorized, parse_code, build_call = - parse_constraint(_error, constraint_expr) + parse_constraint(error_fn, constraint_expr) result_variable = gensym() code = quote $parse_code @@ -1760,17 +1766,17 @@ julia> @NLexpressions(model, begin """ :(@NLexpressions) """ - _moi_sense(_error::Function, sense) + _moi_sense(error_fn::Function, sense) Return an expression whose value is an `MOI.OptimizationSense` corresponding to `sense`. Sense is either the symbol `:Min` or `:Max`, corresponding respectively to `MIN_SENSE` and `MAX_SENSE` or it is another symbol, which should be the name of a variable or expression whose value is an `MOI.OptimizationSense`. -In the last case, the expression throws an error using the `_error` +In the last case, the expression throws an error using the `error_fn` function in case the value is not an `MOI.OptimizationSense`. """ -function _moi_sense(_error::Function, sense) +function _moi_sense(error_fn::Function, sense) expr = if sense == :Min MIN_SENSE elseif sense == :Max @@ -1778,11 +1784,11 @@ function _moi_sense(_error::Function, sense) else esc(sense) end - return :(_throw_error_for_invalid_sense($_error, $expr)) + return :(_throw_error_for_invalid_sense($error_fn, $expr)) end -function _throw_error_for_invalid_sense(_error::Function, sense) - return _error( +function _throw_error_for_invalid_sense(error_fn::Function, sense) + return error_fn( "unexpected sense `$sense`. The sense must be an " * "`::MOI.OptimizatonSense`, or the symbol `:Min` or `:Max`.", ) @@ -1839,20 +1845,20 @@ x² - 2 x + 1 ``` """ macro objective(model, args...) - function _error(str...) + function error_fn(str...) return _macro_error(:objective, (model, args...), __source__, str...) end - # We don't overwrite `model` as it is used in `_error` + # We don't overwrite `model` as it is used in `error_fn` esc_model = esc(model) if length(args) != 2 # Either just an objective sense, or just an expression. - _error( + error_fn( "needs three arguments: model, objective sense (Max or Min) and expression.", ) end sense, x = args - sense_expr = _moi_sense(_error, sense) + sense_expr = _moi_sense(error_fn, sense) newaff, parsecode = _rewrite_expression(x) code = quote $parsecode @@ -1915,7 +1921,7 @@ julia> expr = @expression(model, [i in 1:3], i * sum(x[j] for j in 1:3)) ``` """ macro expression(args...) - _error(str...) = _macro_error(:expression, args, __source__, str...) + error_fn(str...) = _macro_error(:expression, args, __source__, str...) args, kw_args, requested_container = Containers._extract_kw_args(args) if length(args) == 3 m = esc(args[1]) @@ -1926,18 +1932,18 @@ macro expression(args...) c = gensym() x = args[2] else - _error("needs at least two arguments.") + error_fn("needs at least two arguments.") end - length(kw_args) == 0 || _error("unrecognized keyword argument") + length(kw_args) == 0 || error_fn("unrecognized keyword argument") if isexpr(args[2], :block) - _error("Invalid syntax. Did you mean to use `@expressions`?") + error_fn("Invalid syntax. Did you mean to use `@expressions`?") end anonvar = isexpr(c, :vect) || isexpr(c, :vcat) || length(args) == 2 variable = gensym() - idxvars, indices = Containers.build_ref_sets(_error, c) + idxvars, indices = Containers.build_ref_sets(error_fn, c) if args[1] in idxvars - _error( + error_fn( "Index $(args[1]) is the same symbol as the model. Use a " * "different name for the index.", ) @@ -1974,7 +1980,7 @@ _esc_non_constant(x) = esc(x) """ build_variable( - _error::Function, + error_fn::Function, info::VariableInfo, args...; kwargs..., @@ -1987,7 +1993,7 @@ It should never be called by users of JuMP. ## Arguments - * `_error`: a function to call instead of `error`. `_error` annotates the + * `error_fn`: a function to call instead of `error`. `error_fn` annotates the error message with additional information for the user. * `info`: an instance of [`VariableInfo`](@ref). This has a variety of fields relating to the variable such as `info.lower_bound` and `info.binary`. @@ -2011,7 +2017,7 @@ See also: [`@variable`](@ref) ``` will call ```julia -build_variable(_error::Function, info::VariableInfo, ::Type{Foo}) +build_variable(error_fn::Function, info::VariableInfo, ::Type{Foo}) ``` Passing special-case positional arguments such as `Bin`, `Int`, and `PSD` is @@ -2023,20 +2029,20 @@ okay, along with keyword arguments: ``` will call ```julia -build_variable(_error::Function, info::VariableInfo, ::Foo; mykwarg) +build_variable(error_fn::Function, info::VariableInfo, ::Foo; mykwarg) ``` and `info.integer` will be true. Note that the order of the positional arguments does not matter. """ function build_variable( - _error::Function, + error_fn::Function, info::VariableInfo, args...; kwargs..., ) if length(args) > 0 - _error( + error_fn( "Unrecognized positional arguments: $(args). (You may have " * "passed it as a positional argument, or as a keyword value to " * "`variable_type`.)\n\nIf you're trying to create a JuMP " * @@ -2046,20 +2052,20 @@ function build_variable( end for (key, _) in kwargs if key == :Bool - _error( + error_fn( "Unsupported keyword argument: $key.\n\nIf you intended to " * "create a `{0, 1}` decision variable, use the `binary` keyword " * "argument instead: `@variable(model, x, binary = true)`.", ) end - _error( + error_fn( "Unrecognized keyword argument: $key.\n\nIf you're trying " * "to create a JuMP extension, you need to implement " * "`build_variable`. Read the docstring for more details.", ) end if info.lower_bound isa AbstractArray - _error( + error_fn( """ Passing arrays as variable bounds without indexing them is not supported. @@ -2079,7 +2085,7 @@ function build_variable( """, ) elseif info.upper_bound isa AbstractArray - _error( + error_fn( """ Passing arrays as variable bounds without indexing them is not supported. @@ -2099,7 +2105,7 @@ function build_variable( """, ) elseif info.fixed_value isa AbstractArray - _error( + error_fn( """ Passing arrays as variable bounds without indexing them is not supported. @@ -2119,7 +2125,7 @@ function build_variable( """, ) elseif info.start isa AbstractArray - _error( + error_fn( """ Passing arrays as variable starts without indexing them is not supported. @@ -2143,12 +2149,12 @@ function build_variable( end function build_variable( - _error::Function, + error_fn::Function, info::VariableInfo, ::Type{Bool}; kwargs..., ) - return _error( + return error_fn( "Unsupported positional argument `Bool`. If you intended to create a " * "`{0, 1}` decision variable, use `Bin` instead. For example, " * "`@variable(model, x, Bin)` or `@variable(model, x, binary = true)`.", @@ -2164,12 +2170,12 @@ function build_variable( end function build_variable( - _error::Function, + error_fn::Function, variables::AbstractArray{<:ScalarVariable}, sets::AbstractArray{<:MOI.AbstractScalarSet}, ) if length(variables) != length(sets) - return _error( + return error_fn( "Dimensions must match. Got a vector of scalar variables with" * "$(length(variables)) elements and a vector of " * "scalar sets with $(length(sets)).", @@ -2187,11 +2193,11 @@ function build_variable( end function build_variable( - _error::Function, + error_fn::Function, ::ScalarVariable, sets::AbstractArray{<:MOI.AbstractScalarSet}, ) - return _error( + return error_fn( "It is not possible to add a scalar variable in an Array of " * "sets. Either add an Array of scalar variables in a scalar set or " * "add an Array of scalar variables in an Array of scalar sets of " * @@ -2258,13 +2264,13 @@ reverse_sense(::Val{:≥}) = Val(:≤) reverse_sense(::Val{:(==)}) = Val(:(==)) """ - parse_variable(_error::Function, ::_VariableInfoExpr, args...) + parse_variable(error_fn::Function, ::_VariableInfoExpr, args...) A hook for extensions to intercept the parsing of inequality constraints in the [`@variable`](@ref) macro. """ -function parse_variable(_error::Function, ::_VariableInfoExpr, args...) - return _error( +function parse_variable(error_fn::Function, ::_VariableInfoExpr, args...) + return error_fn( "Invalid syntax: your syntax is wrong, but we don't know why. " * "Consult the documentation for various ways to create variables in " * "JuMP.", @@ -2272,7 +2278,7 @@ function parse_variable(_error::Function, ::_VariableInfoExpr, args...) end """ - parse_one_operator_variable(_error::Function, infoexpr::_VariableInfoExpr, sense::Val{S}, value) where S + parse_one_operator_variable(error_fn::Function, infoexpr::_VariableInfoExpr, sense::Val{S}, value) where S Update `infoexr` for a variable expression in the `@variable` macro of the form `variable name S value`. """ @@ -2287,49 +2293,49 @@ function parse_one_operator_variable( return set end function parse_one_operator_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Union{Val{:<=},Val{:≤},Val{:.<=},Val{:.≤}}, upper, ) - _set_upper_bound_or_error(_error, infoexpr, upper) + _set_upper_bound_or_error(error_fn, infoexpr, upper) return end function parse_one_operator_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Union{Val{:>=},Val{:≥},Val{:.>=},Val{:.≥}}, lower, ) - _set_lower_bound_or_error(_error, infoexpr, lower) + _set_lower_bound_or_error(error_fn, infoexpr, lower) return end function parse_one_operator_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Union{Val{:(==)},Val{:.==}}, value, ) - _fix_or_error(_error, infoexpr, value) + _fix_or_error(error_fn, infoexpr, value) return end function parse_one_operator_variable( - _error::Function, + error_fn::Function, ::_VariableInfoExpr, ::Val{S}, ::Any, ) where {S} - return _error("unsupported operator $S") + return error_fn("unsupported operator $S") end function parse_one_operator_variable( - _error::Function, + error_fn::Function, ::_VariableInfoExpr, ::Val{:>}, ::Any, ) - return _error( + return error_fn( "unsupported operator `>`.\n\n" * "JuMP does not support strict inequalities, use `>=` instead.\n\n" * "If you require a strict inequality, you will need to use a " * @@ -2342,12 +2348,12 @@ function parse_one_operator_variable( end function parse_one_operator_variable( - _error::Function, + error_fn::Function, ::_VariableInfoExpr, ::Val{:<}, ::Any, ) - return _error( + return error_fn( "unsupported operator `<`.\n\n" * "JuMP does not support strict inequalities, use `<=` instead.\n\n" * "If you require a strict inequality, you will need to use a " * @@ -2367,14 +2373,14 @@ end # that `a` has a value. # In that case we assume the variable is the lhs. function parse_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, sense::Symbol, var, value, ) set = parse_one_operator_variable( - _error, + error_fn, infoexpr, Val(sense), _esc_non_constant(value), @@ -2385,14 +2391,14 @@ end # If the lhs is a number and not the rhs, we can deduce that the rhs is # the variable. function parse_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, sense::Symbol, value::Number, var, ) set = parse_one_operator_variable( - _error, + error_fn, infoexpr, reverse_sense(Val(sense)), _esc_non_constant(value), @@ -2401,7 +2407,7 @@ function parse_variable( end """ - parse_ternary_variable(_error, variable_info, lhs_sense, lhs, rhs_sense, rhs) + parse_ternary_variable(error_fn, variable_info, lhs_sense, lhs, rhs_sense, rhs) A hook for JuMP extensiosn to intercept the parsing of a `:comparison` expression, which has the form `lhs lhs_sense variable rhs_sense rhs`. @@ -2409,19 +2415,19 @@ expression, which has the form `lhs lhs_sense variable rhs_sense rhs`. function parse_ternary_variable end function parse_ternary_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Union{Val{:<=},Val{:≤},Val{:.<=},Val{:.≤}}, lower, ::Union{Val{:<=},Val{:≤},Val{:.<=},Val{:.≤}}, upper, ) - _set_lower_bound_or_error(_error, infoexpr, lower) - _set_upper_bound_or_error(_error, infoexpr, upper) + _set_lower_bound_or_error(error_fn, infoexpr, lower) + _set_upper_bound_or_error(error_fn, infoexpr, upper) return end function parse_ternary_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Union{Val{:>=},Val{:≥},Val{:.>=},Val{:.≥}}, upper, @@ -2429,7 +2435,7 @@ function parse_ternary_variable( lower, ) return parse_ternary_variable( - _error, + error_fn, infoexpr, Val(:≤), lower, @@ -2439,14 +2445,14 @@ function parse_ternary_variable( end function parse_ternary_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, ::Val{A}, lb, ::Val{B}, ub, ) where {A,B} - return _error( + return error_fn( "unsupported mix of comparison operators `$lb $A ... $B $ub`.\n\n" * "Two-sided variable bounds must of the form `$lb <= ... <= $ub` or " * "`$ub >= ... >= $lb`.", @@ -2454,7 +2460,7 @@ function parse_ternary_variable( end function parse_variable( - _error::Function, + error_fn::Function, infoexpr::_VariableInfoExpr, lvalue, lsign::Symbol, @@ -2464,7 +2470,7 @@ function parse_variable( ) # lvalue lsign var rsign rvalue set = parse_ternary_variable( - _error, + error_fn, infoexpr, Val(lsign), _esc_non_constant(lvalue), @@ -2838,14 +2844,14 @@ julia> @variable(model, z[i=1:3], set_string_name = false) ``` """ macro variable(args...) - _error(str...) = _macro_error(:variable, args, __source__, str...) + error_fn(str...) = _macro_error(:variable, args, __source__, str...) # We need to re-order the parameters here to account for cases like # `@variable(model; integer = true)`, since Julia handles kwargs by placing # them first(!) in the list of arguments. args = _reorder_parameters(args) model = esc(args[1]) if length(args) >= 2 && isexpr(args[2], :block) - _error("Invalid syntax. Did you mean to use `@variables`?") + error_fn("Invalid syntax. Did you mean to use `@variables`?") end extra, kw_args, requested_container = Containers._extract_kw_args(args[2:end]) @@ -2858,17 +2864,17 @@ macro variable(args...) else x = popfirst!(extra) if x == :Int - _error( + error_fn( "Ambiguous variable name $x detected. To specify an anonymous integer " * "variable, use `@variable(model, integer = true)` instead.", ) elseif x == :Bin - _error( + error_fn( "Ambiguous variable name $x detected. To specify an anonymous binary " * "variable, use `@variable(model, binary = true)` instead.", ) elseif x == :PSD - _error( + error_fn( "Size of anonymous square matrix of positive semidefinite anonymous variables is not specified. To specify size of square matrix " * "use `@variable(model, [1:n, 1:n], PSD)` instead.", ) @@ -2904,14 +2910,14 @@ macro variable(args...) # In the three last cases, we call parse_variable explicit_comparison = isexpr(x, :comparison) || isexpr(x, :call) if explicit_comparison - var, set = parse_variable(_error, infoexpr, x.args...) + var, set = parse_variable(error_fn, infoexpr, x.args...) else var = x set = nothing end anonvar = isexpr(var, :vect) || isexpr(var, :vcat) || anon_singleton if anonvar && explicit_comparison && set === nothing - _error( + error_fn( "Cannot use explicit bounds via >=, <= with an anonymous variable", ) end @@ -2926,14 +2932,16 @@ macro variable(args...) set_kw_args = filter(kw -> kw.args[1] == :set, kw_args) if length(set_kw_args) == 1 if set !== nothing - _error( + error_fn( "Cannot use set keyword because the variable is already " * "constrained to `$set`.", ) end set = esc(set_kw_args[1].args[2]) elseif length(set_kw_args) > 1 - _error("`set` keyword argument was given $(length(set_kw_args)) times.") + error_fn( + "`set` keyword argument was given $(length(set_kw_args)) times.", + ) end for (sym, cone) in ( :PSD => PSDCone(), @@ -2942,7 +2950,7 @@ macro variable(args...) ) if any(isequal(sym), extra) if set !== nothing - _error( + error_fn( "Cannot pass `$sym` as a positional argument because the " * "variable is already constrained to `$set`.", ) @@ -2953,9 +2961,9 @@ macro variable(args...) end for ex in extra if ex == :Int - _set_integer_or_error(_error, infoexpr) + _set_integer_or_error(error_fn, infoexpr) elseif ex == :Bin - _set_binary_or_error(_error, infoexpr) + _set_binary_or_error(error_fn, infoexpr) end end extra = esc.(filter(ex -> !(ex in [:Int, :Bin]), extra)) @@ -2968,12 +2976,12 @@ macro variable(args...) # Easy case - a single variable name_code = base_name else - isa(var, Expr) || _error("Expected $var to be a variable name") + isa(var, Expr) || error_fn("Expected $var to be a variable name") # We now build the code to generate the variables (and possibly the # SparseAxisArray to contain them) - idxvars, indices = Containers.build_ref_sets(_error, var) + idxvars, indices = Containers.build_ref_sets(error_fn, var) if args[1] in idxvars - _error( + error_fn( "Index $(args[1]) is the same symbol as the model. Use a " * "different name for the index.", ) @@ -2990,7 +2998,7 @@ macro variable(args...) end # Code to be used to create each variable of the container. - buildcall = :(build_variable($_error, $info, $(extra...))) + buildcall = :(build_variable($error_fn, $info, $(extra...))) _add_kw_args(buildcall, extra_kw_args) if set !== nothing if isa(var, Symbol) @@ -3011,7 +3019,7 @@ macro variable(args...) ) end end - buildcall = :(build_variable($_error, $scalar_variables, $set)) + buildcall = :(build_variable($error_fn, $scalar_variables, $set)) end buildcall = :(model_convert($model, $buildcall)) new_name_code = if isempty(set_string_name_kw_args) @@ -3074,10 +3082,10 @@ Subject to ``` """ macro NLobjective(model, sense, x) - function _error(str...) + function error_fn(str...) return _macro_error(:NLobjective, (model, sense, x), __source__, str...) end - sense_expr = _moi_sense(_error, sense) + sense_expr = _moi_sense(error_fn, sense) esc_model = esc(model) parsing_code, expr = _parse_nonlinear_expression(esc_model, x) code = quote @@ -3110,28 +3118,28 @@ julia> @NLconstraint(model, [i = 1:3], sin(i * x) <= 1 / i) ``` """ macro NLconstraint(m, x, args...) - function _error(str...) + function error_fn(str...) return _macro_error(:NLconstraint, (m, x, args...), __source__, str...) end esc_m = esc(m) if isexpr(x, :block) - _error("Invalid syntax. Did you mean to use `@NLconstraints`?") + error_fn("Invalid syntax. Did you mean to use `@NLconstraints`?") end # Two formats: # - @NLconstraint(m, a*x <= 5) # - @NLconstraint(m, myref[a=1:5], sin(x^a) <= 5) extra, kw_args, requested_container = Containers._extract_kw_args(args) if length(extra) > 1 || length(kw_args) > 0 - _error("too many arguments.") + error_fn("too many arguments.") end # Canonicalize the arguments c = length(extra) == 1 ? x : gensym() con = length(extra) == 1 ? extra[1] : x # Strategy: build up the code for non-macro add_constraint, and if needed # we will wrap in loops to assign to the ConstraintRefs - idxvars, indices = Containers.build_ref_sets(_error, c) + idxvars, indices = Containers.build_ref_sets(error_fn, c) if m in idxvars - _error( + error_fn( "Index $(m) is the same symbol as the model. Use a different " * "name for the index.", ) @@ -3197,10 +3205,10 @@ subexpression[5]: log(1.0 + (exp(subexpression[2]) + exp(subexpression[3]))) ``` """ macro NLexpression(args...) - _error(str...) = _macro_error(:NLexpression, args, __source__, str...) + error_fn(str...) = _macro_error(:NLexpression, args, __source__, str...) args, kw_args, requested_container = Containers._extract_kw_args(args) if length(args) <= 1 - _error( + error_fn( "To few arguments ($(length(args))); must pass the model and nonlinear expression as arguments.", ) elseif length(args) == 2 @@ -3210,14 +3218,14 @@ macro NLexpression(args...) m, c, x = args end if isexpr(args[2], :block) - _error("Invalid syntax. Did you mean to use `@NLexpressions`?") + error_fn("Invalid syntax. Did you mean to use `@NLexpressions`?") end if length(args) > 3 || length(kw_args) > 0 - _error("To many arguments ($(length(args))).") + error_fn("To many arguments ($(length(args))).") end - idxvars, indices = Containers.build_ref_sets(_error, c) + idxvars, indices = Containers.build_ref_sets(error_fn, c) if args[1] in idxvars - _error( + error_fn( "Index $(args[1]) is the same symbol as the model. Use a " * "different name for the index.", ) @@ -3325,7 +3333,7 @@ julia> value(y[2]) """ macro NLparameter(model, args...) esc_m = esc(model) - function _error(str...) + function error_fn(str...) return _macro_error(:NLparameter, (model, args...), __source__, str...) end pos_args, kw_args, requested_container = Containers._extract_kw_args(args) @@ -3337,20 +3345,22 @@ macro NLparameter(model, args...) end kw_args = filter(kw -> kw.args[1] != :value, kw_args) if !ismissing(value) && length(pos_args) > 0 - _error( + error_fn( "Invalid syntax: no positional args allowed for anonymous " * "parameters.", ) elseif length(pos_args) > 1 - _error("Invalid syntax: too many positional arguments.") + error_fn("Invalid syntax: too many positional arguments.") elseif length(kw_args) > 0 - _error("Invalid syntax: unsupported keyword arguments.") + error_fn("Invalid syntax: unsupported keyword arguments.") elseif ismissing(value) && isexpr(pos_args[1], :block) - _error("Invalid syntax: did you mean to use `@NLparameters`?") + error_fn("Invalid syntax: did you mean to use `@NLparameters`?") elseif ismissing(value) ex = pos_args[1] if !isexpr(ex, :call) || length(ex.args) != 3 || ex.args[1] != :(==) - _error("Invalid syntax: expected syntax of form `param == value`.") + error_fn( + "Invalid syntax: expected syntax of form `param == value`.", + ) end end param, anon = gensym(), true @@ -3358,16 +3368,16 @@ macro NLparameter(model, args...) param, value = pos_args[1].args[2], pos_args[1].args[3] anon = isexpr(param, :vect) || isexpr(param, :vcat) end - index_vars, index_values = Containers.build_ref_sets(_error, param) + index_vars, index_values = Containers.build_ref_sets(error_fn, param) if model in index_vars - _error( + error_fn( "Index $(model) is the same symbol as the model. Use a different " * "name for the index.", ) end code = quote if !isa($(esc(value)), Number) - $(esc(_error))("Parameter value is not a number.") + $(esc(error_fn))("Parameter value is not a number.") end add_nonlinear_parameter($esc_m, $(esc(value))) end diff --git a/src/reified.jl b/src/reified.jl index 8d48b4d3e97..984b817326e 100644 --- a/src/reified.jl +++ b/src/reified.jl @@ -3,30 +3,30 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. -function _build_reified_constraint(_error::Function, lhs, rhs) +function _build_reified_constraint(error_fn::Function, lhs, rhs) set = MOI.Reified(moi_set(rhs)) return VectorConstraint([lhs; jump_function(rhs)], set) end function parse_constraint_call( - _error::Function, + error_fn::Function, ::Bool, ::Union{Val{:(<-->)},Val{:⟺}}, lhs, rhs, ) if !isexpr(rhs, :braces) || length(rhs.args) != 1 - _error( + error_fn( "Invalid right-hand side `$(rhs)` of reified constraint. " * "Expected constraint surrounded by `{` and `}`.", ) end - vectorized, parse_code, build_call = parse_constraint(_error, rhs.args[1]) + vectorized, parse_code, build_call = parse_constraint(error_fn, rhs.args[1]) if vectorized - _error("vectorized constraints cannot be used with reification.") + error_fn("vectorized constraints cannot be used with reification.") end new_build_call = - Expr(:call, :_build_reified_constraint, _error, esc(lhs), build_call) + Expr(:call, :_build_reified_constraint, error_fn, esc(lhs), build_call) return parse_code, new_build_call end diff --git a/src/sd.jl b/src/sd.jl index 77388c7392c..f91f7592ff1 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -277,20 +277,20 @@ function vectorize(matrix::LinearAlgebra.LowerTriangular, ::SquareMatrixShape) return [matrix[i, j] for j in 1:n for i in 1:n] end -function _square_side(_error::Function, variables::Matrix) +function _square_side(error_fn::Function, variables::Matrix) n, m = size(variables) if n != m - _error("Symmetric variables must be square. Got size ($n, $m).") + error_fn("Symmetric variables must be square. Got size ($n, $m).") end return n end -function _vectorize_variables(_error::Function, matrix::Matrix) +function _vectorize_variables(error_fn::Function, matrix::Matrix) n = LinearAlgebra.checksquare(matrix) for j in 1:n for i in 1:j if matrix[i, j] != matrix[j, i] - _error( + error_fn( "Non-symmetric bounds, integrality or starting values for symmetric variable.", ) end @@ -300,7 +300,7 @@ function _vectorize_variables(_error::Function, matrix::Matrix) end """ - build_variable(_error::Function, variables, ::SymmetricMatrixSpace) + build_variable(error_fn::Function, variables, ::SymmetricMatrixSpace) Return a `VariablesConstrainedOnCreation` of shape [`SymmetricMatrixShape`](@ref) creating variables in `MOI.Reals`, i.e. "free" variables unless they are @@ -317,22 +317,22 @@ julia> @variable(model, Q[1:2, 1:2], Symmetric) ``` """ function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, ::SymmetricMatrixSpace, ) - n = _square_side(_error, variables) + n = _square_side(error_fn, variables) set = MOI.Reals(MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(n))) shape = SymmetricMatrixShape(n) return VariablesConstrainedOnCreation( - _vectorize_variables(_error, variables), + _vectorize_variables(error_fn, variables), set, shape, ) end """ - build_variable(_error::Function, variables, ::SkewSymmetricMatrixSpace) + build_variable(error_fn::Function, variables, ::SkewSymmetricMatrixSpace) Return a `VariablesConstrainedOnCreation` of shape [`SkewSymmetricMatrixShape`](@ref) creating variables in `MOI.Reals`, i.e. "free" variables unless they are @@ -349,11 +349,11 @@ julia> @variable(model, Q[1:2, 1:2] in SkewSymmetricMatrixSpace()) ``` """ function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, ::SkewSymmetricMatrixSpace, ) - n = _square_side(_error, variables) + n = _square_side(error_fn, variables) set = MOI.Reals(div(n^2 - n, 2)) shape = SkewSymmetricMatrixShape(n) return VariablesConstrainedOnCreation( @@ -364,7 +364,7 @@ function build_variable( end """ - build_variable(_error::Function, variables, ::HermitianMatrixSpace) + build_variable(error_fn::Function, variables, ::HermitianMatrixSpace) Return a `VariablesConstrainedOnCreation` of shape [`HermitianMatrixShape`](@ref) creating variables in `MOI.Reals`, i.e. "free" variables unless they are @@ -381,24 +381,24 @@ julia> @variable(model, Q[1:2, 1:2] in HermitianMatrixSpace()) ``` """ function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, ::HermitianMatrixSpace, ) - n = _square_side(_error, variables) + n = _square_side(error_fn, variables) set = MOI.Reals( MOI.dimension(MOI.HermitianPositiveSemidefiniteConeTriangle(n)), ) shape = HermitianMatrixShape(n) return VariablesConstrainedOnCreation( - _vectorize_complex_variables(_error, variables), + _vectorize_complex_variables(error_fn, variables), set, shape, ) end """ - build_variable(_error::Function, variables, ::PSDCone) + build_variable(error_fn::Function, variables, ::PSDCone) Return a `VariablesConstrainedOnCreation` of shape [`SymmetricMatrixShape`](@ref) constraining the variables to be positive semidefinite. @@ -414,13 +414,13 @@ julia> @variable(model, Q[1:2, 1:2], PSD) ``` """ function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, ::PSDCone, ) - n = _square_side(_error, variables) + n = _square_side(error_fn, variables) set = MOI.PositiveSemidefiniteConeTriangle(n) - return build_variable(_error, variables, set) + return build_variable(error_fn, variables, set) end function value( @@ -434,7 +434,7 @@ end """ build_constraint( - _error::Function, + error_fn::Function, Q::LinearAlgebra.Symmetric{V, M}, ::PSDCone, ) where {V<:AbstractJuMPScalar,M<:AbstractMatrix{V}} @@ -472,17 +472,21 @@ julia> @constraint(model, Q in PSDCone()) ``` """ function build_constraint( - _error::Function, + error_fn::Function, Q::LinearAlgebra.Symmetric{V,M}, ::PSDCone, ) where {V<:AbstractJuMPScalar,M<:AbstractMatrix{V}} n = LinearAlgebra.checksquare(Q) - return build_constraint(_error, Q, MOI.PositiveSemidefiniteConeTriangle(n)) + return build_constraint( + error_fn, + Q, + MOI.PositiveSemidefiniteConeTriangle(n), + ) end """ build_constraint( - _error::Function, + error_fn::Function, Q::AbstractMatrix{<:AbstractJuMPScalar}, ::PSDCone, ) @@ -502,12 +506,12 @@ julia> @constraint(model, Q in PSDCone()) ``` """ function build_constraint( - _error::Function, + error_fn::Function, Q::AbstractMatrix{<:AbstractJuMPScalar}, ::PSDCone, ) n = LinearAlgebra.checksquare(Q) - return build_constraint(_error, Q, MOI.PositiveSemidefiniteConeSquare(n)) + return build_constraint(error_fn, Q, MOI.PositiveSemidefiniteConeSquare(n)) end """ @@ -599,24 +603,24 @@ function reshape_vector(v::Vector{T}, shape::HermitianMatrixShape) where {T} return LinearAlgebra.Hermitian(matrix) end -function _vectorize_complex_variables(_error::Function, matrix::Matrix) +function _vectorize_complex_variables(error_fn::Function, matrix::Matrix) if any(_is_binary, matrix) || any(_is_integer, matrix) # We would then need to fix the imaginary value to zero. Let's wait to # see if there is need for such complication first. - _error( + error_fn( "Binary or integer variables in a Hermitian matrix are not supported.", ) end n = LinearAlgebra.checksquare(matrix) for j in 1:n if !_isreal(matrix[j, j]) - _error( + error_fn( "Non-real bounds or starting values for diagonal of Hermitian variable.", ) end for i in 1:j if matrix[i, j] != _conj(matrix[j, i]) - _error( + error_fn( "Non-conjugate bounds or starting values for Hermitian variable.", ) end @@ -626,15 +630,15 @@ function _vectorize_complex_variables(_error::Function, matrix::Matrix) end function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, ::HermitianPSDCone, ) - n = _square_side(_error, variables) + n = _square_side(error_fn, variables) set = MOI.HermitianPositiveSemidefiniteConeTriangle(n) shape = HermitianMatrixShape(n) return VariablesConstrainedOnCreation( - _vectorize_complex_variables(_error, variables), + _vectorize_complex_variables(error_fn, variables), set, shape, ) @@ -642,7 +646,7 @@ end """ build_constraint( - _error::Function, + error_fn::Function, Q::LinearAlgebra.Hermitian{V,M}, ::HermitianPSDCone, ) where {V<:AbstractJuMPScalar,M<:AbstractMatrix{V}} @@ -678,18 +682,22 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, Q::AbstractMatrix{<:AbstractJuMPScalar}, cone::HermitianPSDCone, ) - return _error( + return error_fn( "Unable to add matrix in HermitianPSDCone because the matrix is " * "not a subtype of `LinearAlgebra.Hermitian`. To fix, wrap the matrix " * "`H` in `LinearAlgebra.Hermitian(H)`.", ) end -function build_constraint(_error::Function, H::LinearAlgebra.Hermitian, ::Zeros) +function build_constraint( + error_fn::Function, + H::LinearAlgebra.Hermitian, + ::Zeros, +) n = LinearAlgebra.checksquare(H) shape = HermitianMatrixShape(n) x = vectorize(H, shape) @@ -698,7 +706,11 @@ end reshape_set(s::MOI.Zeros, ::HermitianMatrixShape) = Zeros() -function build_constraint(_error::Function, f::LinearAlgebra.Symmetric, ::Zeros) +function build_constraint( + error_fn::Function, + f::LinearAlgebra.Symmetric, + ::Zeros, +) n = LinearAlgebra.checksquare(f) shape = SymmetricMatrixShape(n) x = vectorize(f, shape) @@ -707,8 +719,8 @@ end reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() -function build_constraint(_error::Function, ::AbstractMatrix, ::Nonnegatives) - return _error( +function build_constraint(error_fn::Function, ::AbstractMatrix, ::Nonnegatives) + return error_fn( "Unsupported matrix in vector-valued set. Did you mean to use the " * "broadcasting syntax `.>=` instead? Alternatively, perhaps you are " * "missing a set argument like `@constraint(model, X >= 0, PSDCone())` " * @@ -716,8 +728,8 @@ function build_constraint(_error::Function, ::AbstractMatrix, ::Nonnegatives) ) end -function build_constraint(_error::Function, ::AbstractMatrix, ::Nonpositives) - return _error( +function build_constraint(error_fn::Function, ::AbstractMatrix, ::Nonpositives) + return error_fn( "Unsupported matrix in vector-valued set. Did you mean to use the " * "broadcasting syntax `.<=` instead? Alternatively, perhaps you are " * "missing a set argument like `@constraint(model, X <= 0, PSDCone())` " * @@ -725,8 +737,8 @@ function build_constraint(_error::Function, ::AbstractMatrix, ::Nonpositives) ) end -function build_constraint(_error::Function, ::AbstractMatrix, ::Zeros) - return _error( +function build_constraint(error_fn::Function, ::AbstractMatrix, ::Zeros) + return error_fn( "Unsupported matrix in vector-valued set. Did you mean to use the " * "broadcasting syntax `.==` for element-wise equality? Alternatively, " * "this syntax is supported in the special case that the matrices are " * @@ -735,7 +747,7 @@ function build_constraint(_error::Function, ::AbstractMatrix, ::Zeros) end function build_constraint( - _error::Function, + error_fn::Function, Q::LinearAlgebra.Symmetric{V,M}, set::MOI.AbstractSymmetricMatrixSetTriangle, ) where {V<:AbstractJuMPScalar,M<:AbstractMatrix{V}} @@ -745,7 +757,7 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, Q::AbstractMatrix{<:AbstractJuMPScalar}, set::MOI.AbstractSymmetricMatrixSetSquare, ) @@ -755,7 +767,7 @@ function build_constraint( end function build_constraint( - _error::Function, + error_fn::Function, f::AbstractMatrix{<:AbstractJuMPScalar}, ::Nonnegatives, extra::Union{ @@ -764,11 +776,11 @@ function build_constraint( PSDCone, }, ) - return build_constraint(_error, f, extra) + return build_constraint(error_fn, f, extra) end function build_constraint( - _error::Function, + error_fn::Function, f::AbstractMatrix{<:AbstractJuMPScalar}, ::Nonpositives, extra::Union{ @@ -778,15 +790,15 @@ function build_constraint( }, ) new_f = _MA.operate!!(*, -1, f) - return build_constraint(_error, new_f, extra) + return build_constraint(error_fn, new_f, extra) end function build_variable( - _error::Function, + error_fn::Function, variables::Matrix{<:AbstractVariable}, set::MOI.AbstractSymmetricMatrixSetTriangle, ) - n = _square_side(_error, variables) - x = _vectorize_variables(_error, variables) + n = _square_side(error_fn, variables) + x = _vectorize_variables(error_fn, variables) return VariablesConstrainedOnCreation(x, set, SymmetricMatrixShape(n)) end diff --git a/src/sets.jl b/src/sets.jl index bbce7d0b5f4..7e1e0d5c838 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -22,7 +22,7 @@ abstract type AbstractVectorSet end # Used in `@variable(model, [1:n] in s)` function build_variable( - _error::Function, + error_fn::Function, variables::Vector{<:AbstractVariable}, set::AbstractVectorSet, ) @@ -34,16 +34,16 @@ end # Used in `@constraint(model, func in set)` function build_constraint( - _error::Function, + error_fn::Function, func::AbstractVector, set::AbstractVectorSet, ) - return build_constraint(_error, func, moi_set(set, length(func))) + return build_constraint(error_fn, func, moi_set(set, length(func))) end """ build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector{<:AbstractJuMPScalar}, ::Nonnegatives, extra::Union{MOI.AbstractVectorSet,AbstractVectorSet}, @@ -59,17 +59,17 @@ into ``` """ function build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector{<:AbstractJuMPScalar}, ::Nonnegatives, extra::Union{MOI.AbstractVectorSet,AbstractVectorSet}, ) - return build_constraint(_error, f, extra) + return build_constraint(error_fn, f, extra) end """ build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector{<:AbstractJuMPScalar}, ::Nonpositives, extra::Union{MOI.AbstractVectorSet,AbstractVectorSet}, @@ -85,13 +85,13 @@ into ``` """ function build_constraint( - _error::Function, + error_fn::Function, f::AbstractVector{<:AbstractJuMPScalar}, ::Nonpositives, extra::Union{MOI.AbstractVectorSet,AbstractVectorSet}, ) new_f = _MA.operate!!(*, -1, f) - return build_constraint(_error, new_f, extra) + return build_constraint(error_fn, new_f, extra) end # Handle the case `@constraint(model, X >= 0, Set())`. @@ -264,7 +264,7 @@ See also: [`moi_set`](@ref). abstract type AbstractScalarSet end function build_variable( - _error::Function, + error_fn::Function, variable::AbstractVariable, set::AbstractScalarSet, ) @@ -272,27 +272,27 @@ function build_variable( end function build_variable( - _error::Function, + error_fn::Function, variables::AbstractArray{<:AbstractVariable}, sets::AbstractArray{<:AbstractScalarSet}, ) - return build_variable.(_error, variables, sets) + return build_variable.(error_fn, variables, sets) end function build_variable( - _error::Function, + error_fn::Function, variables::AbstractArray{<:AbstractVariable}, set::AbstractScalarSet, ) - return build_variable.(_error, variables, Ref(set)) + return build_variable.(error_fn, variables, Ref(set)) end function build_constraint( - _error::Function, + error_fn::Function, func::AbstractJuMPScalar, set::AbstractScalarSet, ) - return build_constraint(_error, func, moi_set(set)) + return build_constraint(error_fn, func, moi_set(set)) end """ diff --git a/src/variables.jl b/src/variables.jl index 7a4a16b5088..b309a828def 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -36,12 +36,12 @@ mutable struct _VariableInfoExpr end function _set_lower_bound_or_error( - _error::Function, + error_fn::Function, info::_VariableInfoExpr, lower, ) if info.has_lb - _error("Cannot specify variable lower_bound twice") + error_fn("Cannot specify variable lower_bound twice") end info.has_lb = true info.lower_bound = lower @@ -49,38 +49,42 @@ function _set_lower_bound_or_error( end function _set_upper_bound_or_error( - _error::Function, + error_fn::Function, info::_VariableInfoExpr, upper, ) if info.has_ub - _error("Cannot specify variable upper_bound twice") + error_fn("Cannot specify variable upper_bound twice") end info.has_ub = true info.upper_bound = upper return end -function _fix_or_error(_error::Function, info::_VariableInfoExpr, value) +function _fix_or_error(error_fn::Function, info::_VariableInfoExpr, value) if info.has_fix - _error("Cannot specify variable fixed value twice") + error_fn("Cannot specify variable fixed value twice") end info.has_fix = true info.fixed_value = value return end -function _set_binary_or_error(_error::Function, info::_VariableInfoExpr) +function _set_binary_or_error(error_fn::Function, info::_VariableInfoExpr) if info.binary - _error("'Bin' and 'binary' keyword argument cannot both be specified.") + error_fn( + "'Bin' and 'binary' keyword argument cannot both be specified.", + ) end info.binary = true return end -function _set_integer_or_error(_error::Function, info::_VariableInfoExpr) +function _set_integer_or_error(error_fn::Function, info::_VariableInfoExpr) if info.integer - _error("'Int' and 'integer' keyword argument cannot both be specified.") + error_fn( + "'Int' and 'integer' keyword argument cannot both be specified.", + ) end info.integer = true return @@ -2036,11 +2040,11 @@ struct ComplexVariable{S,T,U,V} <: AbstractVariable info::VariableInfo{S,T,U,V} end -function build_variable(_error::Function, v::ScalarVariable, ::ComplexPlane) +function build_variable(error_fn::Function, v::ScalarVariable, ::ComplexPlane) if _is_binary(v) || _is_integer(v) # We would then need to fix the imaginary value to zero. Let's wait to # see if there is need for such complication first. - _error( + error_fn( "Creation of binary or integer complex variable is not supported.", ) end @@ -2116,11 +2120,11 @@ function add_variable( end function build_variable( - _error::Function, + error_fn::Function, variables::AbstractArray{<:ScalarVariable}, set::ComplexPlane, ) - return build_variable.(_error, variables, Ref(set)) + return build_variable.(error_fn, variables, Ref(set)) end function add_variable( diff --git a/test/test_macros.jl b/test/test_macros.jl index 25158c0d60c..90eeb52c88b 100644 --- a/test/test_macros.jl +++ b/test/test_macros.jl @@ -62,14 +62,14 @@ struct PowerCone{T} exponent::T end function JuMP.build_constraint( - _error::Function, + error_fn::Function, f, set::PowerCone; dual = false, ) moi_set = dual ? MOI.DualPowerCone(set.exponent) : MOI.PowerCone(set.exponent) - return build_constraint(_error, f, moi_set) + return build_constraint(error_fn, f, moi_set) end struct MyConstrType end @@ -77,32 +77,32 @@ struct MyConstrType end struct BadPosArg end function JuMP.build_constraint( - _error::Function, + error_fn::Function, f::GenericAffExpr, set::MOI.EqualTo, ::Type{MyConstrType}; d = 0, ) new_set = MOI.LessThan{Float64}(set.value + d) - return build_constraint(_error, f, new_set) + return build_constraint(error_fn, f, new_set) end struct CustomType end -function JuMP.parse_constraint_head(_error::Function, ::Val{:≔}, lhs, rhs) - return false, :(), :(build_constraint($_error, $(esc(lhs)), $(esc(rhs)))) +function JuMP.parse_constraint_head(error_fn::Function, ::Val{:≔}, lhs, rhs) + return false, :(), :(build_constraint($error_fn, $(esc(lhs)), $(esc(rhs)))) end struct CustomSet <: MOI.AbstractScalarSet end Base.copy(set::CustomSet) = set -function JuMP.build_constraint(_error::Function, func, ::CustomType) - return build_constraint(_error, func, CustomSet()) +function JuMP.build_constraint(error_fn::Function, func, ::CustomType) + return build_constraint(error_fn, func, CustomSet()) end -function JuMP.parse_constraint_call(_error::Function, ::Bool, ::Val{:f}, x) - return :(), :(build_constraint($_error, $(esc(x)), $(esc(CustomType())))) +function JuMP.parse_constraint_call(error_fn::Function, ::Bool, ::Val{:f}, x) + return :(), :(build_constraint($error_fn, $(esc(x)), $(esc(CustomType())))) end const MyVariableTuple{S,T,U,V} = Tuple{VariableInfo{S,T,U,V},Int,Int} @@ -2108,7 +2108,7 @@ struct Issue3514Type{S} <: AbstractConstraint end function JuMP.build_constraint( - _error::Function, + error_fn::Function, f::AffExpr, set::MOI.AbstractScalarSet, extra::Issue3514Tag,