Skip to content

Conversation

@openroad-ci
Copy link
Collaborator

This PR improves the notch detection for the notch penalty calculation. It achieves it by considering closely aligned macros as aligned (in order to generate a more meaningful grid) and merging cells in order to find the notches actual size and amount.

It also enhances the debug visualization, showing the notches when using the mpl::mpl_debug command.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an improved notch detection algorithm for penalty calculation, along with debug visualization for the detected notches. The core logic changes are in SACoreSoftMacro::calNotchPenalty. My review focuses on enhancing the performance and maintainability of this new algorithm. I've identified a significant performance bottleneck due to redundant computations and suggest a fix. Additionally, I've provided recommendations for refactoring to reduce code duplication, replacing magic numbers with constants, and removing a non-standard header for better portability.


#include "SACoreSoftMacro.h"

#include <bits/ranges_algo.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The header <bits/ranges_algo.h> is a non-standard, GCC-specific implementation detail. For better portability, it's best to rely on standard C++ headers. Since <algorithm> and <ranges> are already included, this header is likely unnecessary and should be removed.

Comment on lines 703 to 718
std::vector<int> x_coords;
int epsilon = outline_.dx() / 100;
for (size_t i = 0; i < x_point.size(); i++) {
if (i + 1 < x_point.size()
&& std::abs(x_point[i + 1] - x_point[i]) <= epsilon) {
continue;
}
x_point.insert(macro.getX());
x_point.insert(macro.getX() + macro.getWidth());
y_point.insert(macro.getY());
y_point.insert(macro.getY() + macro.getHeight());
x_coords.push_back(x_point[i]);
}

std::vector<int> y_coords;
epsilon = outline_.dy() / 100;
for (size_t i = 0; i < y_point.size(); i++) {
if (i + 1 < y_point.size()
&& std::abs(y_point[i + 1] - y_point[i]) <= epsilon) {
continue;
}
y_coords.push_back(y_point[i]);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The value 100 is used as a magic number in two places to calculate epsilon. It would be better to define it as a single named constant (e.g., constexpr int kCoordEpsilonDivisor = 100;) at a suitable scope to improve readability, maintainability, and ensure consistency.

References
  1. Define tunable parameters as named constants instead of using hardcoded magic numbers.

Comment on lines 844 to 864
if (n.total() == 4) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
} else if (n.top && n.bottom) {
if (height < notch_h_th_) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
}
} else if (n.left && n.right) {
if (width < notch_v_th_) {
notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
block_->dbuToMicrons(height));
if (graphics_) {
graphics_->addNotch(odb::Rect(x_coords[start_col],
y_coords[start_row],
x_coords[end_col + 1],
y_coords[end_row + 1]));
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The code block for calculating the notch penalty and adding the notch for visualization is duplicated in three places within the if-else if structure. This can be refactored to improve maintainability and reduce redundancy.

You could determine if a notch should be penalized using a boolean flag, and then have a single block of code that performs the penalty calculation and graphics update.

      bool is_notch = false;
      if (n.total() == 4) {
        is_notch = true;
      } else if (n.top && n.bottom) {
        if (height < notch_h_th_) {
          is_notch = true;
        }
      } else if (n.left && n.right) {
        if (width < notch_v_th_) {
          is_notch = true;
        }
      }

      if (is_notch) {
        notch_penalty_ += calSingleNotchPenalty(block_->dbuToMicrons(width),
                                                block_->dbuToMicrons(height));
        if (graphics_) {
          graphics_->addNotch(odb::Rect(x_coords[start_col],
                                        y_coords[start_row],
                                        x_coords[end_col + 1],
                                        y_coords[end_row + 1]));
        }
      }

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

#include <cmath>
#include <iterator>
#include <limits>
#include <ranges>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header limits is not used directly [misc-include-cleaner]

Suggested change
#include <ranges>
#include <ranges>

#include <iterator>
#include <limits>
#include <ranges>
#include <set>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header ranges is not used directly [misc-include-cleaner]

Suggested change
#include <set>
#include <set>


if (graphics_) {
graphics_->setNotchPenalty(
{"Notch", notch_weight_, notch_penalty_, norm_notch_penalty_});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use designated initializer list to initialize 'PenaltyData' [modernize-use-designated-initializers]

Suggested change
{"Notch", notch_weight_, notch_penalty_, norm_notch_penalty_});
{.name="Notch", .weight=notch_weight_, .value=notch_penalty_, .normalization_factor=norm_notch_penalty_});
Additional context

src/mpl/src/mpl-util.h:89: aggregate type is defined here

struct PenaltyData
^


std::vector<int> x_coords;
int epsilon = outline_.dx() / 100;
for (size_t i = 0; i < x_point.size(); i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "size_t" is directly included [misc-include-cleaner]

src/mpl/src/SACoreSoftMacro.cpp:9:

- #include <iterator>
+ #include <cstddef>
+ #include <iterator>

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@joaomai joaomai requested a review from AcKoucher January 21, 2026 13:54
Copy link
Contributor

@AcKoucher AcKoucher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the notch function is too big and should be broken into smaller pieces, that's why most of the comments. We've been making a large effort to a more granular code and it has been proven to be a very helpful thing.

I tend to prefer functions over lambdas if it starts to get too big. I think it does depend on the context (it looks that the idea here was to keep the notch detection code well encapsulated), but for concision and readability, let's keep things smaller. We can have a "section" of methods only for notch penalty.

A question: are the notches being erased when rendering after MPL is done? (see eraseDrawing).

bool left = true;
bool right = true;

int total() { return top + bottom + left + right; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of implicit casting. However the logic here is sound. @maliberty would you have an opinion on it?

for (int col = 0; col < num_x; col++) {
if (grid[row][col]) {
continue;
auto neighbors = [&](int row1, int col1, int row2, int col2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a function `findNeighbors".

int end_row = start_row;
int end_col = start_col;

Neighbors n = neighbors(start_row, start_col, end_row, end_col);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, avoid this type of abbreviation.

return neighbors;
};

auto valid = [&](int row1, int col1, int row2, int col2) {
Copy link
Contributor

@AcKoucher AcKoucher Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be a function. Validity doesn't feel like the best quality here. Perhaps expansionOverlapped or something in this sense? I'm not attached to my suggestion.

return;
}

struct Neighbors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a member of the annealer itself. I also think we should have a comment explaining what are Neighbors (seeing a boolean in the code doesn't tell us much).

Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
@joaomai joaomai requested a review from AcKoucher January 22, 2026 12:22
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

painter.drawRect(rect);
}

// notches_.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover?

@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants