How to make if in the objective function

Here is the function

        @objective(
            prmx,
            Min,
            sum(
                sum(
                    UNITS[u_k]["cap"] - sum(
                        PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                        for (op_k, op) in _ORDER_PRODUCTs_ALL
                    )
                for  u_k in keys(UNITS)
                )
            for t in TIME
            )
        )

How to change UNITS[u_k][“cap”] to be zero if sum(PRODAMOUNT…) is zero in given objective function?

I would suggest defining the function in general in a less “compact” way, which will make transparent that kind of condition:

function f(parameters?)

  sum1 = sum(  PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                        for (op_k, op) in _ORDER_PRODUCTs_ALL    )
  if sum1 == 0
      ...
  end
  sum2 = sum(UNITS[u_k]["cap"] - sum1...)
  sum3 = ...

end

@objective(prmx,Min,f)

Also, by doing so, I at least would find much easier to take care of types of parameters, avoiding global variables, etc.

3 Likes

thanks, let me try that…

I’ve created function this way:

        function goal()
            total = 0
            for t in TIME
                for (u_k, u) in UNITS
                    sum1 = sum(PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                                        for (op_k, op) in _ORDER_PRODUCTs_ALL)
                    if sum1 > 0
                        total += u["cap"] - sum1
                    end
                end
            end
            return total
        end
      
      
        @objective(
            prmx,
            Min,
            goal
        )

but I am getting error:

ERROR: LoadError: The objective function goal is not supported by JuMP.

And if I write it like this:

        function goal(u_k, t)
            sum1 = sum(PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                                for (op_k, op) in _ORDER_PRODUCTs_ALL)
            if sum1 > 0
                sum1 = UNITS[u_k]["cap"] - sum1
            end
    
            return sum1
        end
      
      
        @objective(
            prmx,
            Min,
            sum(
                sum( 
                    goal(u_k, t)
                for u_k in keys(UNITS)
                )
            for t in TIME
            )
        )

I am then getting this error:

ERROR: LoadError: MethodError: no method matching isless(::Int64, ::GenericAffExpr{Float64,VariableRef})

I would be nice if you provided a minimal code to be tested. It is not clear to me what is the flexibility of the @objective macro of JuMP in accepting functions coded differently. The examples in the manual are quite restrictive.

2 Likes

I started coding in julia / jump just two weeks ago and I am not familiar with its syntax yet. Can you please share with me those examples in the manual?

I can share with you the complete code if you want, just tell me where I should send it to you?

I’ve managed this, but still, it is not working properly:

        function goal(u_k, t)
            sum1 = sum(PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                                for (op_k, op) in _ORDER_PRODUCTs_ALL)
                                    
            if sum1 == 0
            else
                sum1 = (UNITS[u_k]["cap"] - sum1) * UNITS[u_k]["util_cost1"]
            end
    
            return sum1
        end
      
        @objective(
            prmx,
            Min,
            sum(
                sum( 
                    goal(u_k, t)
                for u_k in keys(UNITS)
                )
            for t in TIME
            )
        )

Now the objective value of the solved model is 1172286, but when I output the results it says: 92282

So something is going on in behind that I can’t see…

I am not familiar with JuMP either. I have used Optim instead, and I prefer its syntax in general:

1 Like

Please refrain from posting the same question multiple times:

3 Likes

I understand that you have some parameter that define your objective function. Using Optim you would do something like (which I think it is quite natural):

using Optim
pars = [ 1, 2, 3 ]
f(x) = pars[1]*x[1]^2 + pars[2]*x[2]^2 + (x[3]-pars[3])^2
x0 = rand(3) 
r = optimize(f,x0)

Result:

julia> r = optimize(f,x0)
 * Status: success

 * Candidate solution
    Final objective value:     4.408826e-09

 * Found with
    Algorithm:     Nelder-Mead

 * Convergence measures
    √(Σ(yᵢ-ȳ)²)/n ≤ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    63
    f(x) calls:    124

julia> r.minimizer
3-element Array{Float64,1}:
 -3.578322069066043e-7
 -2.8679363307059896e-5
  3.0000525707764787

This is using the NelderMead algorithm, which does not use derivatives. You can choose a different algorithm, and use automatic derivatives, with, for example:

