Quintiq file version 2.0 
 | 
#parent: #root 
 | 
Method HasConvergencedForFocusLevel ( 
 | 
  LibOpt_Task task 
 | 
) as Boolean 
 | 
{ 
 | 
  TextBody: 
 | 
  [* 
 | 
    value := false;  
 | 
    window := this.ConvergenceWindowSize();  
 | 
    snapshotserror := selectset( task,  
 | 
                                Run.Snapshot.astype( LibOpt_SnapshotError ),  
 | 
                                s, 
 | 
                                true,  
 | 
                                true );  
 | 
     
 | 
    value := snapshotserror.Size() > this.MaxError();  
 | 
     
 | 
    if ( not value and not task.Scope().IsGeneratedByPreProductionSelector() )  
 | 
    { 
 | 
      snapshots := selectsortedset( task, Run.SnapshotMacroPlannerOptimizerAccepted, s, s.FocusLevel() = this.FocusLevel(), -s.SequenceNr() ).SelectFirst( window ); 
 | 
         
 | 
      if ( snapshots.Size() > 0 )  
 | 
      { 
 | 
        runcontext := RunContextForCapacityPlanning::GetRunContextCapacityPlanning( task.Run() ); 
 | 
     
 | 
        islimitingforshiftoptimization := false;  
 | 
        if ( runcontext.UseShiftOptimization() )  
 | 
        { 
 | 
          traverse( task.Run().Optimization().astype( Optimization ).MacroPlan(),  
 | 
                    Unit,  
 | 
                    unit,  
 | 
                    unit.IsInOptimizerPuzzle() )  
 | 
          { 
 | 
            islimitingforshiftoptimization := islimitingforshiftoptimization or exists( unit, UnitShiftPatternAllowed, usa, true, not usa.IsActive() );  
 | 
            if ( not islimitingforshiftoptimization )  
 | 
            { 
 | 
              traverse( unit.GetAllChildren(), Elements, child )  
 | 
              { 
 | 
                islimitingforshiftoptimization := islimitingforshiftoptimization or exists( child, UnitShiftPatternAllowed, usa, true, not usa.IsActive() );  
 | 
              } 
 | 
            } 
 | 
          } 
 | 
        }     
 | 
         
 | 
        iscampaignsequencing_or_shiftopt := runcontext.UseCampaignSequenceOptimizer() or islimitingforshiftoptimization; // don't take shortcuts because we limit binaries  
 | 
         
 | 
        lastsnapshots := snapshots.SelectFirst( minvalue(  snapshots.Size(), window ) );  
 | 
        lastsnapshot := snapshots.Element( 0 );    
 | 
         
 | 
        maxbestscorelevel := max( lastsnapshots, Elements, s, true, RealVector::Construct( s.BestFoundScoresForLevels() ).Get( this.FocusLevel() ) );  
 | 
        minbestscorelevel := min( lastsnapshots, Elements, s, true, RealVector::Construct( s.BestFoundScoresForLevels() ).Get( this.FocusLevel() ) );  
 | 
        movement_relative := guard( ( maxbestscorelevel - minbestscorelevel ) / ( 0.5 * abs( maxbestscorelevel) + 0.5* abs( minbestscorelevel )), 0.0 );   
 | 
        absolute_movement := abs( maxbestscorelevel - minbestscorelevel ); // around 0 relative movement becomes noisy 
 | 
        movement := minvalue(  movement_relative, absolute_movement );  
 | 
        lastsnapshot.MaxScoreConvergenceWindow( maxbestscorelevel );  
 | 
        lastsnapshot.MinScoreConvergenceWindow( minbestscorelevel );  
 | 
        lastsnapshot.MovementScoreConvergenceWindow( movement );  
 | 
        debuginfo( 'maxscore=', maxbestscorelevel, 'minscore = ', minbestscorelevel, 'movement=', movement, '( stop when < ', this.ConvergenceThreshold(), ')' );  
 | 
        avgsubopttime := average( lastsnapshots, Elements, s, true, s.DurationSecondsSubOptimizer() );  
 | 
                                
 | 
        if ( movement < this.ConvergenceThreshold() and snapshots.Size() >= window )   
 | 
        { 
 | 
          value := true;  
 | 
          lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'Converged. Avg time=' + [String] avgsubopttime );   
 | 
        } 
 | 
         
 | 
        if ( not value and this.HasTimedOutLocalDuration( task ) )  
 | 
        { 
 | 
          lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'iteration time out' );   
 | 
          value := true;  
 | 
        } 
 | 
         
 | 
        if ( not value and this.HasTimedOutGlobalDuration( task ) )  
 | 
        { 
 | 
          lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'global time out' );   
 | 
          value := true;  
 | 
        } 
 | 
         
 | 
        // check for some shortcuts to exit the level 
 | 
        if ( not value )  
 | 
        { 
 | 
          mp := task.Run().Optimization().astype( Optimization ).MacroPlan();  
 | 
          enoughscalinginfocollected := not this.IsEnabledAutoScaling()  
 | 
                                        or counter( mp, Optimization.AlgorithmScaling.ScaleFactorsSuggestedForLevel, s, true, s.FocusLevel() = this.FocusLevel() ) > 0;  
 | 
     
 | 
          // check if at best possible KPI (if it is known) 
 | 
          level := select(  mp, StrategyMacroPlan.StrategyLevelMacroPlan, slm, true, slm.Level() = this.FocusLevel() );  
 | 
          lastsnapshot := maxselect(  task, Run.Snapshot.astype( SnapshotMacroPlannerOptimizer ), s, s.IsAccepted() and s.FocusLevel() = this.FocusLevel(), s.TimeStamp() ); 
 | 
          if ( not isnull( lastsnapshot ) )  
 | 
          {  
 | 
            rv := RealVector::Construct( lastsnapshot.RollbackKPI() );  
 | 
            lastscore := rv.Get( this.FocusLevel() );   
 | 
            value := guard( abs( level.KPIUpperBound() - lastscore) < this.CutOffForOptimal(), false ) and enoughscalinginfocollected;  
 | 
            if ( value )  
 | 
            { 
 | 
                lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'level kpi (' + [String] lastscore + ')within threhold (' + [String]this.CutOffForOptimal() + ') of best possible value ( ' + [String] level.KPIUpperBound() + ')' );   
 | 
            } 
 | 
            else  
 | 
            { 
 | 
               
 | 
              noincrease := guard( snapshots.Element( 1 ).NrOperationPeriodTaskInOptimizerRun() = lastsnapshot.NrOperationPeriodTaskInOptimizerRun()  
 | 
                                   and snapshots.Element( 1 ).NrProductInTripOptimizerRun() = lastsnapshot.NrProductInTripOptimizerRun(),  
 | 
                                   false );  
 | 
              // check if last was full puzzle size and optimal cplex 
 | 
              value := lastsnapshot.NrCampaignSequenceCombisTotal() = lastsnapshot.NrCampaignSequenceCombisActive() 
 | 
                       and lastsnapshot.NumberOfPISPIPPlanningGlobal() = lastsnapshot.NrPISPIPPlanningOptimizerRun() 
 | 
                       and lastsnapshot.NrOperationPeriodTaskInOptimizerRun() >= lastsnapshot.NrOperationPeriodTaskInGlobal()  
 | 
                       and lastsnapshot.NrProductInTripOptimizerRun() >= lastsnapshot.NrProductInTripGlobal() 
 | 
                       and lastsnapshot.AllLevelsOptimal() 
 | 
                       and lastsnapshot.IsAccepted() 
 | 
                       and this.FocusLevel() = lastsnapshot.FocusLevel() 
 | 
                       and noincrease 
 | 
                       and enoughscalinginfocollected 
 | 
                       and not iscampaignsequencing_or_shiftopt 
 | 
                       and not lastsnapshot.Comment() ~ Translations::LibOpt_Rollback();  
 | 
              if ( value )  
 | 
              { 
 | 
                  lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'neighborhood at max size and cplex optimal' );   
 | 
              } 
 | 
              else 
 | 
              { 
 | 
                value := this.StopForBenchmarking( task, lastsnapshot, runcontext, lastscore ) and enoughscalinginfocollected;    
 | 
                if ( value )  
 | 
                { 
 | 
                  lastsnapshot.StopCriteriumInfo( lastsnapshot.StopCriteriumInfo() + 'stopping for benchmark purpose' );    
 | 
                } 
 | 
              } 
 | 
            } 
 | 
          } 
 | 
        } // end look for shortcuts  
 | 
      }     
 | 
    } 
 | 
     
 | 
    return value; 
 | 
  *] 
 | 
  InterfaceProperties { Accessibility: 'Module' } 
 | 
} 
 |