@@ -30,7 +30,7 @@ std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver sol
30
30
return nullptr ;
31
31
}
32
32
33
- bool contains_NaN (Eigen::SparseMatrix<double > &mat) {
33
+ static bool contains_NaN (Eigen::SparseMatrix<double > &mat) {
34
34
bool contains_nan = false ;
35
35
for (int k = 0 ; k < mat.outerSize (); ++k) {
36
36
for (Eigen::SparseMatrix<double >::InnerIterator it (mat, k); it; ++it) {
@@ -46,11 +46,11 @@ bool contains_NaN(Eigen::SparseMatrix<double> &mat) {
46
46
return contains_nan;
47
47
}
48
48
49
- bool isSymmetric (const Eigen::SparseMatrix<double > &A){
49
+ static bool isSymmetric (const Eigen::SparseMatrix<double > &A){
50
50
return A.isApprox (A.transpose ());
51
51
}
52
52
53
- bool isSemiPosDef (const Eigen::SparseMatrix<double > &A){
53
+ static bool isSemiPosDef (const Eigen::SparseMatrix<double > &A){
54
54
// TODO: This is slow, it could be faster if we use Cholesky decomposition (Eigen::SimplicialLDLT), still O(n^3)
55
55
Eigen::SelfAdjointEigenSolver<Eigen::SparseMatrix<double >> solver;
56
56
solver.compute (A);
@@ -65,7 +65,7 @@ bool isSemiPosDef(const Eigen::SparseMatrix<double> &A){
65
65
return true ;
66
66
}
67
67
68
- double conditionNumber (const Eigen::SparseMatrix<double > &A) {
68
+ static double conditionNumber (const Eigen::SparseMatrix<double > &A) {
69
69
// Since real and sym, instead of sigmamax/sigmamin, lambdamax^2/lambdamin^2 is also ok, could be faster.
70
70
// Eigen::MatrixXd denseMat = Eigen::MatrixXd(A);
71
71
// Eigen::JacobiSVD<Eigen::MatrixXd> svd(denseMat);
@@ -217,6 +217,8 @@ void B2BSolver::initialize_placement_random_normal(PartialPlacement &p_placement
217
217
// std::random_device rd;
218
218
// std::mt19937 gen(rd());
219
219
std::mt19937 gen (1894650209824387 );
220
+ // FIXME: the standard deviation is too small, [width/height] / [2/3]
221
+ // can spread nodes further aparts.
220
222
std::normal_distribution<> disx ((double )grid_width / 2 , 1.0 );
221
223
std::normal_distribution<> disy ((double )grid_height / 2 , 1.0 );
222
224
for (size_t i = 0 ; i < p_placement.num_moveable_nodes ; i++) {
@@ -247,21 +249,27 @@ void B2BSolver::initialize_placement_random_uniform(PartialPlacement &p_placemen
247
249
void B2BSolver::initialize_placement_least_dense (PartialPlacement &p_placement) {
248
250
size_t grid_width = g_vpr_ctx.device ().grid .width ();
249
251
size_t grid_height = g_vpr_ctx.device ().grid .height ();
250
- unsigned cols = std::ceil (std::sqrt (p_placement.num_moveable_nodes * grid_height / static_cast <double >(grid_width)));
251
- unsigned rows = std::ceil (p_placement.num_moveable_nodes / static_cast <double >(cols));
252
- double x_gap = grid_width / static_cast <double >(cols + 1 );
253
- double y_gap = grid_height / static_cast <double >(rows + 1 );
254
- for (int r = 1 ; r <= static_cast <int >(rows); r++) {
255
- for (int c = 1 ; c <= static_cast <int >(cols); c++) {
256
- p_placement.node_loc_x [(r-1 )*cols + c -1 ] = c * x_gap;
257
- p_placement.node_loc_x [(r-1 )*cols + c -1 ] = r * y_gap;
252
+ double gap = std::sqrt (grid_height * grid_width / static_cast <double >(p_placement.num_moveable_nodes ));
253
+ size_t cols = std::ceil (grid_width / gap);
254
+ size_t rows = std::ceil (grid_height / gap);
255
+ // VTR_LOG("width: %zu, height: %zu, gap: %f\n", grid_width, grid_height, gap);
256
+ // VTR_LOG("cols: %zu, row: %zu, cols * rows: %zu, numnode: %zu\n", cols, rows, cols*rows, p_placement.num_moveable_nodes);
257
+ // VTR_ASSERT(cols * rows == p_placement.num_moveable_nodes && "number of movable nodes does not match grid size");
258
+ for (size_t r = 0 ; r <= rows; r++) {
259
+ for (size_t c = 0 ; c <= cols; c++) {
260
+ size_t i = r * cols + c;
261
+ if (i >= p_placement.num_moveable_nodes )
262
+ break ;
263
+ p_placement.node_loc_x [i] = c * gap;
264
+ p_placement.node_loc_x [i] = r * gap;
258
265
}
259
266
}
267
+
260
268
}
261
269
262
270
// This function return the two nodes on the bound of a netlist, (max, min)
263
271
std::pair<size_t , size_t > B2BSolver::boundNode (std::vector<size_t >& node_ids, std::vector<double >& node_locs){
264
- auto compare = [&node_locs](int a, int b) {
272
+ auto compare = [&node_locs](size_t a, size_t b) {
265
273
return node_locs[a] < node_locs[b];
266
274
};
267
275
auto maxIt = std::max_element (node_ids.begin (), node_ids.end (), compare);
@@ -274,8 +282,10 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
274
282
const AtomContext& atom_ctx = g_vpr_ctx.atom ();
275
283
const AtomNetlist& netlist = p_placement.atom_netlist ;
276
284
// Resetting As bs
277
- A_sparse_x.setZero ();
278
- A_sparse_y.setZero ();
285
+ A_sparse_x = Eigen::SparseMatrix<double >(A_sparse_x.rows (), A_sparse_x.cols ());
286
+ A_sparse_y = Eigen::SparseMatrix<double >(A_sparse_y.rows (), A_sparse_y.cols ());
287
+ // A_sparse_x.setZero();
288
+ // A_sparse_y.setZero();
279
289
std::vector<Eigen::Triplet<double >> tripletList_x;
280
290
tripletList_x.reserve (p_placement.num_moveable_nodes * netlist.nets ().size ());
281
291
std::vector<Eigen::Triplet<double >> tripletList_y;
@@ -297,15 +307,19 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
297
307
node_ids.push_back (node_id);
298
308
}
299
309
// remove duplicated node, they are there becaues of prepacked molecules.
310
+ // FIXME: duplicate exists because atoms are packed in to molecules so some edges are now hidden.
311
+ // We can create our own netlist class to resolve this problem.
300
312
std::set<size_t > node_ids_set (node_ids.begin (), node_ids.end ());
301
313
std::vector<size_t > node_ids_no_duplicate (node_ids_set.begin (), node_ids_set.end ());
302
314
303
- auto [maxXId, minXId] = boundNode (node_ids_no_duplicate, p_placement.node_loc_x );
304
- auto [maxYId, minYId] = boundNode (node_ids_no_duplicate, p_placement.node_loc_y );
305
315
if (node_ids_no_duplicate.size () <= 1 ){
306
316
continue ;
307
317
}
318
+ // TODO: do this in a for loop instead of creating vectors
319
+ auto [maxXId, minXId] = boundNode (node_ids_no_duplicate, p_placement.node_loc_x );
320
+ auto [maxYId, minYId] = boundNode (node_ids_no_duplicate, p_placement.node_loc_y );
308
321
// assign arbitrary node as bound node when they are all equal
322
+ // TODO: although deterministic, investigate other ways to break ties.
309
323
if (maxXId == minXId) {
310
324
maxXId = node_ids_no_duplicate[0 ];
311
325
minXId = node_ids_no_duplicate[1 ];
@@ -321,7 +335,9 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
321
335
}
322
336
std::swap (first_node_id, second_node_id);
323
337
}
324
- double epsilon = 1e-6 ;
338
+ // epsilon is needed to prevent numerical instability. If two nodes are on top of each other.
339
+ // The denominator of weight is zero, which causes infinity term in the matrix. Another way of
340
+ // interpreting epsilon is the minimum distance two nodes are considered to be in placement.
325
341
double dist = 0 ;
326
342
if (is_x)
327
343
dist = 1.0 /std::max (std::abs (p_placement.node_loc_x [first_node_id] - p_placement.node_loc_x [second_node_id]), epsilon);
@@ -334,7 +350,7 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
334
350
tripletList_x.emplace_back (second_node_id, second_node_id, w);
335
351
tripletList_x.emplace_back (first_node_id, second_node_id, -w);
336
352
tripletList_x.emplace_back (second_node_id, first_node_id, -w);
337
- }else {
353
+ } else {
338
354
tripletList_y.emplace_back (first_node_id, first_node_id, w);
339
355
tripletList_y.emplace_back (second_node_id, second_node_id, w);
340
356
tripletList_y.emplace_back (first_node_id, second_node_id, -w);
@@ -344,13 +360,15 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
344
360
if (is_x){
345
361
tripletList_x.emplace_back (first_node_id, first_node_id, w);
346
362
b_x (first_node_id) += w * p_placement.node_loc_x [second_node_id];
347
- }else {
363
+ } else {
348
364
tripletList_y.emplace_back (first_node_id, first_node_id, w);
349
365
b_y (first_node_id) += w * p_placement.node_loc_y [second_node_id];
350
366
}
351
367
}
352
368
};
353
- for (size_t node_id = 0 ; node_id < node_ids_no_duplicate.size (); node_id++) {
369
+ // TODO: when adding custom netlist, also modify here.
370
+ size_t num_nodes = node_ids_no_duplicate.size ();
371
+ for (size_t node_id = 0 ; node_id < num_nodes; node_id++) {
354
372
if (node_id != maxXId && node_id != minXId) {
355
373
add_node (node_id, maxXId, node_ids_no_duplicate.size (), true );
356
374
add_node (node_id, minXId, node_ids_no_duplicate.size (), true );
@@ -367,9 +385,9 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
367
385
A_sparse_y.setFromTriplets (tripletList_y.begin (), tripletList_y.end ());
368
386
}
369
387
370
-
388
+ // This function adds anchors for legalized solution. Anchors are treated as fixed node,
389
+ // each connecting to a movable node. Number of nodes in a anchor net is always 2.
371
390
void B2BSolver::populate_matrix_anchor (PartialPlacement& p_placement, unsigned iteration) {
372
- double epsilon = 1e-6 ;
373
391
double coeff_pseudo_anchor = 0.001 * std::exp ((double )iteration/29.0 );
374
392
// double coeff_pseudo_anchor = std::exp((double)iteration/1.0);
375
393
for (size_t i = 0 ; i < p_placement.num_moveable_nodes ; i++){
@@ -395,14 +413,13 @@ void B2BSolver::populate_matrix_anchor(PartialPlacement& p_placement, unsigned i
395
413
// This function is the inner loop that alternate between building matrix based on placement, and solving model to get placement.
396
414
void B2BSolver::b2b_solve_loop (unsigned iteration, PartialPlacement &p_placement){
397
415
double previous_hpwl, current_hpwl = std::numeric_limits<double >::max ();
398
- unsigned counter = 0 ;
399
- do {
416
+ for (unsigned counter = 0 ; counter < inner_iterations; counter++) {
400
417
counter++;
401
418
previous_hpwl = current_hpwl;
402
419
VTR_LOG (" placement hpwl in b2b loop: %f\n " , p_placement.get_HPWL ());
403
420
VTR_ASSERT (p_placement.is_valid_partial_placement () && " did not produce a valid placement in b2b solve loop" );
404
421
populate_matrix (p_placement);
405
- if (iteration != 0 )
422
+ if (iteration != 0 )
406
423
populate_matrix_anchor (p_placement, iteration);
407
424
Eigen::VectorXd x, y;
408
425
Eigen::ConjugateGradient<Eigen::SparseMatrix<double >, Eigen::Lower|Eigen::Upper> cg_x;
@@ -423,6 +440,7 @@ void B2BSolver::b2b_solve_loop(unsigned iteration, PartialPlacement &p_placement
423
440
VTR_ASSERT (cg_y.info () == Eigen::Success && " Conjugate Gradient failed at compute for A_y!" );
424
441
x = cg_x.solve (b_x);
425
442
y = cg_y.solve (b_y);
443
+ // These assertion are not valid because we do not need cg to converge. And often they do not converge with in default max iterations.
426
444
// VTR_ASSERT(cg_x.info() == Eigen::Success && "Conjugate Gradient failed at solving b_x!");
427
445
// VTR_ASSERT(cg_y.info() == Eigen::Success && "Conjugate Gradient failed at solving b_y!");
428
446
for (size_t node_id = 0 ; node_id < p_placement.num_moveable_nodes ; node_id++) {
@@ -434,7 +452,7 @@ void B2BSolver::b2b_solve_loop(unsigned iteration, PartialPlacement &p_placement
434
452
// }while(std::abs(current_hpwl - previous_hpwl) < 5 && counter < 100);
435
453
// current_hpwl > previous_hpwl - 10 would not work when this tries to grow and converge to legalized solution
436
454
// simplest one for now
437
- }while (counter < 30 );
455
+ }
438
456
}
439
457
440
458
void B2BSolver::solve (unsigned iteration, PartialPlacement &p_placement) {
0 commit comments