lazhen
2025-01-09 8afe90b633046db39042aada36b88193062f8cff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Quintiq file version 2.0
#parent: #root
Method NextRe (
  Number periodNumber,
  ProductInStockingPoint_MP pisp
) as owning ProbabilityDistribution
{
  TextBody:
  [*
    /*
      nextre
      calculates the new supply distribution
      ####################################
      this will check how much the target changes from previous and calculate how much we would need to fulfill the change in target and the expected demand (including dependent demand from childs)
      then it will calculate how much we have in upstream location as a total supply (based on nexts and nextre of upstream stockingpoint at period-leadtime)
      taking minimum of what we want and what we can have will give us the distribution of the new supply
    */
    
    dsum := 0.0;  
    prev_target := 0.0; 
    value := MEIO_Engine::GetZeroDistribution();   
    
    // if we do not set previous target, use the earlier (or manually set) value from mp
    if( periodNumber-1 > 0 
        and  periodNumber-1 <= pisp.MEIO_Leadtime() )  
    
    {
      prev_target := pisp.GetPISPIPFromPeriodNumber( periodNumber - 1 ).TargetInQuantity(); 
    }
    //# if we cannot affect the new supply of this period, we use the value from mp
    if( periodNumber <= pisp.MEIO_Leadtime() ) 
    {
      value := DiscreteDistribution::Construct( pisp.GetPISPIPFromPeriodNumber( periodNumber ).NewSupplyQuantity() ); 
    }
    // in other cases we need to calculate how much the siblings need and how much of the parent supply is used for this stocking point
    // we calculate factor based on expected demands
    // we assume infinite supply from root, so this is only needed in a case when parent is not root
    else
    {
      factor := 1.0;
      if( isnull( pisp.MEIO_ParentRoot() ) ) // if dummy supplier node is not the parent.
      {
        siblingDemand := this.ReDistOfWealth( pisp.MEIO_Parent(), periodNumber - pisp.MEIO_Leadtime() ); 
        thisDemand := this.ReDistOfWealth(pisp, periodNumber )+ MEIO_Engine::GetDemandExpectedValue( pisp, periodNumber ); 
    
        if( siblingDemand > 0 )
        {
          factor := thisDemand / siblingDemand;
        }
        // we force the factor to 1 due to bug that was previously in the redist_of_wealth, analysis of the effect of the factor should be done again
        // without this forcing to 1, the effect from the upstream is far too big compared to evaluation
      }
      // collect the expected and variance from the childnodes of the periods based on the leadtime to childs
      listofnodes := MEIO_Engine::GetTreePISP( pisp ); 
      traverse( listofnodes, Elements, leaf ) 
      {
        future_period := periodNumber + this.GetLeadTime( leaf, pisp );
        if( future_period <= this.LastPeriodNumber() )
        {
          dsum := dsum + MEIO_Engine::GetDemandExpectedValue( leaf, future_period ); 
        }
        // if the demand of childs would go over the planning horizon we use demand of the last period (that is, extrapolation of demands as constant)
        else
        {
          dsum := dsum + MEIO_Engine::GetDemandExpectedValue( leaf, this.LastPeriodNumber() ) 
        }
      }
      //# if previous period is set by us, then we use target from it
      if( periodNumber > (pisp.MEIO_Leadtime() + 1 ) )
      {
        prev_target := pisp.GetPISPIPFromPeriodNumber( periodNumber - 1 ).MEIO_Target(); 
      }
      // we only take minimum of "what we want and what we have" if upstream is not root
      if( isnull( pisp.MEIO_ParentRoot() ) ) // if dummy supplier node is not the parent. 
      {
        parentResupply := null( ProbabilityDistribution ); 
        // parentResupply is the the what we can have from parent      
        // if period-leadtime points to period where we cannot control the supply in upstream location, we simply assume sufficient supply in upstream     
        if ( periodNumber-pisp.MEIO_Leadtime() <= pisp.MEIO_Parent().MEIO_Leadtime() ) 
        {
          parentResupply := MEIO_Engine::GetDiracDistribution( this.MEIO_Parameters().InitialLargeValueParentResupply() ); //#huge number, assume we have enough supply if we cannot control upstream
        }
        // if we can control upstream supply, we will assume that we only have what the target in upstream allows us to have as calculated by nexts and nextre at upstream location
        else
        {
          // this is where we make a recursive call - for efficientcy we want to replace it by stored distributions
          
          dis1 := this.NextRe( periodNumber - pisp.MEIO_Leadtime(), pisp.MEIO_Parent() ); 
          dis2 := this.NextS( periodNumber - pisp.MEIO_Leadtime(), pisp.MEIO_Parent() ); 
          parentResupply := ProbabilityDistribution::Sum( dis1, dis2, this.MEIO_Parameters().SampleSizeForDistributions() ); 
        }
        // take minimum of what we can have and what we would want, and make sure it's positive (also don't use in place of "what we can have" a distribution that has negative values)
        
        expr1 := ProbabilityDistribution::Sum( parentResupply, MEIO_Engine::GetDemandDistributionTimesMinusOne( pisp.MEIO_Parent(), periodNumber - pisp.MEIO_Leadtime() )); 
        expr2 := ProbabilityDistribution::Max ( expr1, MEIO_Engine::GetZeroDistribution(), this.MEIO_Parameters().SampleSizeForDistributions() ); 
        factordistr := DiscreteDistribution::Construct( factor ); 
        expr3 := ProbabilityDistribution::Multiply( expr2, factordistr, this.MEIO_Parameters().SampleSizeForDistributions() ); 
        current_target := pisp.GetPISPIPFromPeriodNumber( periodNumber ).MEIO_Target(); 
        valuefordirac := dsum + current_target - prev_target; 
        
        diracdistr := MEIO_Engine::GetDiracDistribution( valuefordirac  ); 
        expr4 := ProbabilityDistribution::Min( diracdistr, expr3, this.MEIO_Parameters().SampleSizeForDistributions() ); 
        value := ProbabilityDistribution::Max( expr4, MEIO_Engine::GetZeroDistribution(), this.MEIO_Parameters().SampleSizeForDistributions() );
        
        /* In R lingo the above computes the following for value: 
        value = Maximum(
                        Minimum(
                                diracDist( dsum + stockingpoint$targets[period] - prev_target),
                                factor*( 
                                        Maximum( parentResupply  - stockingpoint$parent$demands[[period-stockingpoint$leadtime]], 
                                        zeroDist() ) ) 
                                ),
                        zeroDist() 
                        )
        */    
      }
      //if upstream is root, we get what we want    
      else
      {
        current_target := pisp.GetPISPIPFromPeriodNumber( periodNumber ).MEIO_Target(); 
        valuefordiscretedistr := maxvalue( dsum + current_target - prev_target, 0 ) 
        value := MEIO_Engine::GetDiracDistribution( valuefordiscretedistr );  
      }
    }
    return &value;
  *]
}