| Quintiq file version 2.0 | 
| #parent: #root | 
| Method FreezeZeroSlack ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   Boolean isunfreeze, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   LibOpt_Scope scope | 
| ) | 
| { | 
|   Description: | 
|   [* | 
|     Check whether all slack variables of a certain type are zero after the first strategy level ( which only contains hard constraints ). | 
|     If so, these slack variables can be frozen at zero as there is no longer a risk of an infeasible solution. | 
|   *] | 
|   TextBody: | 
|   [* | 
|     start := OS::PrecisionCounter();  | 
|     weightlevels := runcontext.WeightLevelNonFinancial();  | 
|     // Inactivate the DemandSlack if it is zero. The DemandSlack indicates an infeasibility in the balance constraint. | 
|     // Even if the total slack is not zero, we still want to freeze all DemandSlacks that are zero | 
|     // If we do not freeze them, the model after the CPLEX presolve will be much bigger and the optimization will take much longer | 
|     justoptimizedhiddenslacklevel := this.CurrentSubOptimizerLevel().IsFirst();   | 
|      | 
|     nextlevelafterzero := this.FirstSubOptimizerLevel().NextSubOptimizerLevel().LevelNumber();  | 
|     debuginfo( 'Action:', ifexpr( isunfreeze, 'un-freezing slack variables', 'freezing slack variables' ) );  | 
|     debuginfo(  'next level after hidden slack = ', nextlevelafterzero );  | 
|     metaisskiplevelone := nextlevelafterzero > 1; // in below we only fix level 1 goals. So if we skip then ok to fix after level 0 | 
|     fixformeta := ( metaisskiplevelone and justoptimizedhiddenslacklevel )  | 
|                          or ( not metaisskiplevelone and this.CurrentSubOptimizerLevel().LevelNumber() = 1 );  | 
|                     | 
|     lookforfixlevelone := ( not runcontext.IsMetaIteration() and this.CurrentSubOptimizerLevel().LevelNumber() = 1) | 
|                             or ( runcontext.IsMetaIteration() and fixformeta );  | 
|      | 
|     pispipsinrun := constnull( ProductInStockingPointInPeriodPlannings, constcontent );  | 
|     if ( justoptimizedhiddenslacklevel or lookforfixlevelone )  | 
|     { | 
|       pispipsinrun := scope.GetPISPIPInOptimizerRun();  | 
|     } | 
|      | 
|     if ( justoptimizedhiddenslacklevel or isunfreeze ) // unfreeze used for sliding windows | 
|     { | 
|       count := 0;  | 
|       varcount := 0; | 
|       traverse( pispipsinrun, Elements.astype( ProductInStockingPointInPeriodPlanningLeaf ), pispip) | 
|       { | 
|         varcount++;  | 
|         demandslackvar := program.DemandSlackVariables().Get( pispip ); | 
|         if( demandslackvar.OptimalValue()  < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           this.FreezeZeroSlackVarUpperBound( demandslackvar, isunfreeze ); | 
|           count++;  | 
|         } | 
|       } | 
|       debuginfo(  ifexpr( isunfreeze, 'un-', '' ), 'frozen nr of DemandSlack variables:', count, '/', varcount, '(', guard( 100* count/varcount, 0.0).Round( 2 ), '%)' );  | 
|      | 
|       // Inactivate the maturation slacks | 
|       traverse( pispipsinrun, Elements, pispip, | 
|                 pispip.ProductInStockingPoint_MP().IsOptShelfLife()  | 
|                 and pispip.ProductInStockingPoint_MP().Product_MP().HasMaturation()  | 
|               ) | 
|       { | 
|         traverse( pispip, ProductInStockingPoint_MP.OutgoingShelfLifeDay, oslday )  | 
|         { | 
|           maturationslackvar := program.MaturationSlackVariables().Get( pispip, oslday );  | 
|           if( maturationslackvar.OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( maturationslackvar, isunfreeze );  | 
|           } | 
|         } | 
|       } | 
|          | 
|       // Inactivate the OperationInputGroup slacks if the TotalSlack is zero | 
|       if( program.TotalSlackVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|       { | 
|         traverse( scope.GetOperationInputGroupInOptimizerRunConst(), Elements, group ) | 
|         { | 
|           operation := group.Operation(); | 
|           periods := this.GetPeriodsForOperation( scope, operation ) | 
|        | 
|           traverse( periods, Elements, period ) | 
|           { | 
|             traverse( group, OperationInput, input, input.GetIsProductInOptimizerRun( runcontext.IsPostProcessing() ) ) | 
|             { | 
|               this.FreezeZeroSlackVarUpperBound( program.OperationInputGroupOverVariables().Get( input, period ), isunfreeze ); | 
|               this.FreezeZeroSlackVarUpperBound( program.OperationInputGroupUnderVariables().Get( input, period ), isunfreeze ); | 
|             } | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the OperationInputSet slacks if the TotalSlack is zero | 
|       if( program.TotalSlackVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|       { | 
|         traverse( scope.GetOperationInputSetInOptimizerRunConst(), Elements, set ) | 
|         { | 
|           operation := set.Operation(); | 
|           periods := this.GetPeriodsForOperation( scope, operation ); | 
|        | 
|           traverse( periods, Elements, period ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.OperationInputSetOverVariables().Get( set, period ), isunfreeze); | 
|             this.FreezeZeroSlackVarUpperBound( program.OperationInputSetUnderVariables().Get( set, period ), isunfreeze ); | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the minimum shift pattern duration slacks. | 
|       if( runcontext.UseShiftOptimization() ) | 
|       { | 
|         traverse( scope.GetUnitInOptimizerRunConst(), Elements, unit, unit.GetUseShiftOptimization() ) | 
|         { | 
|           traverse( unit.GetUnitForShiftOptimization(), Elements.UnitPeriod.astype(UnitPeriodTime), up ) | 
|           { | 
|             traverse( up, Unit.UnitShiftPatternAllowed.ShiftPattern, sp )  | 
|             { | 
|               minshiftdurationslackvar := program.ShiftPatternDurationSlackVariables().Get( sp, up ); | 
|               if( minshiftdurationslackvar.OptimalValue()  < this.SmallestFeasibilityTolerance() ) | 
|               { | 
|                 this.FreezeZeroSlackVarUpperBound( minshiftdurationslackvar, isunfreeze ); | 
|               } | 
|             } | 
|           } | 
|         } | 
|       } | 
|     } | 
|      | 
|     if ( lookforfixlevelone or isunfreeze )  | 
|     { | 
|       unitperiods := construct( UnitPeriods, constcontent ); | 
|        | 
|       traverse( scope.GetUnitPeriodInOptimizerRun(), Elements, up ) | 
|       { | 
|         unitperiods.Add( up ); | 
|         parentups := up.GetAllParentsOfUnitDimension(); | 
|         unitperiods := unitperiods.Union( parentups ); | 
|       } | 
|       unitperiods := unitperiods.Unique(); | 
|        | 
|        | 
|       // Disable the UnitCapacityOverloaded slack if the TotalUnitCapacity slack is zero | 
|       if( weightlevels.UnitCapacityLevel() = 1 ) | 
|       { | 
|         if( program.TotalUnitCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         {   | 
|           // The variables are not defined for parent units with infinite capacity | 
|           traverse( unitperiods, Elements, unitperiod, not unitperiod.Unit().HasChild() or not unitperiod.Unit().HasCapacityTypeInfinite() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( unitperiod.GetCapacityOverloadedVariable( program ), isunfreeze ); | 
|           } | 
|         } | 
|          | 
|         if( program.TotalUnitSecondaryCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           // The variables are only defined for unit periods of type TransportQuantity with secondary capacity defined | 
|           traverse( unitperiods, Elements, up, | 
|                     ( not up.Unit().HasChild() or not up.Unit().HasCapacityTypeInfinite() ) | 
|                     and up.istype( UnitPeriodTransportQuantity ) | 
|                     and up.astype( UnitPeriodTransportQuantity ).HasSecondaryCapacityDefinition() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.UnitSecondaryCapacityOverloadedVariables().Get( up ), isunfreeze ); | 
|           } | 
|         }               | 
|       } | 
|        | 
|        | 
|        | 
|        | 
|       // Inactivate the MinCampaignQtyUnder and MaxCampaignQtyOver slacks if the TotalCampaign slack is zero | 
|       if( not runcontext.UseCampaignSequenceOptimizer() and weightlevels.CampaignLevel() = 1 ) | 
|       { | 
|         if( program.TotalCampaignVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( scope.GetUnitInOptimizerRunConst(), Elements.CampaignType_MP, type ) | 
|           { | 
|             // Only consider this campaign if at least one of its operations is part of this optimizer run | 
|             traverse( type, Campaign_MP, campaign, | 
|                       exists( campaign, OperationInCampaign.Operation, operation, | 
|                               scope.Contains(  operation.OperationInOptimizerRun() ) ) ) | 
|             { | 
|               this.FreezeZeroSlackVarUpperBound( program.MinCampaignQtyUnderVariables().Get( campaign ), isunfreeze );  | 
|        | 
|               this.FreezeZeroSlackVarUpperBound( program.MaxCampaignQtyOverVariables().Get( campaign ), isunfreeze );  | 
|             } | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the TripLotSizeUnder and PTLotSizeUnder and OperationInputLotSizeUnder slacks if the TotalLotSize slack is zero | 
|       if( weightlevels.LotSizeLevel() = 1 ) | 
|       { | 
|         count := 0;  | 
|         if( program.TotalLotSizeVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( scope.GetTripInOptimizerRun(), Elements, trip, | 
|                     trip.DepartureUnitPeriod().Period_MP().IsWithinLotSizeHorizon() ) | 
|           { | 
|             unitperiod := trip.DepartureUnitPeriod(); | 
|             if( guard( unitperiod.HasLotSize(), false ) ) | 
|             { | 
|               var := program.TripLotSizeUnderVariables().Get( trip ); | 
|               this.FreezeZeroSlackVarUpperBound( var, isunfreeze ); | 
|                | 
|               varover := program.TripLotSizeOverVariables().Get( trip ); | 
|               this.FreezeZeroSlackVarUpperBound( varover, isunfreeze ); | 
|               count := count + 2;  | 
|             } | 
|           } | 
|           traverse( scope.GetOperationInOptimizerRun(), Elements, operation, | 
|                     operation.HasLotSize() ) | 
|           { | 
|             periods := this.GetPeriodsForOperation( scope, operation ) | 
|             traverse( periods, Elements, period, | 
|                       period.IsWithinLotSizeHorizon() ) | 
|             { | 
|               varunder := program.PTLotSizeUnderVariables().Get( operation, period ); | 
|               this.FreezeZeroSlackVarUpperBound( varunder, isunfreeze );  | 
|                | 
|               varover := program.PTLotSizeOverVariables().Get( operation, period ); | 
|               this.FreezeZeroSlackVarUpperBound( varover, isunfreeze );  | 
|               count := count + 2;  | 
|             } | 
|           } | 
|                  | 
|           traverse( scope.GetOperationInOptimizerRun(), Elements, operation, | 
|                     operation.HasInputLotSize() ) | 
|           { | 
|             periods := this.GetPeriodsForOperation( scope, operation ); | 
|             periodsfordd := construct( Period_MPs, constcontent ); // periods for dependent demand | 
|              | 
|             traverse( periods, Elements, period ) | 
|             { | 
|               // Consider all relevant periods for the dependent demand | 
|               CapacityPlanningSuboptimizer::GetOperationDependentDemandPeriods( period, operation, &periodsfordd, this.GetPeriodsFromPeriodTaskOperation() ); // output set need not have unique membership | 
|             } | 
|             periodsfordd := periodsfordd.Unique();  | 
|              | 
|             traverse( periodsfordd, Elements, period, | 
|                         period.IsWithinLotSizeHorizon() ) | 
|             { | 
|               traverse( operation, OperationInput, input, | 
|                         ( input.HasRegularProductforOptimizer() or input.GetIsProductInOptimizerRun( runcontext.IsPostProcessing() ) ) | 
|                         and input.ProductInStockingPoint_MP().HasInputLotSize() ) | 
|               { | 
|                 var := program.OperationInputLotSizeUnderVariables().Get( input, period ); | 
|                 this.FreezeZeroSlackVarUpperBound( var, isunfreeze ); | 
|                 count++;  | 
|               } | 
|             } | 
|           } | 
|         } | 
|         debuginfo(  ifexpr(  isunfreeze, 'un-', '' ), 'frozen nr of lot size slack variables:', count );  | 
|       } | 
|        | 
|       pispipswithinvspecification := null( ProductInStockingPointInPeriodPlannings, owning, constcontent ); // only set it if needed (performance)   | 
|       if( weightlevels.MaximumInventoryLevel() = 1 ) | 
|       { | 
|         if( program.TotalMaximumInventoryLevelVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           leafpispips := null( ProductInStockingPointInPeriodPlannings, constcontent, owning );  | 
|           pispipswithinvspecification := this.GetPISPIPsForInventorySpecifications( scope, false, &leafpispips ); | 
|        | 
|           traverse( pispipswithinvspecification, Elements, pispip ) | 
|           { | 
|             if( pispip.GetHasMaxLevel() ) | 
|             { | 
|               maxinvqtyovervar := program.MaxInvQtyOverVariables().Get( pispip ); | 
|               this.FreezeZeroSlackVarUpperBound( maxinvqtyovervar, isunfreeze );  | 
|             } | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the MinInvQtyUnder slack variables if the TotalMinimumInventoryLevel slack is zero | 
|       if( weightlevels.MinimumInventoryLevel() = 1 ) | 
|       { | 
|         if( program.TotalMinimumInventoryLevelVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           if ( isnull( pispipswithinvspecification ) ) // for performance we only set it once and if needed - check if it ha been set | 
|           { | 
|                 leafpispips := null( ProductInStockingPointInPeriodPlannings, constcontent, owning ); | 
|                 pispipswithinvspecification := this.GetPISPIPsForInventorySpecifications( scope, false, &leafpispips ); | 
|           } | 
|           traverse( pispipswithinvspecification, Elements, pispip ) | 
|           { | 
|             if( pispip.GetHasMinLevel() ) | 
|             { | 
|               mininvqtyundervar := program.MinInvQtyUnderVariables().Get( pispip ); | 
|               this.FreezeZeroSlackVarUpperBound( mininvqtyundervar, isunfreeze ); | 
|             } | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Supply specifications can also be applied to higher level units which will not be part of unitsforoptimization | 
|       // The supply specification should only be considered if at least one unit in this optimizer runs influences this supply specification | 
|       supplyspecs := selectset( this.MacroPlan().GetUnits(), Elements.SupplySpecification, supplyspec, | 
|                                 exists( supplyspec, Unit.AllChildren.AsChildren, unit, | 
|                                         scope.Contains(  unit.UnitInOptimizerRun() ) ) ); | 
|        | 
|       // Inactivate the MaxSupplyQtyOver slack variables if the TotalMaximumSupply slack is zero | 
|       if( weightlevels.MaximumSupplyLevel() = 1 ) | 
|       { | 
|         if( program.TotalMaximumSupplyVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( supplyspecs, Elements, supplyspec ) | 
|           { | 
|             var := program.MaxSupplyQtyOverVariables().Get( supplyspec ); | 
|             this.FreezeZeroSlackVarUpperBound( var, isunfreeze); | 
|           } | 
|         } | 
|       } | 
|        | 
|        | 
|        | 
|       // Inactivate the MinSupplyQtyUnder slack variables if the TotalMinimumSupply slack is zero | 
|       if( weightlevels.MinimumSupplyLevel() = 1 ) | 
|       { | 
|         if( program.TotalMinimumSupplyVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( supplyspecs, Elements, supplyspec ) | 
|           { | 
|             var := program.MinSupplyQtyUnderVariables().Get( supplyspec ); | 
|             this.FreezeZeroSlackVarUpperBound( var, isunfreeze ); | 
|           } | 
|         } | 
|       } | 
|        | 
|        | 
|       // Inactivate the UnitCapacityNotMet slack variable if the TotalMinimumUnitCapacity is zero | 
|       if( weightlevels.MinimumUnitCapacityLevel() = 1 ) | 
|       { | 
|         if( program.TotalMinimumUnitCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           // The variables are not defined for parent units with infinite capacity | 
|           traverse( unitperiods, Elements, unitperiod, not unitperiod.Unit().HasChild() or not unitperiod.Unit().HasCapacityTypeInfinite() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( unitperiod.GetCapacityNotMetVariable( program ), isunfreeze ); | 
|           } | 
|         } | 
|          | 
|         if( program.TotalMinimumUnitSecondaryCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           // The variables are only defined for unit periods of type TransportQuantity with secondary capacity defined | 
|           traverse( unitperiods, Elements, up, | 
|                     ( not up.Unit().HasChild() or not up.Unit().HasCapacityTypeInfinite() ) | 
|                     and up.istype( UnitPeriodTransportQuantity ) | 
|                     and up.astype( UnitPeriodTransportQuantity ).HasSecondaryCapacityDefinition() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.UnitSecondaryCapacityNotMetVariables().Get( up ), isunfreeze ) | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the MaxPTQtyOver slack if the TotalProcessMaximumQuantity is zero | 
|       if( weightlevels.ProcessMaximumQuantityLevel() = 1 ) | 
|       { | 
|         if( program.TotalProcessMaximumQuantityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( scope.GetOperationInOptimizerRun(), Elements, operation, | 
|                     operation.HasMaximumQuantity() ) | 
|           { | 
|             periods := this.GetPeriodsForOperation( scope, operation ); | 
|             traverse( periods, Elements, period ) | 
|             { | 
|               var := program.MaxPTQtyOverVariables().Get( operation, period ); | 
|               this.FreezeZeroSlackVarUpperBound( var, isunfreeze ); | 
|             } | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Disable the StockingPointCapacityOverloaded slack if TotalStockingPointCapacity slack is zero | 
|       if( weightlevels.StockingPointCapacityLevel() = 1 ) | 
|       { | 
|         if( program.TotalStockingPointCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( scope.GetStockingPointInPeriodInOptimizerRun(), Elements, spip, | 
|                     not spip.StockingPoint_MP().IsPlannedInfinite() ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.StockingPointCapacityOverloadedVariables().Get( spip ), isunfreeze ); | 
|           } | 
|         } | 
|       } | 
|        | 
|        | 
|        | 
|       // Inactivate the CampaignPeriodOverloaded slack if the TotalUnitCapacity is zero | 
|       if( not runcontext.UseCampaignSequenceOptimizer() and weightlevels.CampaignLevel() = 1 ) | 
|       { | 
|         if( program.TotalUnitCapacityVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|         { | 
|           traverse( scope.GetUnitInOptimizerRunConst(), Elements.CampaignType_MP, type ) | 
|           { | 
|             // Only consider this campaign if at least one of its operations is part of this optimizer run | 
|             traverse( type, Campaign_MP, campaign, | 
|                       exists( campaign, OperationInCampaign.Operation, operation, | 
|                               scope.Contains(  operation.OperationInOptimizerRun() ) ) ) | 
|             { | 
|               // Only consider the campaingperiod if its unit period is part of this optimizer run | 
|               traverse( campaign, PlanningCampaignPeriod, cp, | 
|                         guard( scope.Contains(  cp.UnitPeriod().UnitPeriodInOptimizerRun() ), false ) ) | 
|               { | 
|                 this.FreezeZeroSlackVarUpperBound( program.CampaignPeriodOverloadedVariables().Get( cp ), isunfreeze); | 
|               } | 
|             } | 
|           } | 
|            | 
|           transitionperiods := selectset( scope.GetUnitInOptimizerRun(),  | 
|                                       Elements.TransitionType_MP.Transition_MP.TransitionPeriod_MP,  | 
|                                       tp, | 
|                                       guard( scope.Contains( tp.UnitPeriod().UnitPeriodInOptimizerRun() ), false ) | 
|                                       and guard( tp.UnitPeriod().GetIsWithinCampaignHorizonConstraint(), false ) | 
|                                       and exists( tp.Transition_MP(), OperationInTransition.OperationInTransitionType.Operation, operation, | 
|                                                   scope.Contains( operation.OperationInOptimizerRun() ) ) ) | 
|      | 
|           traverse( transitionperiods, Elements, tperiod ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.TransitionPeriodOverloadedVariables().Get( tperiod ), isunfreeze );  | 
|           } | 
|         } | 
|       } | 
|        | 
|       // Inactivate the blending slacks if the TotalBlending is zero | 
|       if( runcontext.UseBlending() | 
|           and weightlevels.BlendingLevel() = 1 | 
|           and program.TotalBlendingVariables().Get().OptimalValue() < this.SmallestFeasibilityTolerance() ) | 
|       { | 
|         traverse( scope.GetOperationInOptimizerRun(), Elements, operation, operation.IsBlending() ) | 
|         { | 
|           periods := this.GetPeriodsForOperation( scope, operation ); | 
|            | 
|           traverse( periods, Elements, period ) | 
|           { | 
|             this.FreezeZeroSlackVarUpperBound( program.BlendingMaxOverVariables().Get( operation, period ), isunfreeze ); | 
|              | 
|             this.FreezeZeroSlackVarUpperBound( program.BlendingMinUnderVariables().Get( operation, period ), isunfreeze ); | 
|           } | 
|         } | 
|       } | 
|     }   | 
|     end := OS::PrecisionCounter();  | 
|     debuginfo(  'Time for method:', (end-start)/OS::PrecisionCounterFrequency(), 'sec' ); | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |