odow / SDDP.jl

Stochastic Dual Dynamic Programming in Julia

Home Page:https://sddp.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

multiple lead time

hghayoom opened this issue · comments

Hi Oscar,

I am trying to develop a model and in the first step, I want to have a model without uncertainty. Then I will improve it.
I have a demand DEM[t], In the first step, I should buy one unit of material and decide when I will start transferring that. therefore I made two pipelines, first from time t=1 to the beginning of the time that transfer begins, Second the pipeline for the time that material takes to go to the destination. the decision variable would be which one of the elements of pipeline1 gets the value of one. ( I want to add in the future uncertainty)
In stage 1 , I forced the prbolem to push buy to one of the elements of pipeline1[i]. then in the next steps I continuously transfer it to the next times untill @constraint(sp,pipeline2[2].out== pipeline1[8].in+Plus-Neg) which sends it to the start of pipeline2.
the same transfer happens for pipeline2 until gets to t=9 which sends it to the required demand=1.

see the picture:

Capture

This is the model:

using SDDP,Plots , Plots.PlotMeasures,Gurobi
import HiGHS,Gurobi
import Distributions
DEM=[0,0,0,0,0,0,0,0,1]
Model_1 = SDDP.LinearPolicyGraph(
    stages = 9,
    sense = :Max,
    upper_bound = 10,
    optimizer = Gurobi.Optimizer,
    )  do sp, t

           @variable(sp, 0<=pipeline1[1:8]<=1, SDDP.State, Int, initial_value = 0)
           @variable(sp, 0<=pipeline2[1:8]<=1, SDDP.State,Int, initial_value = 0)
           @variable(sp, 0 <= buy <= 1, integer = true)
           @variable(sp,0<=x_inventory , SDDP.State, initial_value = 0)
           @variable(sp, u_sell >= 0)
           @variable(sp, u_sell2 >= 0)
           @variables(sp, begin
                Plus>=0
                Neg>=0
                Plus1>=0
                Neg1>=0
                Plus2>=0
                Neg2>=0
            end)
           
            # Buy orders get placed in the pipeline.
           if t==1
                # one of i's in pipeline1[i] gets value =1=Buy as var buy is binary 
                @constraint(sp,pipeline1[1].out+pipeline1[2].out+ pipeline1[3].out+pipeline1[4].out+pipeline1[5].out+ pipeline1[6].out+pipeline1[7].out+pipeline1[8].out==buy )
                
                # fixing pipeline2 so that dont get value
                fix(pipeline2[1].out, 0; force=true)
                fix(pipeline2[2].out, 0; force=true) 
                fix(pipeline2[3].out, 0; force=true) 
                fix(pipeline2[4].out, 0; force=true) 
                fix(pipeline2[5].out, 0; force=true) 
                fix(pipeline2[6].out, 0; force=true) 
                fix(pipeline2[7].out, 0; force=true) 
                fix(pipeline2[8].out, 0; force=true)  
                @constraint(sp,x_inventory.out==x_inventory.in )
                # @stageobjective(sp,-sum(pipeline1[i].out for i in [1:5]))
                # @constraint(sp,  pipeline2[1].out == 0)
                @stageobjective(sp,10000buy)
           else 
            # fixing first element of Pipeline2 
                fix(pipeline2[1].out, 0; force=true) 

                # transfering to the next time for pipe1
                @constraint(sp, [i=2:8], pipeline1[i].out == pipeline1[i-1].in)

                #the last element of pipe1(pipe[8])is equal to first element of pipe2(pipe2[2])
                @constraint(sp,pipeline2[2].out==  pipeline1[8].in+Plus-Neg)
                # transfering to the next time for pipe2
                @constraint(sp, [i=2:8], pipeline2[i].out == pipeline2[i-1].in)
                # Inventory equation
                @constraint(sp,x_inventory.out == x_inventory.in - u_sell2 + pipeline2[8].in+Plus1-Neg1)
                # satisfying demand
                @constraint(sp,DEM[t]==u_sell2-Neg2+Plus2)
                


                @stageobjective(sp, 60*u_sell2-200x_inventory.out-1001*Plus-10000Neg-90000*Plus1-1001Neg1-9001Plus2-1001Neg2)
            end

    end
aaa=1
SDDP.train(Model_1; iteration_limit = 20)
simulations = SDDP.simulate(
    # The trained model to simulate.
    Model_1,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:u_sell,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)
