From a1cccde477359b800db92ec758749f782e0ca7bf Mon Sep 17 00:00:00 2001 From: prithviseran Date: Mon, 29 Sep 2025 20:25:01 -0400 Subject: [PATCH 01/13] solved challenge --- README.md | 2 +- placement.py | 96 ++++++++++++++++++++++++++++++++++++++++++++-------- test.py | 8 +++-- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 5ad8080..b7f0c82 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Also, if you think there are any bugs in the provided code, please feel free to | Rank | Name | Overlap | Wirelength (um) | Runtime (s) | Notes | |------|-----------------|-------------|-----------------|-------------|----------------------| | 1 | partcl baseline | 0.8 | 0.4 | 5 | Baseline solution | -| 2 | *Add yours!* | | | | | +| 2 | Prithvi Seran | 0.0651 | 0.4596 | 992.84 | Almost all the time was taken up by the last test (took 870 seconds)| > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. diff --git a/placement.py b/placement.py index d70412d..fbb3e1a 100644 --- a/placement.py +++ b/placement.py @@ -284,7 +284,7 @@ def wirelength_attraction_loss(cell_features, pin_features, edge_list): # Calculate smooth approximation of Manhattan distance # Using log-sum-exp approximation for differentiability - alpha = 0.1 # Smoothing parameter + alpha = 0.02 # Smoothing parameter dx = torch.abs(src_x - tgt_x) dy = torch.abs(src_y - tgt_y) @@ -343,21 +343,86 @@ def overlap_repulsion_loss(cell_features, pin_features, edge_list): Returns: Scalar loss value (should be 0 when no overlaps exist) """ - N = cell_features.shape[0] - if N <= 1: - return torch.tensor(0.0, requires_grad=True) - # TODO: Implement overlap detection and loss calculation here - # - # Your implementation should: - # 1. Extract cell positions, widths, and heights - # 2. Compute pairwise overlaps using vectorized operations - # 3. Return a scalar loss that is zero when no overlaps exist - # - # Delete this placeholder and add your implementation: + import torch + + eps = 1e-8 + + positions = cell_features[:, 2:4] + widths = cell_features[:, 4] + heights = cell_features[:, 5] + areas = cell_features[:, 0] + + N = positions.size(0) + + if N < 2: + return torch.tensor(0.0, device=cell_features.device, requires_grad=True) + + # Broadcast positions for pairwise comparisons + pos_i = positions.unsqueeze(1) # [N, 1, 2] + pos_j = positions.unsqueeze(0) # [1, N, 2] + delta = (pos_i - pos_j).abs() # [N, N, 2] + + # Broadcast dimensions + w_i = widths.unsqueeze(1) + w_j = widths.unsqueeze(0) + h_i = heights.unsqueeze(1) + h_j = heights.unsqueeze(0) + + area_i = areas.unsqueeze(1) + area_j = areas.unsqueeze(0) + min_area = torch.minimum(area_i, area_j) + + # Only penalize when cells actually overlap, not when they're close + overlap_x = torch.relu((w_i + w_j) / 2.0 - delta[:, :, 0]) + overlap_y = torch.relu((h_i + h_j) / 2.0 - delta[:, :, 1]) + overlap_area = overlap_x * overlap_y + + # Normalize by smaller cell area + relative_overlap = overlap_area / (min_area + eps) + + # Only consider upper triangle to avoid double counting + mask = torch.triu(torch.ones(N, N, device=cell_features.device), diagonal=1) + + overlap_count = ((overlap_area * mask) > eps).float().sum() + + # Smooth indicator for overlaps (differentiable counting) + k = 50.0 # Moderate steepness for stability + overlap_indicator = torch.sigmoid(k * relative_overlap) + + # Different strategies based on number of overlaps + if overlap_count == 0: + # No overlaps - return zero loss + return torch.tensor(0.0, device=cell_features.device, requires_grad=True) + + elif overlap_count <= 3: + # Very few overlaps: aggressive penalty to eliminate them + # Use high power to strongly penalize even small overlaps + penalty = (relative_overlap ** 1.5 * mask).sum() * 1000.0 + count_penalty = (overlap_indicator * mask).sum() * 500.0 + total_loss = penalty + count_penalty + + elif overlap_count <= 10: + # Few overlaps: strong penalty + penalty = (relative_overlap ** 1.5 * mask).sum() * 500.0 + count_penalty = (overlap_indicator * mask).sum() * 200.0 + total_loss = penalty + count_penalty + + elif overlap_count <= 30: + # Moderate overlaps: balanced approach + penalty = (relative_overlap ** 2 * mask).sum() * 200.0 + count_penalty = (overlap_indicator * mask).sum() * 100.0 + total_loss = penalty + count_penalty + + else: + # Many overlaps: gentler penalty (let system organize) + # Use quadratic to provide smooth gradients + penalty = (relative_overlap ** 2 * mask).sum() * 100.0 + count_penalty = (overlap_indicator * mask).sum() * 50.0 + total_loss = penalty + count_penalty + + return total_loss - # Placeholder - returns a constant loss (REPLACE THIS!) - return torch.tensor(1.0, requires_grad=True) def train_placement( @@ -759,7 +824,10 @@ def main(): pin_features, edge_list, verbose=True, + num_epochs = 15500, log_interval=200, + lambda_wirelength=7, + lambda_overlap=80.0 ) # Calculate final metrics (both detailed and normalized) diff --git a/test.py b/test.py index 5f9a6f5..ed17642 100644 --- a/test.py +++ b/test.py @@ -6,7 +6,7 @@ of various sizes and reports metrics for leaderboard submission. Usage: - python test_placement.py + python test.py Metrics Reported: - Average Overlap: (num cells with overlaps / total num cells) @@ -93,7 +93,11 @@ def run_placement_test( cell_features, pin_features, edge_list, - verbose=False, # Suppress per-epoch output + verbose=True, + num_epochs = 15500, + log_interval=200, + lambda_wirelength=7, + lambda_overlap=80.0 ) elapsed_time = time.time() - start_time From e705dc82ba1cec6891a474663bbb0e352e5dd1f8 Mon Sep 17 00:00:00 2001 From: prithviseran Date: Mon, 29 Sep 2025 20:31:15 -0400 Subject: [PATCH 02/13] solved challenge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b7f0c82..dc60d4c 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,7 @@ Also, if you think there are any bugs in the provided code, please feel free to > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. +## How to access my submission +My code is available in the fork :) + Good luck! \ No newline at end of file From e66c50088eb0cbae9db53f0812d6f5f2a2825f8f Mon Sep 17 00:00:00 2001 From: prithviseran Date: Mon, 29 Sep 2025 22:15:59 -0400 Subject: [PATCH 03/13] much better results --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index dc60d4c..24edeaf 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,10 @@ Also, if you think there are any bugs in the provided code, please feel free to | Rank | Name | Overlap | Wirelength (um) | Runtime (s) | Notes | |------|-----------------|-------------|-----------------|-------------|----------------------| | 1 | partcl baseline | 0.8 | 0.4 | 5 | Baseline solution | -| 2 | Prithvi Seran | 0.0651 | 0.4596 | 992.84 | Almost all the time was taken up by the last test (took 870 seconds)| +| 2 | Prithvi Seran | 0.0499 | 0.4890 | 398.58 | | > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. -## How to access my submission -My code is available in the fork :) Good luck! \ No newline at end of file From 676b11bc66b85288d01ca063deafe2ab6894475f Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 22:18:01 -0400 Subject: [PATCH 04/13] Update placement.py --- placement.py | 96 ++++++++-------------------------------------------- 1 file changed, 14 insertions(+), 82 deletions(-) diff --git a/placement.py b/placement.py index fbb3e1a..d70412d 100644 --- a/placement.py +++ b/placement.py @@ -284,7 +284,7 @@ def wirelength_attraction_loss(cell_features, pin_features, edge_list): # Calculate smooth approximation of Manhattan distance # Using log-sum-exp approximation for differentiability - alpha = 0.02 # Smoothing parameter + alpha = 0.1 # Smoothing parameter dx = torch.abs(src_x - tgt_x) dy = torch.abs(src_y - tgt_y) @@ -343,86 +343,21 @@ def overlap_repulsion_loss(cell_features, pin_features, edge_list): Returns: Scalar loss value (should be 0 when no overlaps exist) """ + N = cell_features.shape[0] + if N <= 1: + return torch.tensor(0.0, requires_grad=True) - import torch - - eps = 1e-8 - - positions = cell_features[:, 2:4] - widths = cell_features[:, 4] - heights = cell_features[:, 5] - areas = cell_features[:, 0] - - N = positions.size(0) - - if N < 2: - return torch.tensor(0.0, device=cell_features.device, requires_grad=True) - - # Broadcast positions for pairwise comparisons - pos_i = positions.unsqueeze(1) # [N, 1, 2] - pos_j = positions.unsqueeze(0) # [1, N, 2] - delta = (pos_i - pos_j).abs() # [N, N, 2] - - # Broadcast dimensions - w_i = widths.unsqueeze(1) - w_j = widths.unsqueeze(0) - h_i = heights.unsqueeze(1) - h_j = heights.unsqueeze(0) - - area_i = areas.unsqueeze(1) - area_j = areas.unsqueeze(0) - min_area = torch.minimum(area_i, area_j) - - # Only penalize when cells actually overlap, not when they're close - overlap_x = torch.relu((w_i + w_j) / 2.0 - delta[:, :, 0]) - overlap_y = torch.relu((h_i + h_j) / 2.0 - delta[:, :, 1]) - overlap_area = overlap_x * overlap_y - - # Normalize by smaller cell area - relative_overlap = overlap_area / (min_area + eps) - - # Only consider upper triangle to avoid double counting - mask = torch.triu(torch.ones(N, N, device=cell_features.device), diagonal=1) - - overlap_count = ((overlap_area * mask) > eps).float().sum() - - # Smooth indicator for overlaps (differentiable counting) - k = 50.0 # Moderate steepness for stability - overlap_indicator = torch.sigmoid(k * relative_overlap) - - # Different strategies based on number of overlaps - if overlap_count == 0: - # No overlaps - return zero loss - return torch.tensor(0.0, device=cell_features.device, requires_grad=True) - - elif overlap_count <= 3: - # Very few overlaps: aggressive penalty to eliminate them - # Use high power to strongly penalize even small overlaps - penalty = (relative_overlap ** 1.5 * mask).sum() * 1000.0 - count_penalty = (overlap_indicator * mask).sum() * 500.0 - total_loss = penalty + count_penalty - - elif overlap_count <= 10: - # Few overlaps: strong penalty - penalty = (relative_overlap ** 1.5 * mask).sum() * 500.0 - count_penalty = (overlap_indicator * mask).sum() * 200.0 - total_loss = penalty + count_penalty - - elif overlap_count <= 30: - # Moderate overlaps: balanced approach - penalty = (relative_overlap ** 2 * mask).sum() * 200.0 - count_penalty = (overlap_indicator * mask).sum() * 100.0 - total_loss = penalty + count_penalty - - else: - # Many overlaps: gentler penalty (let system organize) - # Use quadratic to provide smooth gradients - penalty = (relative_overlap ** 2 * mask).sum() * 100.0 - count_penalty = (overlap_indicator * mask).sum() * 50.0 - total_loss = penalty + count_penalty - - return total_loss + # TODO: Implement overlap detection and loss calculation here + # + # Your implementation should: + # 1. Extract cell positions, widths, and heights + # 2. Compute pairwise overlaps using vectorized operations + # 3. Return a scalar loss that is zero when no overlaps exist + # + # Delete this placeholder and add your implementation: + # Placeholder - returns a constant loss (REPLACE THIS!) + return torch.tensor(1.0, requires_grad=True) def train_placement( @@ -824,10 +759,7 @@ def main(): pin_features, edge_list, verbose=True, - num_epochs = 15500, log_interval=200, - lambda_wirelength=7, - lambda_overlap=80.0 ) # Calculate final metrics (both detailed and normalized) From 7122c738a9ff38770b5da9382cecf2f36745b92a Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 22:18:58 -0400 Subject: [PATCH 05/13] Update test.py --- test.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test.py b/test.py index ed17642..d5e02e9 100644 --- a/test.py +++ b/test.py @@ -6,7 +6,7 @@ of various sizes and reports metrics for leaderboard submission. Usage: - python test.py + python test_placement.py Metrics Reported: - Average Overlap: (num cells with overlaps / total num cells) @@ -93,11 +93,7 @@ def run_placement_test( cell_features, pin_features, edge_list, - verbose=True, - num_epochs = 15500, - log_interval=200, - lambda_wirelength=7, - lambda_overlap=80.0 + verbose=False, # Suppress per-epoch output ) elapsed_time = time.time() - start_time @@ -193,4 +189,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From 33aeeeefb7fa12f33f32167a6ffd6f8243b0de08 Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 22:38:53 -0400 Subject: [PATCH 06/13] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24edeaf..5c7e759 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,10 @@ Also, if you think there are any bugs in the provided code, please feel free to |------|-----------------|-------------|-----------------|-------------|----------------------| | 1 | partcl baseline | 0.8 | 0.4 | 5 | Baseline solution | | 2 | Prithvi Seran | 0.0499 | 0.4890 | 398.58 | | +| 3 | Prithvi Seran | 0.0579 | 0.4911 | 318.10 | | > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. -Good luck! \ No newline at end of file +Good luck! From cefafe08ef5fa817fae740dd7dff9475ac2c0994 Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 22:40:58 -0400 Subject: [PATCH 07/13] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5c7e759..a573ec9 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Also, if you think there are any bugs in the provided code, please feel free to | 2 | Prithvi Seran | 0.0499 | 0.4890 | 398.58 | | | 3 | Prithvi Seran | 0.0579 | 0.4911 | 318.10 | | +PS: There are two entries because those were my two best results. + > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. From 1de5f4f7836609673f08785ba5319dd1fb6a7a1e Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 22:57:14 -0400 Subject: [PATCH 08/13] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a573ec9..050450d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ Also, if you think there are any bugs in the provided code, please feel free to PS: There are two entries because those were my two best results. +My code is in the "full-code" branch + > **To add your results:** > Insert a new row in the table above with your name, overlap, wirelength, and any notes. From f592ee3123e0bde04d96787042993b23ef757518 Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 23:20:40 -0400 Subject: [PATCH 09/13] Update test.py From c52c266fe5635ff6fc27004d9ee33717004cdf61 Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 23:21:32 -0400 Subject: [PATCH 10/13] Update test.py --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index d5e02e9..419a36e 100644 --- a/test.py +++ b/test.py @@ -189,4 +189,4 @@ def main(): if __name__ == "__main__": - main() + main() From 5133826cf56c7d5c1504ccdf566650351f58036b Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 23:22:53 -0400 Subject: [PATCH 11/13] Update test.py --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 419a36e..d5e02e9 100644 --- a/test.py +++ b/test.py @@ -189,4 +189,4 @@ def main(): if __name__ == "__main__": - main() + main() From 5af5927d56c9ab6b5ad58d22d2ea053032bdcd25 Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 23:24:08 -0400 Subject: [PATCH 12/13] Update test.py --- test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test.py b/test.py index d5e02e9..183bde6 100644 --- a/test.py +++ b/test.py @@ -190,3 +190,4 @@ def main(): if __name__ == "__main__": main() + From 75e211cd4f5bd47b333b53ae221aa77fae10399e Mon Sep 17 00:00:00 2001 From: Prithvi Seran Date: Mon, 29 Sep 2025 23:24:50 -0400 Subject: [PATCH 13/13] Update test.py --- test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test.py b/test.py index 183bde6..d5e02e9 100644 --- a/test.py +++ b/test.py @@ -190,4 +190,3 @@ def main(): if __name__ == "__main__": main() -