12
12
#include < memory>
13
13
#include < utility>
14
14
#include < vector>
15
+ #include " device_grid.h"
16
+ #include " flat_placement_types.h"
15
17
#include " partial_placement.h"
16
18
#include " ap_netlist.h"
17
19
#include " vpr_error.h"
36
38
#endif // EIGEN_INSTALLED
37
39
38
40
std::unique_ptr<AnalyticalSolver> make_analytical_solver (e_analytical_solver solver_type,
39
- const APNetlist& netlist) {
41
+ const APNetlist& netlist,
42
+ const DeviceGrid& device_grid) {
40
43
// Based on the solver type passed in, build the solver.
41
44
switch (solver_type) {
42
45
case e_analytical_solver::QP_HYBRID:
43
46
#ifdef EIGEN_INSTALLED
44
- return std::make_unique<QPHybridSolver>(netlist);
47
+ return std::make_unique<QPHybridSolver>(netlist, device_grid );
45
48
#else
46
49
(void )netlist;
50
+ (void )device_grid;
47
51
VPR_FATAL_ERROR (VPR_ERROR_AP,
48
52
" QP Hybrid Solver requires the Eigen library" );
49
53
break ;
@@ -64,8 +68,11 @@ AnalyticalSolver::AnalyticalSolver(const APNetlist& netlist)
64
68
// row ID from [0, num_moveable_blocks) for each moveable block in the
65
69
// netlist.
66
70
num_moveable_blocks_ = 0 ;
71
+ num_fixed_blocks_ = 0 ;
67
72
size_t current_row_id = 0 ;
68
73
for (APBlockId blk_id : netlist.blocks ()) {
74
+ if (netlist.block_mobility (blk_id) == APBlockMobility::FIXED)
75
+ num_fixed_blocks_++;
69
76
if (netlist.block_mobility (blk_id) != APBlockMobility::MOVEABLE)
70
77
continue ;
71
78
APRowId new_row_id = APRowId (current_row_id);
@@ -155,10 +162,10 @@ void QPHybridSolver::init_linear_system() {
155
162
}
156
163
157
164
// Initialize the linear system with zeros.
158
- size_t num_variables = num_moveable_blocks_ + num_star_nodes;
159
- A_sparse = Eigen::SparseMatrix<double >(num_variables, num_variables );
160
- b_x = Eigen::VectorXd::Zero (num_variables );
161
- b_y = Eigen::VectorXd::Zero (num_variables );
165
+ num_variables_ = num_moveable_blocks_ + num_star_nodes;
166
+ A_sparse = Eigen::SparseMatrix<double >(num_variables_, num_variables_ );
167
+ b_x = Eigen::VectorXd::Zero (num_variables_ );
168
+ b_y = Eigen::VectorXd::Zero (num_variables_ );
162
169
163
170
// Create a list of triplets that will be used to create the sparse
164
171
// coefficient matrix. This is the method recommended by Eigen to initialize
@@ -254,7 +261,54 @@ void QPHybridSolver::update_linear_system_with_anchors(
254
261
}
255
262
}
256
263
264
+ void QPHybridSolver::init_guesses (const DeviceGrid& device_grid) {
265
+ // If the number of fixed blocks is zero, initialized the guesses to the
266
+ // center of the device.
267
+ if (num_fixed_blocks_ == 0 ) {
268
+ guess_x = Eigen::VectorXd::Constant (num_variables_, device_grid.width () / 2.0 );
269
+ guess_y = Eigen::VectorXd::Constant (num_variables_, device_grid.height () / 2.0 );
270
+ return ;
271
+ }
272
+
273
+ // Compute the centroid of all fixed blocks in the netlist.
274
+ t_flat_pl_loc centroid ({0 .0f , 0 .0f , 0 .0f });
275
+ unsigned num_blks_summed = 0 ;
276
+ for (APBlockId blk_id : netlist_.blocks ()) {
277
+ // We only get the centroid of fixed blocks since these are the only
278
+ // blocks with positions that we know.
279
+ if (netlist_.block_mobility (blk_id) != APBlockMobility::FIXED)
280
+ continue ;
281
+ // Get the flat location of the fixed block.
282
+ APFixedBlockLoc fixed_blk_loc = netlist_.block_loc (blk_id);
283
+ VTR_ASSERT_SAFE (fixed_blk_loc.x != APFixedBlockLoc::UNFIXED_DIM);
284
+ VTR_ASSERT_SAFE (fixed_blk_loc.y != APFixedBlockLoc::UNFIXED_DIM);
285
+ VTR_ASSERT_SAFE (fixed_blk_loc.layer_num != APFixedBlockLoc::UNFIXED_DIM);
286
+ t_flat_pl_loc flat_blk_loc;
287
+ flat_blk_loc.x = fixed_blk_loc.x ;
288
+ flat_blk_loc.y = fixed_blk_loc.y ;
289
+ flat_blk_loc.layer = fixed_blk_loc.layer_num ;
290
+ // Accumulate into the centroid.
291
+ centroid += flat_blk_loc;
292
+ num_blks_summed++;
293
+ }
294
+ // Divide the sum by the number of fixed blocks.
295
+ VTR_ASSERT_SAFE (num_blks_summed == num_fixed_blocks_);
296
+ centroid /= static_cast <float >(num_blks_summed);
297
+
298
+ // Set the guesses to the centroid location.
299
+ guess_x = Eigen::VectorXd::Constant (num_variables_, centroid.x );
300
+ guess_y = Eigen::VectorXd::Constant (num_variables_, centroid.y );
301
+ }
302
+
257
303
void QPHybridSolver::solve (unsigned iteration, PartialPlacement& p_placement) {
304
+ // In the first iteration, if the number of fixed blocks is 0, set the
305
+ // placement to be equal to the guess. The solver below will just set the
306
+ // solution to the zero vector if we do not set it to the guess directly.
307
+ if (iteration == 0 && num_fixed_blocks_ == 0 ) {
308
+ store_solution_into_placement (guess_x, guess_y, p_placement);
309
+ return ;
310
+ }
311
+
258
312
// Create a temporary linear system which will contain the original linear
259
313
// system which may be updated to include the anchor points.
260
314
Eigen::SparseMatrix<double > A_sparse_diff = Eigen::SparseMatrix<double >(A_sparse);
@@ -280,14 +334,24 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
280
334
cg.compute (A_sparse_diff);
281
335
VTR_ASSERT (cg.info () == Eigen::Success && " Conjugate Gradient failed at compute!" );
282
336
// Use the solver to solve for x and y using the constant vectors
283
- // TODO: Use solve with guess to make this faster. Use the previous placement
284
- // as a guess.
285
- Eigen::VectorXd x = cg.solve (b_x_diff);
337
+ Eigen::VectorXd x = cg.solveWithGuess (b_x_diff, guess_x);
286
338
VTR_ASSERT (cg.info () == Eigen::Success && " Conjugate Gradient failed at solving b_x!" );
287
- Eigen::VectorXd y = cg.solve (b_y_diff);
339
+ Eigen::VectorXd y = cg.solveWithGuess (b_y_diff, guess_y );
288
340
VTR_ASSERT (cg.info () == Eigen::Success && " Conjugate Gradient failed at solving b_y!" );
289
341
290
342
// Write the results back into the partial placement object.
343
+ store_solution_into_placement (x, y, p_placement);
344
+
345
+ // Update the guess. The guess for the next iteration is the solution in
346
+ // this iteration.
347
+ guess_x = x;
348
+ guess_y = y;
349
+ }
350
+
351
+ void QPHybridSolver::store_solution_into_placement (const Eigen::VectorXd& x_soln,
352
+ const Eigen::VectorXd& y_soln,
353
+ PartialPlacement& p_placement) {
354
+
291
355
// NOTE: The first [0, num_moveable_blocks_) rows always represent the
292
356
// moveable APBlocks. The star nodes always come after and are ignored
293
357
// in the solution.
@@ -296,8 +360,23 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
296
360
APBlockId blk_id = row_id_to_blk_id_[row_id];
297
361
VTR_ASSERT_DEBUG (blk_id.is_valid ());
298
362
VTR_ASSERT_DEBUG (netlist_.block_mobility (blk_id) == APBlockMobility::MOVEABLE);
299
- p_placement.block_x_locs [blk_id] = x[row_id_idx];
300
- p_placement.block_y_locs [blk_id] = y[row_id_idx];
363
+ // Due to the iterative nature of CG, it is possible for the solver to
364
+ // overstep 0 and return a negative number by an incredibly small margin.
365
+ // Clamp the number to 0 in this case.
366
+ // TODO: Should investigate good bounds on this, the bounds below were
367
+ // chosen since any difference higher than 1e-9 would concern me.
368
+ double x_pos = x_soln[row_id_idx];
369
+ if (x_pos < 0.0 ) {
370
+ VTR_ASSERT_SAFE (std::abs (x_pos) < negative_soln_tolerance_);
371
+ x_pos = 0.0 ;
372
+ }
373
+ double y_pos = y_soln[row_id_idx];
374
+ if (y_pos < 0.0 ) {
375
+ VTR_ASSERT_SAFE (std::abs (y_pos) < negative_soln_tolerance_);
376
+ y_pos = 0.0 ;
377
+ }
378
+ p_placement.block_x_locs [blk_id] = x_pos;
379
+ p_placement.block_y_locs [blk_id] = y_pos;
301
380
}
302
381
}
303
382
0 commit comments