a=1

I cannot get the result that buys in the step one and sends it to the pipeline 1. It uses Neg and Plus pennalties.
I appreciate it if you can help me with that.

I think you need to take another look at your model and the data:

  • To sell in u_sell2, you need inventory in pipeline2[8].
  • Inventory can move one step each time period
  • To satisfy demand in t=9, you'd need to buy pipeline1[8].out` in the first time period
  • it costs 10_000 to buy one unit in t = 1
  • the penalties on Plus is 1001
  • It is cheaper to buy the penalty than to buy in stage 1.

I tidied your model a little, which made it easier for me to see what was going on.

using SDDP
import Gurobi
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = 9,
    sense = :Max,
    upper_bound = 10,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell >= 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:8) == buy)
        for i in 1:8
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 10_000 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true) 
        @constraints(sp, begin
            [i in 2:8], pipeline1[i].out == pipeline1[i-1].in
            [i in 2:8], pipeline2[i].out == pipeline2[i-1].in
            pipeline2[2].out == pipeline1[8].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[8].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            1001 * Plus -
            10_000 * Neg - 
            90_000 * Plus1 -
            1001 * Neg1 - 
            9001 * Plus2 -
            1001 * Neg2,
        )
    end
end

Thanks for your response,
I appreciate it. I worked on that a lot and tried different things.
I want to add uncertainty in time but first I need to solve the deterministic problem.

The problem is Maximizing. In the first stage, I force it to buy because it is always beneficial: Max @stageobjective(sp, 10_000 * buy). In other words, I push the flow in the first stage to the next stages.
it is beneficial to buy in the first stage and get 10000. then sell it in the stage9 and get 60. Using penalties should not happen.
Now in the first stage it should decide to make which pipeline1[i] equal to 1 such a way that it can satisfy the demand in t=9.
To make it better I will change this constraints pipeline2[2].out == pipeline1[8].in + Plus - Neg to pipeline2[4].out == pipeline1[8].in + Plus - Neg. this way pipeline2[8] should get the value equal to one. :
this is the code

using SDDP
import Gurobi
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = 9,
    sense = :Max,
    upper_bound = 10,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell >= 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:8) == buy)
        for i in 1:8
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 10_000 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true) 
        @constraints(sp, begin
            [i in 2:8], pipeline1[i].out == pipeline1[i-1].in
            [i in 2:8], pipeline2[i].out == pipeline2[i-1].in
            pipeline2[4].out == pipeline1[8].in + Plus - Neg  # <---Change(to make shore pipeline1 and pipeline2 cover each other)
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[8].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            1001 * Plus -
            10_000 * Neg - 
            90_000 * Plus1 -
            1001 * Neg1 - 
            9001 * Plus2 -
            1001 * Neg2,
        )
    end
end
SDDP.train(hazard_decision; iteration_limit = 20)
simulations = SDDP.simulate(
    # The trained model to simulate.
    hazard_decision,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:u_sell,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)

Oh, I missed the fact it was sense = :Max.

The issue then is that your upper bound is not a valid upper bound upper_bound = 10, so when making the decision it does not take into consideration the future value.

Thanks,
I tried with different upper bounds but still I could not get the result. everything seems to be correct but I don't know what is the problem
See that.

using SDDP
import Gurobi
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = 9,
    sense = :Max,
    upper_bound = 90000,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:8] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell >= 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:8) == buy)
        for i in 1:8
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 10000 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true) 
        @constraints(sp, begin
            [i in 2:8], pipeline1[i].out == pipeline1[i-1].in
            [i in 2:8], pipeline2[i].out == pipeline2[i-1].in
            pipeline2[4].out == pipeline1[8].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[8].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            1000 * Plus -
            1000 * Neg - 
            1000 * Plus1 -
            1000 * Neg1 - 
            1000 * Plus2 -
            1000 * Neg2,
        )
    end
end
SDDP.train(model; iteration_limit = 120)
simulations = SDDP.simulate(
    # The trained model to simulate.
    model,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:u_sell,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)

your help is appreciated.

I think you need to take another look at your constraints. You have:

            [i in 2:8], pipeline2[i].out == pipeline2[i-1].in
            pipeline2[4].out == pipeline1[8].in + Plus - Neg

This says that pipeline2[4].out == pipeline2[3].in, but that it is also pipeline2[4].out == pipeline1[8].in + Plus - Neg. This is why it needs to use Plus and Neg.

For each pipeline step, you need to ensure that there is a flow balance: flow into the stage = flow out of the stage.

Hi Oscar,
Thanks for your point.
You are exactly correct.
I correct it :

using SDDP
import Gurobi
T=9
Statr=
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = T,
    sense = :Max,
    # It should be upper than your objectives
    upper_bound = 90000,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:T-1) == buy)
        for i in 1:T-1
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 10000 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true) 
        @constraints(sp, begin
            [i in 2:T-1], pipeline1[i].out == pipeline1[i-1].in
            #This is very important here that 5 in [i in 5:8], then in the next line put pipeline2[4].out ==
            [i in 5:T-1], pipeline2[i].out == pipeline2[i-1].in

            pipeline2[4].out == pipeline1[T-1].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[T-1].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            1000 * Plus -
            1000 * Neg - 
            1000 * Plus1 -
            1000 * Neg1 - 
            1000 * Plus2 -
            1000 * Neg2,
        )
    end
end
SDDP.train(model; iteration_limit = 10)
simulations = SDDP.simulate(
    # The trained model to simulate.
    model,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:u_sell,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)
a=1

So it works now?

I honestly appreciate your always help
Yes it worked by your help.
I have another question I will ask about that tomorrow when I made that

Thankssssss

Actually, I want to make uncertainty for the second stage leading time. this is a supply network to assemble a given tailored finished product under lead time uncertainty.
The customer’s request defines this finished product and the set of customizable components needed to customize it(here we have one product for now). There are no product or component stocks available to anticipate this demand and so it is necessary to set a due date for client delivery. The objective function is the expected total cost (ETC), which is composed of tardiness and inventory holding costs.
Therefore at t=1 we should decide about the start time for shipping material in the future considering that the lead time in the next times is random.
I made two pipelines for, First for the time of the start of shipment and second for the shipment of material:
image

I couldn't use these links for accessing to previous steps Link1 and Link2

This is the code:

using SDDP
import Gurobi
T=9
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = T,
    sense = :Max,
    # It should be upper than your objectives
    upper_bound = 90000,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:T-1) == buy)
        for i in 1:T-1
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 10000 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true)

        Ω = [2,3,4,5,6,7,8]
        P = [1/7,1/7,1/7,1/7,1/7,1/7,1/7]
            # delay in shipping
        @variable(sp, Start)

        # delay for this order (shipping+production)
        SDDP.parameterize(sp, Ω, P) do ω
            return JuMP.fix(        @variable(sp, Start)
            , ω)
        end
        

        
        @constraints(sp, begin
            [i in 2:T-1], pipeline1[i].out == pipeline1[i-1].in
            #This is very important here that 5 in [i in 5:8], then in the next line put pipeline2[4].out ==
            [i in Start-1:T-1], pipeline2[i].out == pipeline2[i-1].in

            pipeline2[Start].out == pipeline1[T-1].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[T-1].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            1000 * Plus -
            1000 * Neg - 
            1000 * Plus1 -
            1000 * Neg1 - 
            1000 * Plus2 -
            1000 * Neg2,
        )
    end
end
SDDP.train(model; iteration_limit = 10)
simulations = SDDP.simulate(
    # The trained model to simulate.
    model,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:u_sell,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)

Solving this issue helps me a lot.
Thanks for any feedback.

You can never use the value of a JuMP variable to index variables or constraints (in SDDP, or in regular JuMP models).

Instead, you could do something like this (I didn't test, so might be typos, etc):

@constraint(
    sp,
    c_balance[i in 2:T-1],
    pipeline2[i].out - pipeline1[T-1].in - pipeline2[i-1].in - Plus + Neg == 0
)
Ω = [2, 3, 4, 5, 6, 7, 8]
SDDP.parameterize(sp, Ω) do ω
    for i in 2:8
        if ω == i
            # pipeline2[i].out - pipeline1[T-1].in - Plus + Neg == 0
            # ==> pipeline2[i].out == pipeline1[T-1].in + Plus - Neg
            set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, -1)
            set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, 0)
            set_normalized_coefficient(c_balance[i], Plus, -1)
            set_normalized_coefficient(c_balance[i], Neg, 1)    
        else
            # pipeline2[i].out - pipeline2[i-1].in == 0
            # ==> pipeline2[i].out == pipeline2[i-1].in
            set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, 0)
            set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, -1)
            set_normalized_coefficient(c_balance[i], Plus, 0)
            set_normalized_coefficient(c_balance[i], Neg, 0)
        end
    end
