陈清红
2025-04-14 880f3c0257eeb8c37761d484258fdd102a369a19
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
Quintiq file version 2.0
#parent: #root
StaticMethod IndependentPathStrategy (POAAlgorithm poa) as POAAlgorithm
{
  TextBody:
  [*
    LibOpt_SuboptimizerPOA::StrategyStart( poa );
    // -------------------------------------------------------------------------- //
    // Strategy for independent path problems, for example vehicle routing        //
    // problems                                                                   //
    // -------------------------------------------------------------------------- //
    
    strategy := poa.Strategy();
    
    // ----- Parameters for the strategy -----
    // String used to identify the expression used for time in this POA. This
    // strategy needs an expression for time.
    timeexpressionname := LibOpt_SuboptimizerPOA::GetStrategySetting_TimeExpressionName( poa, 'time' );
    // The total duration of iteratively improving the solution in this strategy.
    // This does not include the time spend on creating and improving an initial
    // solution. More time spend on iterive improvement will give a higher chance
    // on better results.
    iterativeduration := LibOpt_SuboptimizerPOA::GetStrategySetting_IterativeDuration( poa, Duration::Seconds( 60 ) );
    // The number of threads used throughout the strategy execution. The threads
    // are all busy from begin till end.
    nrthreads := LibOpt_SuboptimizerPOA::GetStrategySetting_NrThreads( poa, 3 );
    
    // ----- Advanced Parameters for the strategy -----
    // The number of moves (plan possibilities of planelements) that make it in the
    // final population. Moves that are estimated to be better have a higher chance
    // to be in the population. All moves in the population are fully propagated,
    // after which the best move is carried out. A higher number will mean more
    // precise results because full propagation gives the precise score of a move.
    // This comes at the cost of more time needed in propagation.
    maxpopulation := LibOpt_SuboptimizerPOA::GetStrategySetting_MaxPopulation( poa, 3 );
    // The percentage of available planelements that is destructed by the random
    // destruction action
    randomdestructionpercentage := LibOpt_SuboptimizerPOA::GetStrategySetting_RandomDestructionPercentage( poa, 5.0 );
    // The percentage of paths that is destructed by the path destruction action
    pathdestructionpercentage := LibOpt_SuboptimizerPOA::GetStrategySetting_PathDestructionPercentage( poa, 0.0 );
    // The chance during one iteration that the path destuction action is executed.
    // Where as a minimum 1 path is destructed.
    pathdestructionchance := LibOpt_SuboptimizerPOA::GetStrategySetting_PathDestructionChance( poa, 0.0 );
    // The percentage of available planelements that is destructed by the area stop
    // destruction action
    areastopdestructionpercentage := LibOpt_SuboptimizerPOA::GetStrategySetting_AreaStopDestructionPercentage( poa, 17.0 );
    // The chance during one iteration that the area stop destruction action is
    // executed
    areastopdestructionchance := LibOpt_SuboptimizerPOA::GetStrategySetting_AreaStopDestructionChance( poa, 0.5 );
    // The number of unplanned elements that is considered by the random
    // construction action
    randomconstructiongroupsize := LibOpt_SuboptimizerPOA::GetStrategySetting_RandomConstructionGroupSize( poa, 7 );
    // The duration between the possibility of a reset to the globally best solution
    // found.
    durationbetweenrestarttoglobal := LibOpt_SuboptimizerPOA::GetStrategySetting_DurationBetweenRestartToGlobal( poa, Duration::Seconds( 3.5 ) );
    // The chance that a solution is restarted to the globally best solution
    restarttoglobalchance := LibOpt_SuboptimizerPOA::GetStrategySetting_RestartToGlobalChance( poa, 0.6 );
    
    // The duration between logging information during the iterative strategy
    durationbetweeniterativereports := LibOpt_SuboptimizerPOA::GetStrategySetting_DurationBetweenIterativeReports( poa, Duration::Seconds( 5 ) );
    // Log file name prefix. '' means no logging
    logfilename := LibOpt_SuboptimizerPOA::GetStrategySetting_LogFileName( poa, '' );
    // Toggle info statements
    printinfo := LibOpt_SuboptimizerPOA::GetStrategySetting_PrintInfo( poa, true );
    
    // -------------------------------------------------------------------------- //
    // Print parameter values and initial score                                   //
    // -------------------------------------------------------------------------- //
    
    if( printinfo )
    {
      info( 'parameter timeexpressionname =' , timeexpressionname );
      info( 'parameter iterativeduration =' , iterativeduration );
      info( 'parameter nrthreads =' , nrthreads );
      info( 'parameter maxpopulation =' , maxpopulation );
      info( 'parameter randomdestructionpercentage =' , randomdestructionpercentage );
      info( 'parameter pathdestructionpercentage =' , pathdestructionpercentage );
      info( 'parameter pathdestructionchance =' , pathdestructionchance );
      info( 'parameter areastopdestructionpercentage =' , areastopdestructionpercentage );
      info( 'parameter areastopdestructionchance =' , areastopdestructionchance );
      info( 'parameter randomconstructiongroupsize =' , randomconstructiongroupsize );
      info( 'parameter durationbetweenrestarttoglobal =' , durationbetweenrestarttoglobal );
      info( 'parameter restarttoglobalchance =' , restarttoglobalchance );
      info( 'parameter logfilename =' , logfilename );
      info( 'initial score', strategy.BestSolution().TotalScore() );
    }
    
    // -------------------------------------------------------------------------- //
    // Initialize general settings                                                //
    // -------------------------------------------------------------------------- //
    
    // For all constraints change MaxDownStreamDepth to MaxNumber where applicable.
    // For independent path problems MaxNumber gives generally the best results.
    {
      traverse( poa, Constraints.astype( POAEndConstraint ), c, c.MaxDownStreamDepth() <> Number::MaxNumber() )
      {
        c.MaxDownStreamDepth( Number::MaxNumber() );
        debuginfo( 'Warning: POAEndConstraint', c.Role(), 'MaxDownStreamDepth was changed to MaxNumber, because that will improve the move estimates' );
      }
      
      traverse( poa, Constraints.astype( POACapacityConstraint ), c, c.MaxDownStreamDepth() <> Number::MaxNumber() )
      {
        c.MaxDownStreamDepth( Number::MaxNumber() );
        debuginfo( 'Warning: POACapacityConstraint', c.Role(), 'MaxDownStreamDepth was changed to MaxNumber, because that will improve the move estimates' );
      }
      
      traverse( poa, Constraints.astype( POAInTimeConstraint ), c, c.MaxDownStreamDepth() <> Number::MaxNumber() )
      {
        c.MaxDownStreamDepth( Number::MaxNumber() );
        debuginfo( 'Warning: POAInTimeConstraint', c.Role(), 'MaxDownStreamDepth was changed to MaxNumber, because that will improve the move estimates' );
      }
    }
    
    timeexpression := select( poa, Expressions, e, true, e.Role() = timeexpressionname );
    verify( not isnull( timeexpression ), 
    "The strategy needs an expression that is called 'time' and will be used as the expression representing time
    " );
    
    planstrategy := strategy.PlanStrategy();
    // Apply parameter
    planstrategy.MaxPopulation( maxpopulation );
    // the population of moves per path is infinite
    planstrategy.MaxPathPopulation( maxpopulation );
    
    // -------------------------------------------------------------------------- //
    // Initial Strategy                                                           //
    // -------------------------------------------------------------------------- //
    //
    // The initial strategy will be used to plan all remaining planelements, and do
    // some initial improvement. After that improvement will only be made by
    // iterativestrategy.
    
    initialstrategy := strategy.NewMetaStrategy( 'initialstrategy' );
    if( logfilename <> '' )
    {
      initialstrategy.LogFileName( logfilename + '_InitialStrategy', true );
    }
    
    // The only step used in the initial strategy.
    initialstep := initialstrategy.NewStep( 'initialstep' );
    // Room for one start solution.
    initialstep.StartSolutionPool().Capacity( 1 );
    // Use initial solution as the single starting solution.
    initialstep.StartSolutionPool().Add( poa.Solution() );
    // The step is repeated a number of times equal to the number of threads, each
    // time starting from the same initial solution. Each repetition is carried out
    // by its own thread.
    initialstep.Repetitions( nrthreads );
    // The step happens once in each thread. It will not be carried out on the
    // results of a previous iteration.
    initialstep.MaximumIterationsActions( 1 );
    // 1 000 000 seconds pratically infinity
    initialstep.MaximumSecondsActions( 1000000.0 );
    // The result of each thread is stored.
    initialstep.ResultSolutionPool().Capacity( nrthreads );
    
    actions := initialstep.Actions();
    
    // Use insertion heuristic, which choses a path, and plans one element a time on
    // that path based on the time expression. When no more elements fit on the path
    // a new path is chosen. This continues until there are not paths left,
    insertionheuristic := actions.NewInsertionHeuristic( timeexpression );
    // Try to plan all unplanned planelements.
    insertionheuristic.Nodes( Number::MaxNumber() );
    
    // Use one node improvement action, which selects any planned or unplanned
    // element and tries to find a better position for that element. Depending
    // on the improvement made, a new element is selected and the process
    // repeats itself.
    onenodeimpr := actions.NewRandomOneNodeImprovement();
    // By setting to 0.0 any improvement will make the process repeat itself.
    onenodeimpr.MinimalImprovementPercentage().Set( 0.0 );
    
    // Start execution of the strategy and display the duration
    executestarttime := DateTime::ActualTime();
    debuginfo( 'execute initial actions' );
    initialstrategy.Execute( nrthreads, Real::MaxReal() );
    debuginfo( 'initial actions execution duration', DateTime::ActualTime() - executestarttime );
    
    // -------------------------------------------------------------------------- //
    // Iterative Strategy                                                         //
    // -------------------------------------------------------------------------- //
    //
    // iterativestrategy will repeatedly run on the start solution generated by
    // initialstrategy.
    
    iterativestrategy := strategy.NewMetaStrategy( 'iterativestrategy' );
    if( logfilename <> '' )
    {
      iterativestrategy.LogFileName( logfilename + '_IterativeStrategy', true );
    }
    
    // The only step used in the iterative strategy.
    iterativestep := iterativestrategy.NewStep( 'iterativestep' );
    // Use the result of each thread for the initial stategy as starting point for
    // the iterative strategy. One thread will start for each of these starting
    // points.
    iterativestep.StartSolutionPool().Use( initialstep.ResultSolutionPool() );
    // There is no limit on the number of times this step starts again within a
    // single thread.
    iterativestep.MaximumIterationsActions( Number::MaxNumber() );
    // Execution of the step must not take longer then the reporting duration.
    iterativestep.MaximumSecondsActions( durationbetweenrestarttoglobal.MinutesAsReal() * 60.0 );
    // The result of each thread is stored.
    iterativestep.ResultSolutionPool().Capacity( nrthreads );
    
    actions := iterativestep.Actions();
    
    // The number of elements available for planning.
    nrplanelements := poa.PlanElements( relsize );
    
    randomstopdestr := actions.NewRandomStopDestruction();
    randomstopdestr.SegmentsMinimum( round( randomdestructionpercentage / 100.0 * nrplanelements )  );
    randomstopdestr.SegmentsMaximum( round( randomdestructionpercentage / 100.0 * nrplanelements )  );
    randomstopdestr.SegmentLengthMaximum( 1 );
    randomstopdestr.SegmentLengthMinimum( 1 );
    
    // The number of paths available for planning.
    nrpaths := sum( poa, PathTypes, t, true, t.MaxCount() );
    
    // Use path destruction, which randomly chooses a number of paths to be
    // destructed.
    pathdestr1 := actions.NewRandomPathDestruction();
    // The number of chosen paths is pathdestructionpercentage% of the available
    // paths. Rounded, so possibly zero.
    pathdestr1.Paths( maxvalue( 1, round( pathdestructionpercentage / 100.0 * nrpaths ) ) );
    pathdestr1.ExecutionChance().Set( pathdestructionchance );
    
    // Use area stop destruction, which choses one stop to be destructed and
    // destructs the area surrounding that node. The length of the time expression
    // transition between stops determines if two stops belong to the same area.
    areastopdestr := actions.NewRandomAreaStopDestruction( timeexpression );
    // By specifing the number of stops
    areastopdestr.Stops( round( areastopdestructionpercentage / 100.0 * nrplanelements ) );
    areastopdestr.ExecutionChance().Set( areastopdestructionchance );
    
    // Use random construction, which
    // (1) chooses a number of unplanned elements equal to
    //     'randomconstructiongroupsize'.
    // (2) determines the best move for each element by making estimates for all
    //     moves and propagating the moves that make it in the population.
    // (3) carry out the best move of the element that was best
    randomcons := actions.NewRandomConstruction();
    // Set the number of unplanned elements to consider.
    randomcons.GroupSize( randomconstructiongroupsize );
    
    onenodeimpr := actions.NewRandomOneNodeImprovement();
    // By setting to 0.0 any improvement will make the process repeat itself.
    onenodeimpr.MinimalImprovementPercentage().Set( 0.0 );
    
    // Keep executing the iterative meta strategy until the time limit is reached
    iterativeendtime := DateTime::ActualTime() + iterativeduration;
    lastreporttime := DateTime::ActualTime() - durationbetweeniterativereports;
    while( DateTime::ActualTime() < iterativeendtime )
    {
      executeduration := minvalue( durationbetweenrestarttoglobal, iterativeendtime - DateTime::ActualTime() );
      
      // Call meta strategy execution
      iterativestrategy.Execute( nrthreads, executeduration.MinutesAsReal() * 60.0 );
      
      // Fill start solution
      traverse( iterativestep.ResultSolutionPool().UseSolutions(), Solutions, s )
      {
        if( Real::Random() < restarttoglobalchance )
        {
          iterativestep.StartSolutionPool().Add( strategy.BestSolution() );
        }
        else
        {
          iterativestep.StartSolutionPool().Add( s );
        }
      }
      
      if( printinfo and DateTime::ActualTime() - lastreporttime >= durationbetweeniterativereports )
      {
        lastreporttime := DateTime::ActualTime();
        
        info( 'iterative time remaining', iterativeendtime - DateTime::ActualTime() );
        
        i := 0;
        traverse( iterativestep.StartSolutionPool(), Solutions, s )
        {
          info( i, ': iterative result solution score', s.TotalScore() );
          i++;
        }
        
        info( 'iterations', actions.Iterations().Value() );
        info( 'improvement iterations', actions.IterationsImproved().Value() );
        
        // After logging reset statistics for the next iteration
        actions.ResetStatistics();
      }
    }
    LibOpt_SuboptimizerPOA::StrategyFinish( poa );
    return poa;
  *]
}