Skip to content

Commit eabb6e3

Browse files
[AP] Updated Analytical Solver Documentation
1 parent fbfe638 commit eabb6e3

File tree

2 files changed

+105
-45
lines changed

2 files changed

+105
-45
lines changed

vpr/src/analytical_place/analytical_solver.cpp

Lines changed: 102 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,74 @@ AnalyticalSolver::AnalyticalSolver(const APNetlist& netlist)
6969

7070
#ifdef EIGEN_INSTALLED
7171

72+
/**
73+
* @brief Helper method to add a connection between a src moveable node and a
74+
* target APBlock with the given weight. This updates the tripleList and
75+
* the constant vectors with the values necessary to solve the quadratic
76+
* objective function.
77+
*
78+
* The A_sparse matrix is square and symmetric, so the use of the "row_id" as
79+
* input is arbitrary; it could easily have been "src_col_id".
80+
*
81+
* The src_row_id always represents a moveable node in the linear system. It can
82+
* represent a moveable APBlock or a star node.
83+
*
84+
* The target_blk_id may be either a moveable or fixed block.
85+
*
86+
* If the target block (t) is moveable, with source row s:
87+
* A[s][s] = A[s][s] + weight
88+
* A[t][t] = A[t][t] + weight
89+
* A[s][t] = A[s][t] - weight
90+
* A[t][s] = A[t][s] - weight
91+
* If the target block is fixed:
92+
* A[s][s] = A[s][s] + weight
93+
* b[s] = b[s] + pos[block(t)] * weight
94+
*
95+
* These update equations come from taking the partial derivatives of the
96+
* quadratic objective function w.r.t the moveable block locations. This is
97+
* explained in detail in the FastPlace paper.
98+
*/
99+
static inline void add_connection_to_system(size_t src_row_id,
100+
APBlockId target_blk_id,
101+
double weight,
102+
std::vector<Eigen::Triplet<double>>& tripletList,
103+
Eigen::VectorXd& b_x,
104+
Eigen::VectorXd& b_y,
105+
Eigen::SparseMatrix<double>& A_sparse,
106+
vtr::vector<APBlockId, APRowId>& blk_id_to_row_id,
107+
const APNetlist& netlist) {
108+
// Verify that this is a valid row.
109+
VTR_ASSERT_DEBUG(src_row_id < (size_t)A_sparse.rows());
110+
VTR_ASSERT_DEBUG(A_sparse.rows() == A_sparse.cols());
111+
// Verify that this is a valid block id.
112+
VTR_ASSERT_DEBUG(target_blk_id.is_valid());
113+
// The src_row_id is always a moveable block (rows in the matrix always
114+
// coorespond to a moveable APBlock or a star node.
115+
if (netlist.block_mobility(target_blk_id) == APBlockMobility::MOVEABLE) {
116+
// If the target is also moveable, update the coefficient matrix.
117+
size_t target_row_id = (size_t)blk_id_to_row_id[target_blk_id];
118+
VTR_ASSERT_DEBUG(target_row_id < (size_t)A_sparse.rows());
119+
tripletList.emplace_back(src_row_id, src_row_id, weight);
120+
tripletList.emplace_back(target_row_id, target_row_id, weight);
121+
tripletList.emplace_back(src_row_id, target_row_id, -weight);
122+
tripletList.emplace_back(target_row_id, src_row_id, -weight);
123+
} else {
124+
// If the target is fixed, update the coefficient matrix and the
125+
// constant vectors.
126+
tripletList.emplace_back(src_row_id, src_row_id, weight);
127+
VTR_ASSERT_DEBUG(netlist.block_loc(target_blk_id).x >= 0);
128+
VTR_ASSERT_DEBUG(netlist.block_loc(target_blk_id).y >= 0);
129+
// FIXME: These fixed block locations are aligned to the anchor of
130+
// the tiles they are in. This is not correct. A method
131+
// should be added to the netlist class or to a util file
132+
// which can get a more accurate position.
133+
double blk_loc_x = netlist.block_loc(target_blk_id).x;
134+
double blk_loc_y = netlist.block_loc(target_blk_id).y;
135+
b_x(src_row_id) += weight * blk_loc_x;
136+
b_y(src_row_id) += weight * blk_loc_y;
137+
}
138+
}
139+
72140
void QPHybridSolver::init_linear_system() {
73141
// Count the number of star nodes that the netlist will have.
74142
size_t num_star_nodes = 0;
@@ -86,50 +154,20 @@ void QPHybridSolver::init_linear_system() {
86154
// Create a list of triplets that will be used to create the sparse
87155
// coefficient matrix. This is the method recommended by Eigen to initialize
88156
// this matrix.
157+
// A triplet represents a non-zero entry in a sparse matrix:
158+
// (row index, col index, value)
159+
// Where triplets at the same (row index, col index) are summed together.
89160
std::vector<Eigen::Triplet<double>> tripletList;
90161
// Reserve enough space for the triplets. This is just to help with
91162
// performance.
163+
// This is an over-estimate that assumes that each net connnects to all
164+
// moveable blocks using a star node.
165+
// TODO: This can be made more space-efficient by getting the average fanout
166+
// of all nets in the APNetlist. Ideally this should be not enough
167+
// space, but be within a constant factor.
92168
size_t num_nets = netlist_.nets().size();
93169
tripletList.reserve(num_moveable_blocks_ * num_nets);
94170

95-
// Lambda expression to add a connection to the linear system from the src
96-
// to the target with the given weight. The src_row_id may represent a star
97-
// node (so it does not represent an APBlock) or a moveable APBlock. The
98-
// target_blk_id may be a fixed or moveable block.
99-
auto add_connection_to_system = [&](size_t src_row_id,
100-
APBlockId target_blk_id,
101-
double weight) {
102-
// Verify that this is a valid row.
103-
VTR_ASSERT_DEBUG(src_row_id < A_sparse.rows());
104-
// Verify that this is a valid block id.
105-
VTR_ASSERT_DEBUG(target_blk_id.is_valid());
106-
// The src_row_id is always a moveable block (rows in the matrix always
107-
// coorespond to a moveable APBlock or a star node.
108-
if (netlist_.block_mobility(target_blk_id) == APBlockMobility::MOVEABLE) {
109-
// If the target is also moveable, update the coefficient matrix.
110-
size_t target_row_id = (size_t)blk_id_to_row_id_[target_blk_id];
111-
VTR_ASSERT_DEBUG(target_row_id < A_sparse.rows());
112-
tripletList.emplace_back(src_row_id, src_row_id, weight);
113-
tripletList.emplace_back(target_row_id, target_row_id, weight);
114-
tripletList.emplace_back(src_row_id, target_row_id, -weight);
115-
tripletList.emplace_back(target_row_id, src_row_id, -weight);
116-
} else {
117-
// If the target is fixed, update the coefficient matrix and the
118-
// constant vectors.
119-
tripletList.emplace_back(src_row_id, src_row_id, weight);
120-
VTR_ASSERT_DEBUG(netlist_.block_loc(target_blk_id).x >= 0);
121-
VTR_ASSERT_DEBUG(netlist_.block_loc(target_blk_id).y >= 0);
122-
// FIXME: These fixed block locations are aligned to the anchor of
123-
// the tiles they are in. This is not correct. A method
124-
// should be added to the netlist class or to a util file
125-
// which can get a more accurate position.
126-
double blk_loc_x = netlist_.block_loc(target_blk_id).x;
127-
double blk_loc_y = netlist_.block_loc(target_blk_id).y;
128-
b_x(src_row_id) += weight * blk_loc_x;
129-
b_y(src_row_id) += weight * blk_loc_y;
130-
}
131-
};
132-
133171
// Create the connections using a hybrid connection model of the star and
134172
// clique connnection models.
135173
size_t star_node_offset = 0;
@@ -140,17 +178,21 @@ void QPHybridSolver::init_linear_system() {
140178
// Create a star node and connect each block in the net to the star
141179
// node.
142180
// Using the weight from FastPlace
181+
// TODO: Investigate other weight terms.
143182
double w = static_cast<double>(num_pins) / static_cast<double>(num_pins - 1);
144183
size_t star_node_id = num_moveable_blocks_ + star_node_offset;
145184
for (APPinId pin_id : netlist_.net_pins(net_id)) {
146185
APBlockId blk_id = netlist_.pin_block(pin_id);
147-
add_connection_to_system(star_node_id, blk_id, w);
186+
add_connection_to_system(star_node_id, blk_id, w, tripletList,
187+
b_x, b_y, A_sparse, blk_id_to_row_id_,
188+
netlist_);
148189
}
149190
star_node_offset++;
150191
} else {
151192
// Create a clique connection where every block in a net connects
152193
// exactly once to every other block in the net.
153194
// Using the weight from FastPlace
195+
// TODO: Investigate other weight terms.
154196
double w = 1.0 / static_cast<double>(num_pins - 1);
155197
for (size_t ipin_idx = 0; ipin_idx < num_pins; ipin_idx++) {
156198
APPinId first_pin_id = netlist_.net_pin(net_id, ipin_idx);
@@ -171,7 +213,9 @@ void QPHybridSolver::init_linear_system() {
171213
std::swap(first_blk_id, second_blk_id);
172214
}
173215
size_t first_row_id = (size_t)blk_id_to_row_id_[first_blk_id];
174-
add_connection_to_system(first_row_id, second_blk_id, w);
216+
add_connection_to_system(first_row_id, second_blk_id, w, tripletList,
217+
b_x, b_y, A_sparse, blk_id_to_row_id_,
218+
netlist_);
175219
}
176220
}
177221
}
@@ -194,8 +238,19 @@ void QPHybridSolver::init_linear_system() {
194238
* b[i] = b[i] + pos[block(i)] * coeff_pseudo_anchor;
195239
* Where coeff_pseudo_anchor grows with each iteration.
196240
*
197-
* This is basically a fast way of adding a connection between a moveable block
198-
* and a fixed block.
241+
* This is basically a fast way of adding a connection between all moveable
242+
* blocks in the netlist and their target fixed placement location.
243+
*
244+
* See add_connection_to_system.
245+
*
246+
* @param A_sparse_diff The ceofficient matrix to update.
247+
* @param b_x_diff The x-dimension constant vector to update.
248+
* @param b_y_diff The y-dimension constant vector to update.
249+
* @param p_placement The location the moveable blocks should be anchored
250+
* to.
251+
* @param num_moveable_blocks The number of moveable blocks in the netlist.
252+
* @param row_id_to_blk_id Lookup for the row id from the APBlock Id.
253+
* @param iteration The current iteration of the Global Placer.
199254
*/
200255
static inline void update_linear_system_with_anchors(
201256
Eigen::SparseMatrix<double> &A_sparse_diff,
@@ -252,9 +307,14 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement &p_placement) {
252307
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at solving b_y!");
253308

254309
// Write the results back into the partial placement object.
310+
// NOTE: The first [0, num_moveable_blocks_) rows always represent the
311+
// moveable APBlocks. The star nodes always come after and are ignored
312+
// in the solution.
255313
for (size_t row_id_idx = 0; row_id_idx < num_moveable_blocks_; row_id_idx++) {
256314
APRowId row_id = APRowId(row_id_idx);
257315
APBlockId blk_id = row_id_to_blk_id_[row_id];
316+
VTR_ASSERT_DEBUG(blk_id.is_valid());
317+
VTR_ASSERT_DEBUG(netlist_.block_mobility(blk_id) == APBlockMobility::MOVEABLE);
258318
p_placement.block_x_locs[blk_id] = x[row_id_idx];
259319
p_placement.block_y_locs[blk_id] = y[row_id_idx];
260320
}

vpr/src/analytical_place/analytical_solver.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class AnalyticalSolver {
5555
/**
5656
* @brief Constructor of the base AnalyticalSolver class
5757
*
58-
* Initializes the internal data members of the base class which are usefull
58+
* Initializes the internal data members of the base class which are useful
5959
* for all solvers.
6060
*/
6161
AnalyticalSolver(const APNetlist &netlist);
@@ -148,7 +148,7 @@ class QPHybridSolver : public AnalyticalSolver {
148148
static constexpr size_t star_num_pins_threshold = 3;
149149

150150
/**
151-
* @brief Initializes the linear system of Ax = b_x and Ax = b_y based on
151+
* @brief Initializes the linear system of Ax = b_x and Ay = b_y based on
152152
* the APNetlist and the fixed APBlock locations.
153153
*
154154
* This is the "ideal" quadratic linear system where no anchor-points are
@@ -180,7 +180,7 @@ class QPHybridSolver : public AnalyticalSolver {
180180
*
181181
* Initializes internal data and constructs the initial linear system.
182182
*/
183-
QPHybridSolver(const APNetlist& inetlist) : AnalyticalSolver(inetlist) {
183+
QPHybridSolver(const APNetlist& netlist) : AnalyticalSolver(netlist) {
184184
// Initializing the linear system only depends on the netlist and fixed
185185
// block locations. Both are provided by the netlist, allowing this to
186186
// be initialized in the constructor.

0 commit comments

Comments
 (0)