end

Thanks for the code,
I didn't know that we could write a for loop in the body of SDDP.parameterize.
can we have other ifs? For example in the body of the SDDP.parameterize can we have a nested if that checks the amount of a variable in the second layer of nested if (not the realization of ω)?

I rewrote the code based on that. I couldn't get the result.
I think the problem is that in using this trick, in each step it makes a realization of ω. And then based on that IN EACH STEP, the SDDP.parameterize chooses to go to if ω == i once for each step. that's while pipeline1[T-1].in should be appeared once in the whole steps.
See bellow:

using SDDP
import Gurobi
T=9
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = T,
    sense = :Max,
    # It should be upper than your objectives
    upper_bound = 200,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell2 >= 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:T-1) == buy)
        for i in 1:T-1
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @stageobjective(sp, 100 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true)

        @constraint(
        sp,
        c_balance[i in 2:T-1],
        pipeline2[i].out - pipeline1[T-1].in - pipeline2[i-1].in - Plus + Neg == 0

    )
        Ω = [2, 3, 4, 5, 6, 7,8]
        
        SDDP.parameterize(sp, Ω) do ω
            for i in 2:8
                if ω == i
                    # pipeline2[i].out - pipeline1[T-1].in - Plus + Neg == 0
                    # ==> pipeline2[i].out == pipeline1[T-1].in + Plus - Neg
                    set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, -1)
                    set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, 0)
                    set_normalized_coefficient(c_balance[i], Plus, -1)
                    set_normalized_coefficient(c_balance[i], Neg, 1)    
                else
                    # pipeline2[i].out - pipeline2[i-1].in == 0
                    # ==> pipeline2[i].out == pipeline2[i-1].in
                    set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, 0)
                    set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, -1)
                    set_normalized_coefficient(c_balance[i], Plus, 0)
                    set_normalized_coefficient(c_balance[i], Neg, 0)
                end
            end
        end

        

        
        @constraints(sp, begin
            [i in 2:T-1], pipeline1[i].out == pipeline1[i-1].in
            #This is very important here that 5 in [i in 5:8], then in the next line put pipeline2[4].out ==
            # [i in Start-1:T-1], pipeline2[i].out == pipeline2[i-1].in

            # pipeline2[Start].out == pipeline1[T-1].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[T-1].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            60 * u_sell2 -
            200 * x_inventory.out - 
            500 * Plus -
            500 * Neg - 
            500 * Plus1 -
            500 * Neg1 - 
            500 * Plus2 -
            500 * Neg2,
        )
    end
end
SDDP.train(model; iteration_limit = 50)
simulations = SDDP.simulate(
    # The trained model to simulate.
    model,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)

I also tried to use another trick to force the problem to make this constraint appear once. I made a nested if in the SDDP.parameterize. It checks ω == i and also checks that constraint with -1*pipeline1[T-1].in has appeared once by adding three constraints:

