Skip to content

Commit 1e9878c

Browse files
Yulang LuoAlexandreSinger
Yulang Luo
authored andcommitted
[analytical placer][b2b] addressing PR comments
1 parent 9f27196 commit 1e9878c

File tree

2 files changed

+53
-27
lines changed

2 files changed

+53
-27
lines changed

vpr/src/place/analytical_placement/AnalyticalSolver.cpp

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver sol
3030
return nullptr;
3131
}
3232

33-
bool contains_NaN(Eigen::SparseMatrix<double> &mat) {
33+
static bool contains_NaN(Eigen::SparseMatrix<double> &mat) {
3434
bool contains_nan = false;
3535
for (int k = 0; k < mat.outerSize(); ++k) {
3636
for (Eigen::SparseMatrix<double>::InnerIterator it(mat, k); it; ++it) {
@@ -46,11 +46,11 @@ bool contains_NaN(Eigen::SparseMatrix<double> &mat) {
4646
return contains_nan;
4747
}
4848

49-
bool isSymmetric(const Eigen::SparseMatrix<double> &A){
49+
static bool isSymmetric(const Eigen::SparseMatrix<double> &A){
5050
return A.isApprox(A.transpose());
5151
}
5252

53-
bool isSemiPosDef(const Eigen::SparseMatrix<double> &A){
53+
static bool isSemiPosDef(const Eigen::SparseMatrix<double> &A){
5454
// TODO: This is slow, it could be faster if we use Cholesky decomposition (Eigen::SimplicialLDLT), still O(n^3)
5555
Eigen::SelfAdjointEigenSolver<Eigen::SparseMatrix<double>> solver;
5656
solver.compute(A);
@@ -65,7 +65,7 @@ bool isSemiPosDef(const Eigen::SparseMatrix<double> &A){
6565
return true;
6666
}
6767

68-
double conditionNumber(const Eigen::SparseMatrix<double> &A) {
68+
static double conditionNumber(const Eigen::SparseMatrix<double> &A) {
6969
// Since real and sym, instead of sigmamax/sigmamin, lambdamax^2/lambdamin^2 is also ok, could be faster.
7070
// Eigen::MatrixXd denseMat = Eigen::MatrixXd(A);
7171
// Eigen::JacobiSVD<Eigen::MatrixXd> svd(denseMat);
@@ -217,6 +217,8 @@ void B2BSolver::initialize_placement_random_normal(PartialPlacement &p_placement
217217
// std::random_device rd;
218218
// std::mt19937 gen(rd());
219219
std::mt19937 gen(1894650209824387);
220+
// FIXME: the standard deviation is too small, [width/height] / [2/3]
221+
// can spread nodes further aparts.
220222
std::normal_distribution<> disx((double)grid_width / 2, 1.0);
221223
std::normal_distribution<> disy((double)grid_height / 2, 1.0);
222224
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
247249
void B2BSolver::initialize_placement_least_dense(PartialPlacement &p_placement) {
248250
size_t grid_width = g_vpr_ctx.device().grid.width();
249251
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;
258265
}
259266
}
267+
260268
}
261269

262270
// This function return the two nodes on the bound of a netlist, (max, min)
263271
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) {
265273
return node_locs[a] < node_locs[b];
266274
};
267275
auto maxIt = std::max_element(node_ids.begin(), node_ids.end(), compare);
@@ -274,8 +282,10 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
274282
const AtomContext& atom_ctx = g_vpr_ctx.atom();
275283
const AtomNetlist& netlist = p_placement.atom_netlist;
276284
// 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();
279289
std::vector<Eigen::Triplet<double>> tripletList_x;
280290
tripletList_x.reserve(p_placement.num_moveable_nodes * netlist.nets().size());
281291
std::vector<Eigen::Triplet<double>> tripletList_y;
@@ -297,15 +307,19 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
297307
node_ids.push_back(node_id);
298308
}
299309
// 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.
300312
std::set<size_t> node_ids_set(node_ids.begin(), node_ids.end());
301313
std::vector<size_t> node_ids_no_duplicate(node_ids_set.begin(), node_ids_set.end());
302314

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);
305315
if (node_ids_no_duplicate.size() <= 1){
306316
continue;
307317
}
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);
308321
// assign arbitrary node as bound node when they are all equal
322+
// TODO: although deterministic, investigate other ways to break ties.
309323
if (maxXId == minXId) {
310324
maxXId = node_ids_no_duplicate[0];
311325
minXId = node_ids_no_duplicate[1];
@@ -321,7 +335,9 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
321335
}
322336
std::swap(first_node_id, second_node_id);
323337
}
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.
325341
double dist = 0;
326342
if(is_x)
327343
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) {
334350
tripletList_x.emplace_back(second_node_id, second_node_id, w);
335351
tripletList_x.emplace_back(first_node_id, second_node_id, -w);
336352
tripletList_x.emplace_back(second_node_id, first_node_id, -w);
337-
}else{
353+
} else {
338354
tripletList_y.emplace_back(first_node_id, first_node_id, w);
339355
tripletList_y.emplace_back(second_node_id, second_node_id, w);
340356
tripletList_y.emplace_back(first_node_id, second_node_id, -w);
@@ -344,13 +360,15 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
344360
if (is_x){
345361
tripletList_x.emplace_back(first_node_id, first_node_id, w);
346362
b_x(first_node_id) += w * p_placement.node_loc_x[second_node_id];
347-
}else{
363+
} else {
348364
tripletList_y.emplace_back(first_node_id, first_node_id, w);
349365
b_y(first_node_id) += w * p_placement.node_loc_y[second_node_id];
350366
}
351367
}
352368
};
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++) {
354372
if (node_id != maxXId && node_id != minXId) {
355373
add_node(node_id, maxXId, node_ids_no_duplicate.size(), true);
356374
add_node(node_id, minXId, node_ids_no_duplicate.size(), true);
@@ -367,9 +385,9 @@ void B2BSolver::populate_matrix(PartialPlacement &p_placement) {
367385
A_sparse_y.setFromTriplets(tripletList_y.begin(), tripletList_y.end());
368386
}
369387

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.
371390
void B2BSolver::populate_matrix_anchor(PartialPlacement& p_placement, unsigned iteration) {
372-
double epsilon = 1e-6;
373391
double coeff_pseudo_anchor = 0.001 * std::exp((double)iteration/29.0);
374392
// double coeff_pseudo_anchor = std::exp((double)iteration/1.0);
375393
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
395413
// This function is the inner loop that alternate between building matrix based on placement, and solving model to get placement.
396414
void B2BSolver::b2b_solve_loop(unsigned iteration, PartialPlacement &p_placement){
397415
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++) {
400417
counter++;
401418
previous_hpwl = current_hpwl;
402419
VTR_LOG("placement hpwl in b2b loop: %f\n", p_placement.get_HPWL());
403420
VTR_ASSERT(p_placement.is_valid_partial_placement() && "did not produce a valid placement in b2b solve loop");
404421
populate_matrix(p_placement);
405-
if(iteration != 0)
422+
if (iteration != 0)
406423
populate_matrix_anchor(p_placement, iteration);
407424
Eigen::VectorXd x, y;
408425
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
423440
VTR_ASSERT(cg_y.info() == Eigen::Success && "Conjugate Gradient failed at compute for A_y!");
424441
x = cg_x.solve(b_x);
425442
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.
426444
// VTR_ASSERT(cg_x.info() == Eigen::Success && "Conjugate Gradient failed at solving b_x!");
427445
// VTR_ASSERT(cg_y.info() == Eigen::Success && "Conjugate Gradient failed at solving b_y!");
428446
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
434452
// }while(std::abs(current_hpwl - previous_hpwl) < 5 && counter < 100);
435453
// current_hpwl > previous_hpwl - 10 would not work when this tries to grow and converge to legalized solution
436454
// simplest one for now
437-
}while(counter < 30);
455+
}
438456
}
439457

440458
void B2BSolver::solve(unsigned iteration, PartialPlacement &p_placement) {

vpr/src/place/analytical_placement/AnalyticalSolver.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,19 @@ class B2BSolver : public AnalyticalSolver {
4646
void populate_matrix(PartialPlacement &p_placement);
4747
void populate_matrix_anchor(PartialPlacement& p_placement, unsigned iteration);
4848
std::pair<size_t, size_t> boundNode(std::vector<size_t> &node_id, std::vector<double> &node_loc);
49+
50+
static inline const double epsilon = 1e-6;
51+
static inline const unsigned inner_iterations = 30;
4952

53+
// These are stored because there might be potential oppurtunities of reuse.
54+
// Also, it could be more efficient to allocate heap once and reusing it instead
55+
// of freeing and reallocating.
5056
Eigen::SparseMatrix<double> A_sparse_x;
5157
Eigen::SparseMatrix<double> A_sparse_y;
5258
Eigen::VectorXd b_x;
5359
Eigen::VectorXd b_y;
60+
// They are being stored because legalizer will modified the placement passed in. While building the b2b model with anchors,
61+
// both the previously solved and legalized placement are needed, so we store them as a member.
5462
std::vector<double> node_loc_x_solved;
5563
std::vector<double> node_loc_y_solved;
5664
std::vector<double> node_loc_x_legalized;

0 commit comments

Comments
 (0)