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' }
|
}
|