33
44#include < algorithm>
55#include < cmath>
6+ #include < limits>
67
78#include " globals.h"
89#include " place_macro.h"
10+ #include " vpr_context.h"
11+ #include " vpr_error.h"
912#include " vpr_types.h"
1013#include " place_util.h"
1114#include " placer_state.h"
@@ -291,11 +294,145 @@ PlacementAnnealer::PlacementAnnealer(const t_placer_opts& placer_opts,
291294}
292295
293296float PlacementAnnealer::estimate_starting_temperature_ () {
297+
294298 if (placer_opts_.anneal_sched .type == e_sched_type::USER_SCHED) {
295299 return placer_opts_.anneal_sched .init_t ;
296300 }
297301
298- const auto & cluster_ctx = g_vpr_ctx.clustering ();
302+ switch (placer_opts_.anneal_init_t_estimator ) {
303+ case e_anneal_init_t_estimator::COST_VARIANCE:
304+ return estimate_starting_temp_using_cost_variance_ ();
305+ case e_anneal_init_t_estimator::EQUILIBRIUM_EST:
306+ return estimate_equilibrium_temp_ ();
307+ default :
308+ VPR_FATAL_ERROR (VPR_ERROR_PLACE,
309+ " Unrecognized initial temperature estimator type" );
310+ };
311+ }
312+
313+ float PlacementAnnealer::estimate_equilibrium_temp_ () {
314+ const ClusteringContext& cluster_ctx = g_vpr_ctx.clustering ();
315+
316+ // Determines the block swap loop count.
317+ // TODO: Revisit this. We may be able to get away with doing fewer trial
318+ // swaps. That or we may be able to get a more accurate initial
319+ // temperature by doing more moves.
320+ int move_lim = std::min (annealing_state_.move_lim_max , (int )cluster_ctx.clb_nlist .blocks ().size ());
321+
322+ // Perform N trial swaps and collect the change in cost for each of these
323+ // swaps. Accepted swaps are swaps which resulted in a negative change in
324+ // cost, rejected swaps are swaps which resulted in a positive change in
325+ // cost.
326+ std::vector<double > accepted_swaps;
327+ std::vector<double > rejected_swaps;
328+ accepted_swaps.reserve (move_lim);
329+ rejected_swaps.reserve (move_lim);
330+ for (int i = 0 ; i < move_lim; i++) {
331+ bool manual_move_enabled = false ;
332+ #ifndef NO_GRAPHICS
333+ // Checks manual move flag for manual move feature
334+ t_draw_state* draw_state = get_draw_state_vars ();
335+ if (draw_state->show_graphics ) {
336+ manual_move_enabled = manual_move_is_selected ();
337+ }
338+ #endif /* NO_GRAPHICS*/
339+
340+ t_swap_result swap_result = try_swap_ (*move_generator_1_,
341+ placer_opts_.place_algorithm ,
342+ manual_move_enabled);
343+
344+ if (swap_result.move_result == e_move_result::ACCEPTED) {
345+ accepted_swaps.push_back (swap_result.delta_c );
346+ // TODO: Look into not actually accepting these.
347+ swap_stats_.num_swap_accepted ++;
348+ } else if (swap_result.move_result == e_move_result::ABORTED) {
349+ // Note: We do not keep track of the change in cost due to aborted
350+ // swaps. These are not interesting for this approach.
351+ swap_stats_.num_swap_aborted ++;
352+ } else {
353+ rejected_swaps.push_back (swap_result.delta_c );
354+ swap_stats_.num_swap_rejected ++;
355+ }
356+ }
357+
358+ // Computed the total change in cost due to accepted swaps.
359+ double total_accepted_cost = 0.0 ;
360+ for (double accepted_cost : accepted_swaps) {
361+ total_accepted_cost += accepted_cost;
362+ }
363+
364+ // Perform a binary search to try and find the equilibrium temperature for
365+ // this placement. This is the temperature that we expect would lead to no
366+ // overall change in temperature. We do this by computing the expected
367+ // change in cost given a trial temperature and try larger / smaller
368+ // temperatures until one is found that causes the change cost is close to
369+ // 0. Since the expected change in cost is monotonically increasing for
370+ // all positive temperatures, this method will return a unique result if it
371+ // exists within this range.
372+ // Initialize the lower bound temperature to 0. The temperature cannot
373+ // be less than 0.
374+ double lower_bound_temp = 0.0 ;
375+ // Initialize the upper bound temperature to 1e10. It is possible for
376+ // the equilibrium temperature to be infinite if the initial placement
377+ // is so bad that no swaps are accepted. In that case this value will
378+ // be returned instead of infinity.
379+ // TODO: Find what a reasonable value for this should be.
380+ double upper_bound_temp = 1e10 ;
381+ // The max search iterations should never be hit, but it is here as an
382+ // exit condition to prevent infinite loops.
383+ constexpr unsigned max_search_iters = 100 ;
384+ for (unsigned binary_search_iter = 0 ; binary_search_iter < max_search_iters; binary_search_iter++) {
385+ // Exit condition for binary search. Could be hit if the lower and upper
386+ // bounds are arbitrarily close.
387+ if (lower_bound_temp > upper_bound_temp)
388+ break ;
389+
390+ // Try the temperature in the middle of the lower and upper bounds.
391+ double trial_temp = (lower_bound_temp + upper_bound_temp) / 2.0 ;
392+
393+ // Calculate the expected change in cost at this temperature (which we
394+ // call the residual here).
395+ double expected_total_post_rejected_cost = 0.0 ;
396+ for (double rejected_cost : rejected_swaps) {
397+ // Expected change in cost after a rejected swap is the change in
398+ // cost multiplied by the probability that this swap is accepted at
399+ // this temperature.
400+ double accpetance_prob = std::exp ((-1.0 * rejected_cost) / trial_temp);
401+ expected_total_post_rejected_cost += rejected_cost * accpetance_prob;
402+ }
403+ double residual = expected_total_post_rejected_cost + total_accepted_cost;
404+
405+ // Return the trial temperature if it is within 6 decimal-points of precision.
406+ // NOTE: This is arbitrary.
407+ // TODO: We could stop this early and then use Newton's Method to quickly
408+ // touch it up to a more accurate value.
409+ if (std::abs (upper_bound_temp - lower_bound_temp) / trial_temp < 1e-6 )
410+ return trial_temp;
411+
412+ if (residual < 0 ) {
413+ // Since the function is monotonically increasing, if the residual
414+ // is negative, then the lower bound should be raised to the trial
415+ // temperature.
416+ lower_bound_temp = trial_temp;
417+ } else if (residual > 0 ) {
418+ // Similarly, if the residual is positive, then the upper bound should
419+ // be lowered to the trial temperature.
420+ upper_bound_temp = trial_temp;
421+ } else {
422+ // If we happened to exactly hit the risidual, then this is the
423+ // exact temperature we should use.
424+ return trial_temp;
425+ }
426+ }
427+
428+ // If we get down here, it means that the upper loop did not reach a solution;
429+ // however, we know that the answer should be somewhere between lower and upper
430+ // bound. Therefore, return the average of the two.
431+ return (lower_bound_temp + upper_bound_temp) / 2.0 ;
432+ }
433+
434+ float PlacementAnnealer::estimate_starting_temp_using_cost_variance_ () {
435+ const ClusteringContext& cluster_ctx = g_vpr_ctx.clustering ();
299436
300437 // Use to calculate the average of cost when swap is accepted.
301438 int num_accepted = 0 ;
@@ -318,14 +455,14 @@ float PlacementAnnealer::estimate_starting_temperature_() {
318455#endif /* NO_GRAPHICS*/
319456
320457 // Will not deploy setup slack analysis, so omit crit_exponenet and setup_slack
321- e_move_result swap_result = try_swap_ (*move_generator_1_, placer_opts_.place_algorithm , manual_move_enabled);
458+ t_swap_result swap_result = try_swap_ (*move_generator_1_, placer_opts_.place_algorithm , manual_move_enabled);
322459
323- if (swap_result == e_move_result::ACCEPTED) {
460+ if (swap_result. move_result == e_move_result::ACCEPTED) {
324461 num_accepted++;
325462 av += costs_.cost ;
326463 sum_of_squares += costs_.cost * costs_.cost ;
327464 swap_stats_.num_swap_accepted ++;
328- } else if (swap_result == e_move_result::ABORTED) {
465+ } else if (swap_result. move_result == e_move_result::ABORTED) {
329466 swap_stats_.num_swap_aborted ++;
330467 } else {
331468 swap_stats_.num_swap_rejected ++;
@@ -345,7 +482,7 @@ float PlacementAnnealer::estimate_starting_temperature_() {
345482 return init_temp;
346483}
347484
348- e_move_result PlacementAnnealer::try_swap_ (MoveGenerator& move_generator,
485+ t_swap_result PlacementAnnealer::try_swap_ (MoveGenerator& move_generator,
349486 const t_place_algorithm& place_algorithm,
350487 bool manual_move_enabled) {
351488 /* Picks some block and moves it to another spot. If this spot is
@@ -646,7 +783,11 @@ e_move_result PlacementAnnealer::try_swap_(MoveGenerator& move_generator,
646783 VTR_LOGV_DEBUG (g_vpr_ctx.placement ().f_placer_debug ,
647784 " \t\t After move Place cost %e, bb_cost %e, timing cost %e\n " ,
648785 costs_.cost , costs_.bb_cost , costs_.timing_cost );
649- return move_outcome;
786+
787+ t_swap_result swap_result;
788+ swap_result.move_result = move_outcome;
789+ swap_result.delta_c = delta_c;
790+ return swap_result;
650791}
651792
652793void PlacementAnnealer::outer_loop_update_timing_info () {
@@ -687,13 +828,13 @@ void PlacementAnnealer::placement_inner_loop() {
687828
688829 // Inner loop begins
689830 for (int inner_iter = 0 , inner_crit_iter_count = 1 ; inner_iter < annealing_state_.move_lim ; inner_iter++) {
690- e_move_result swap_result = try_swap_ (move_generator, placer_opts_.place_algorithm , manual_move_enabled);
831+ t_swap_result swap_result = try_swap_ (move_generator, placer_opts_.place_algorithm , manual_move_enabled);
691832
692- if (swap_result == e_move_result::ACCEPTED) {
833+ if (swap_result. move_result == e_move_result::ACCEPTED) {
693834 // Move was accepted. Update statistics that are useful for the annealing schedule.
694835 placer_stats_.single_swap_update (costs_);
695836 swap_stats_.num_swap_accepted ++;
696- } else if (swap_result == e_move_result::ABORTED) {
837+ } else if (swap_result. move_result == e_move_result::ABORTED) {
697838 swap_stats_.num_swap_aborted ++;
698839 } else { // swap_result == REJECTED
699840 swap_stats_.num_swap_rejected ++;
0 commit comments