using SDDP
import Gurobi
T=9
DEM = [0, 0, 0, 0, 0, 0, 0, 0, 1]
model = SDDP.LinearPolicyGraph(
    stages = T,
    sense = :Max,
    # It should be upper than your objectives
    upper_bound = 200,
    optimizer = Gurobi.Optimizer,
) do sp, t
    @variable(sp, 0 <= pipeline1[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= pipeline2[1:T-1] <= 1, SDDP.State, Int, initial_value = 0)
    @variable(sp, 0 <= buy <= 1, Int)
    @variable(sp, 0 <= x_inventory, SDDP.State, initial_value = 0)
    @variable(sp, u_sell2 >= 0)
    @variable(sp, 0 <=k <= 1, SDDP.State, Int, initial_value = 0)
    @variables(sp, begin
        Plus >= 0
        Neg >= 0
        Plus1 >= 0
        Neg1 >= 0
        Plus2 >= 0
        Neg2 >= 0
        OneVar>=0
    end)
    if t==1
        @constraint(sp, sum(pipeline1[i].out for i in 1:T-1) == buy)
        for i in 1:T-1
            fix(pipeline2[i].out, 0; force = true)
        end
        @constraint(sp, x_inventory.out == x_inventory.in)
        @constraint(sp, k.out == k.in)
        @stageobjective(sp, 100 * buy)
    else 
        fix(pipeline2[1].out, 0; force=true)
        fix(OneVar, 1; force=true)
        @constraint(sp, Transfer,1*k.out -1*k.in==0 )

        @constraint(sp, DemandCheck, 0*k.out -0*OneVar== 0)

        @constraint(
        sp,
        c_balance[i in 2:T-1],
        1*pipeline2[i].out - 1*pipeline1[T-1].in - 1*pipeline2[i-1].in -1* Plus +1* Neg == 0

    )
        Ω = [2, 3, 4, 5, 6, 7,8]
        # Ω = [ 3,4]
        SDDP.parameterize(sp, Ω) do ω
            for i in 2:8
                if ω == i
                    if k.in==0
                        # pipeline2[i].out - pipeline1[T-1].in - Plus + Neg == 0
                        # ==> pipeline2[i].out == pipeline1[T-1].in + Plus - Neg
                        
                        set_normalized_coefficient(Transfer, k.out, 0)
                        set_normalized_coefficient(Transfer, k.in, 0)
                        set_normalized_coefficient(DemandCheck, k.out, 1)
                        set_normalized_coefficient(DemandCheck, OneVar, -1)
                        set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, -1)
                        set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, 0)
                        set_normalized_coefficient(c_balance[i], Plus, -1)
                        set_normalized_coefficient(c_balance[i], Neg, 1) 
                    else
                        set_normalized_coefficient(Transfer, k.out, 1)
                        set_normalized_coefficient(Transfer, k.in, -1)
                        set_normalized_coefficient(DemandCheck, k.out, 0)
                        set_normalized_coefficient(DemandCheck, OneVar, 0)
                        set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, 0)
                        set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, -1)
                        set_normalized_coefficient(c_balance[i], Plus, 0)
                        set_normalized_coefficient(c_balance[i], Neg, 0)

                    end   
                else
                    # pipeline2[i].out - pipeline2[i-1].in == 0
                    # ==> pipeline2[i].out == pipeline2[i-1].in
                    set_normalized_coefficient(Transfer, k.out, 1)
                    set_normalized_coefficient(Transfer, k.in, -1)
                    set_normalized_coefficient(DemandCheck, k.out, 0)
                    set_normalized_coefficient(DemandCheck, OneVar, 0)
                    set_normalized_coefficient(c_balance[i], pipeline1[T-1].in, 0)
                    set_normalized_coefficient(c_balance[i], pipeline2[i-1].in, -1)
                    set_normalized_coefficient(c_balance[i], Plus, 0)
                    set_normalized_coefficient(c_balance[i], Neg, 0)
                end
            end
        end

        

        
        @constraints(sp, begin
            [i in 2:T-1], pipeline1[i].out == pipeline1[i-1].in
            #This is very important here that 5 in [i in 5:8], then in the next line put pipeline2[4].out ==
            # [i in Start-1:T-1], pipeline2[i].out == pipeline2[i-1].in

            # pipeline2[Start].out == pipeline1[T-1].in + Plus - Neg
            x_inventory.out == x_inventory.in - u_sell2 + pipeline2[T-1].in + Plus1 - Neg1
            DEM[t] == u_sell2 - Neg2 + Plus2
        end)
        @stageobjective(
            sp,
            k.out+
            60 * u_sell2 -
            200 * x_inventory.out - 
            500 * Plus -
            500 * Neg - 
            500 * Plus1 -
            500 * Neg1 - 
            500 * Plus2 -
            500 * Neg2,
        )
    end
end
SDDP.train(model; iteration_limit = 30)
simulations = SDDP.simulate(
    # The trained model to simulate.
    model,
    # The number of replications.
    10,
    # A list of names to record the values of.
    sampling_scheme = SDDP.InSampleMonteCarlo(
            terminate_on_cycle = false,
            terminate_on_dummy_leaf = true,
    ),
    [:pipeline1,:buy,:x_inventory,:Plus,:Neg,:Plus1,:Neg1,:Plus2,:Neg2,:u_sell2,:pipeline2],
)

Any idea how to fix this issue is very appreciated.

You cannot add if statements that depend on the variable values. The body of the parameterize function must depend only on the incoming random variable.

Thanks Oscar for always being responsive.
I could resolve it using you your help.

Great. I'll close this issue because it seems like things are fixed, but please open a new issue if you have more questions.