julia> r = optimize(f,x0,LBFGS(),autodiff=:forward)
 * Status: success

 * Candidate solution
    Final objective value:     7.896313e-33

 * Found with
    Algorithm:     L-BFGS

 * Convergence measures
    |x - x'|               = 3.53e-01 ≰ 0.0e+00
    |x - x'|/|x'|          = 1.18e-01 ≰ 0.0e+00
    |f(x) - f(x')|         = 3.32e-01 ≰ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 4.21e+31 ≰ 0.0e+00
    |g(x)|                 = 2.22e-16 ≤ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    2
    f(x) calls:    5
    ∇f(x) calls:   5

Result:

julia> r.minimizer
3-element Array{Float64,1}:
 -4.163336342344337e-17
  5.551115123125783e-17
  3.0


Here you can define the function naturally using loops, conditionals, etc:

julia> function f(x)
         f = 0.
         for i in 1:length(x)
            f = f + (x[i]-pars[i])^2
         end
         return f
       end
f (generic function with 1 method)

julia> r = optimize(f,x0,LBFGS(),autodiff=:forward)

julia> r.minimizer
3-element Array{Float64,1}:
 1.0
 2.0
 3.0


1 Like

First let me say this is a lot more readable, and therefore much more accessible to folks eager to help!
However, as @odow mentioned in the other thread, assuming _PRODUCTs_ALL or PRODAMOUNT contain variables, this is not possible. The result of sum1 is an expression containing variables, and this expression is not a number, so when you compare it to 0, the answer is always false. This is clearly not the behavior you want. Unfortunately, what you want to have happen is entirely impossible. You need to introduce a binary variable as discussed in that thread that will determine which branch to take based on the value of the expression. This is sometimes called a Big-M formulation, and @odow gave an example of the approach here

1 Like

I was playing with JuMP now, and this is how you can define a function with multiple variables and conditionals, etc. I’m not sure if this is part of your problem though. Anyway, I only found how to do this in other discourse threads:

julia> using JuMP

julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.

julia> @variable(model,x[1:3])
3-element Array{VariableRef,1}:
 x[1]
 x[2]
 x[3]

julia> function f(x)
         f = 0.
         for i in 1:length(x)
            if i == 1 
              f = f + (x[i]-pars[i])^2
            else
              f = f + (x[i]+pars[i])^2
            end
         end
         return f
       end
f (generic function with 1 method)

julia> pars = [ 1, 2, 3 ]
3-element Array{Int64,1}:
 1
 2
 3

julia> @objective(model, Min, f(x))  # note the f(x) ( not simply f )
x[1]² + x[2]² + x[3]² - 2 x[1] + 4 x[2] + 6 x[3] + 14

1 Like

There is a distinction between conditionals on parameters of the model (i.e., conditionals at the model creation time) and conditionals on variable values (i.e., conditionals at the model evaluation/solving time).

The approach you describe works for conditionals on model parameters (creation time). In your example, you build a non-linear objective function (i.e., variables multiply each other) and the assemblage of the objective function uses if statements. Nice, but this is literally just building a (possibly complicated) polynomial before running the model, and passing the polynomial object to JuMP. How the polynomial was built, if conditionals were used or not, is irrelevant to JuMP. JuMP just received an object describing a polynomial and will pass it to the underlying solver. As for any other method call, the called method (i.e., @objective in this case) has no idea how the values passed as arguments to it were produced. The fact @objective is a macro may lead to confusion, as a macro could have a peek at how the values were produced, but this is not the case here.

The problem is that @sogrbilja wants the JuMP model to behave differently depending on which values the x variables assume during the model solving process by JuMP/‘underlying solver’. This behaviour cannot be straightforwardly described by a simple polynomial object, the conditional must be inside the polynomial, inside the formula, but the polynomial/formula object does not support this construction. This is the reason your suggestion

  sum1 = sum(  PRODAMOUNT[op_k, u_k, t] * _PRODUCTs_ALL[op["product"]]["bagSize"] 
                        for (op_k, op) in _ORDER_PRODUCTs_ALL    )
  if sum1 == 0

will not work: sum1 == 0 always evaluates to false. sum1 is a “polynomial” object with variables x which are of unknown value before solving the model, sum1 is never just the number zero. For @sogrbilja to obtain what he wants, the conditional must be inserted inside the “polynomial” object, for the solver to deal with it during the solving process. There is a trick for doing something like this, and it was mentioned by @odow and reiterated by @tomerarnon:

This is sometimes called a Big-M formulation, and @odow gave an example of the approach here

Big-M formulations have weak bounds and may be hard to prove optimality with them, but they are the only alternative I am aware if you want to model the problem as a MILP (Mixed-Integer Linear Program).

3 Likes

Nice. To me it nor clear what he wants, really. For a general, possibly dynamic objective function, I would use a derivative free solver (NelderMead, for example). Still my impression is that there is some confusion on what the function to be minimized is, or how build the model.

Not sure if he still needs help, but if so I’ll begin suggesting him to explain clearly his problem: which are the variables? Which are the parameters? What is the expected outcome? Etc.

Thank you all for giving me directions, the latest modification I’ve made is this:


            @variable(prmx, PRODAMOUNT[op_k in keys(_ORDER_PRODUCTs_ALL), u_k in keys(UNITS), t in TIME], Int, lower_bound = 0)
            @variable(prmx, UNIT_NOT_IN_USE[u_k in keys(UNITS), t in TIME], Bin)

    @constraint(
        prmx,
        [u_k in keys(UNITS), t in TIME],
        sum(PRODAMOUNT[op_k, u_k, t]  * _PRODUCTs_ALL[op["product"]]["bagSize"]
            for (op_k, op) in _ORDER_PRODUCTs_ALL) - UNITS[u_k]["cap"] * (1 - UNIT_NOT_IN_USE[u_k, t]) <= 0
    )   

        @objective(
            prmx,
            Max,
            sum(
                sum( 
                     UNIT_NOT_IN_USE[u_k, t]
                for u_k in keys(UNITS)
                )
            for t in TIME
            )
        )

This way I will try to maximize units that are not in use thus also maximizing the overall utilization of units as well.

Hope this time I did it right :smiley: