ZLayout EDA Library v1.0.0
Advanced Electronic Design Automation Layout Library with Bilingual Documentation
Loading...
Searching...
No Matches
layout_optimizer.cpp
Go to the documentation of this file.
1
5
7#include <algorithm>
8#include <cmath>
9#include <numeric>
10#include <iostream>
11
12namespace zlayout {
13namespace optimization {
14
15// SimulatedAnnealingOptimizer implementation
17 component_index[comp.name] = components.size();
18 components.push_back(comp);
19
20 // Initialize with random position if not set
21 if (components.back().position.x == 0 && components.back().position.y == 0) {
22 components.back().position = generate_random_position();
23 }
24}
25
27 nets.push_back(net);
28}
29
31 // Initialize best positions
32 best_positions.clear();
33 for (const auto& comp : components) {
34 best_positions.push_back(comp.position);
35 }
36
37 // Initial cost evaluation
38 current_cost = evaluate_cost();
39 best_cost = current_cost;
40 current_temperature = config.initial_temperature;
41
42 std::cout << "Starting simulated annealing optimization..." << std::endl;
43 std::cout << "Initial cost: " << current_cost.total_cost << std::endl;
44
45 // Main optimization loop
46 for (size_t iter = 0; iter < config.max_iterations; ++iter) {
47 if (make_random_move()) {
48 auto new_cost = evaluate_cost();
49 double delta_cost = new_cost.total_cost - current_cost.total_cost;
50
51 total_moves++;
52
53 if (delta_cost < 0 || accept_probability(delta_cost)) {
54 // Accept the move
55 accept_move();
56 current_cost = new_cost;
57 accepted_moves++;
58
59 if (new_cost.total_cost < best_cost.total_cost) {
60 best_cost = new_cost;
61 for (size_t i = 0; i < components.size(); ++i) {
62 best_positions[i] = components[i].position;
63 }
64 improved_moves++;
65 }
66 } else {
67 // Reject the move
68 reject_move();
69 }
70 }
71
72 // Cool down
73 current_temperature *= config.cooling_rate;
74
75 // Progress reporting
76 if (iter % 10000 == 0) {
77 double acceptance_rate = static_cast<double>(accepted_moves) / total_moves;
78 std::cout << "Iteration " << iter
79 << ", Temperature: " << current_temperature
80 << ", Cost: " << current_cost.total_cost
81 << ", Acceptance: " << acceptance_rate * 100 << "%"
82 << std::endl;
83 }
84
85 // Termination check
86 if (current_temperature < config.final_temperature) {
87 std::cout << "Reached final temperature, stopping..." << std::endl;
88 break;
89 }
90 }
91
92 // Restore best solution
93 for (size_t i = 0; i < components.size(); ++i) {
94 components[i].position = best_positions[i];
95 }
96
97 std::cout << "Optimization completed!" << std::endl;
98 std::cout << "Final cost: " << best_cost.total_cost << std::endl;
99 std::cout << "Improvement: " << (current_cost.total_cost - best_cost.total_cost) / current_cost.total_cost * 100 << "%" << std::endl;
100
101 return best_cost;
102}
103
104CostResult SimulatedAnnealingOptimizer::evaluate_cost() const {
105 CostResult result = {};
106
107 result.wirelength_cost = calculate_wirelength_cost();
108 result.timing_cost = calculate_timing_cost();
109 result.area_cost = calculate_area_cost();
110 result.power_cost = calculate_power_cost();
111 result.constraint_violations = calculate_constraint_violations();
112
113 result.total_cost = config.wirelength_weight * result.wirelength_cost +
114 config.timing_weight * result.timing_cost +
115 config.area_weight * result.area_cost +
116 config.power_weight * result.power_cost +
117 1000.0 * result.constraint_violations; // Heavy penalty for violations
118
119 return result;
120}
121
122double SimulatedAnnealingOptimizer::calculate_wirelength_cost() const {
123 double total_wirelength = 0.0;
124
125 for (const auto& net : nets) {
126 // Find driver position
127 auto driver_it = component_index.find(net.driver_component);
128 if (driver_it == component_index.end()) continue;
129
130 geometry::Point driver_pos = components[driver_it->second].position;
131
132 // Calculate total wirelength for this net
133 double net_wirelength = 0.0;
134 for (const auto& [sink_comp, sink_pin] : net.sinks) {
135 auto sink_it = component_index.find(sink_comp);
136 if (sink_it == component_index.end()) continue;
137
138 geometry::Point sink_pos = components[sink_it->second].position;
139 double distance = driver_pos.distance_to(sink_pos);
140 net_wirelength += distance;
141 }
142
143 // Weight by net criticality
144 total_wirelength += net_wirelength * net.weight * (1.0 + net.criticality);
145 }
146
147 return total_wirelength;
148}
149
150double SimulatedAnnealingOptimizer::calculate_timing_cost() const {
151 double timing_cost = 0.0;
152
153 for (const auto& net : nets) {
154 if (net.criticality > 0.8) { // Critical nets
155 auto driver_it = component_index.find(net.driver_component);
156 if (driver_it == component_index.end()) continue;
157
158 geometry::Point driver_pos = components[driver_it->second].position;
159
160 for (const auto& [sink_comp, sink_pin] : net.sinks) {
161 auto sink_it = component_index.find(sink_comp);
162 if (sink_it == component_index.end()) continue;
163
164 geometry::Point sink_pos = components[sink_it->second].position;
165 double distance = driver_pos.distance_to(sink_pos);
166
167 // Timing cost increases quadratically with distance for critical nets
168 timing_cost += distance * distance * net.criticality;
169 }
170 }
171 }
172
173 return timing_cost;
174}
175
176double SimulatedAnnealingOptimizer::calculate_area_cost() const {
177 // Calculate bounding box of all components
178 if (components.empty()) return 0.0;
179
180 double min_x = components[0].position.x;
181 double max_x = min_x + components[0].shape.width;
182 double min_y = components[0].position.y;
183 double max_y = min_y + components[0].shape.height;
184
185 for (const auto& comp : components) {
186 min_x = std::min(min_x, comp.position.x);
187 max_x = std::max(max_x, comp.position.x + comp.shape.width);
188 min_y = std::min(min_y, comp.position.y);
189 max_y = std::max(max_y, comp.position.y + comp.shape.height);
190 }
191
192 double total_area = (max_x - min_x) * (max_y - min_y);
193 double target_area = placement_area.area();
194
195 // Penalty for exceeding placement area
196 if (total_area > target_area) {
197 return total_area - target_area;
198 }
199
200 return 0.0;
201}
202
203double SimulatedAnnealingOptimizer::calculate_power_cost() const {
204 // Simple power cost based on component clustering
205 // Components with higher power should be spread out to avoid hot spots
206
207 double power_cost = 0.0;
208
209 for (size_t i = 0; i < components.size(); ++i) {
210 for (size_t j = i + 1; j < components.size(); ++j) {
211 const auto& comp1 = components[i];
212 const auto& comp2 = components[j];
213
214 double distance = comp1.position.distance_to(comp2.position);
215 double power_product = comp1.power_consumption * comp2.power_consumption;
216
217 // Higher cost for high-power components that are close together
218 if (power_product > 0.001 && distance < 10.0) {
219 power_cost += power_product / (distance + 1.0);
220 }
221 }
222 }
223
224 return power_cost;
225}
226
227double SimulatedAnnealingOptimizer::calculate_constraint_violations() const {
228 double violations = 0.0;
229
230 // Check minimum spacing constraints
231 for (size_t i = 0; i < components.size(); ++i) {
232 for (size_t j = i + 1; j < components.size(); ++j) {
233 const auto& comp1 = components[i];
234 const auto& comp2 = components[j];
235
236 // Calculate actual distance between component boundaries
237 geometry::Rectangle rect1(comp1.position.x, comp1.position.y,
238 comp1.shape.width, comp1.shape.height);
239 geometry::Rectangle rect2(comp2.position.x, comp2.position.y,
240 comp2.shape.width, comp2.shape.height);
241
242 double distance = rect1.distance_to(rect2);
243
244 if (distance < config.min_spacing) {
245 violations += (config.min_spacing - distance);
246 }
247 }
248 }
249
250 // Check placement area constraints
251 for (const auto& comp : components) {
252 geometry::Rectangle comp_rect(comp.position.x, comp.position.y,
253 comp.shape.width, comp.shape.height);
254
255 if (!placement_area.contains(comp_rect)) {
256 violations += 100.0; // Heavy penalty for out-of-bounds
257 }
258 }
259
260 return violations;
261}
262
263bool SimulatedAnnealingOptimizer::make_random_move() {
264 if (components.empty()) return false;
265
266 // Select random component (skip fixed components)
267 std::vector<size_t> movable_components;
268 for (size_t i = 0; i < components.size(); ++i) {
269 if (!components[i].is_fixed) {
270 movable_components.push_back(i);
271 }
272 }
273
274 if (movable_components.empty()) return false;
275
276 std::uniform_int_distribution<size_t> comp_dist(0, movable_components.size() - 1);
277 size_t selected_idx = movable_components[comp_dist(rng)];
278
279 // Store old position for potential rollback
280 old_position = components[selected_idx].position;
281 selected_component = selected_idx;
282
283 // Generate random move
284 std::uniform_real_distribution<double> move_dist(-current_temperature, current_temperature);
285 geometry::Point new_pos(
286 components[selected_idx].position.x + move_dist(rng),
287 components[selected_idx].position.y + move_dist(rng)
288 );
289
290 // Check if new position is valid
291 if (is_position_valid(selected_idx, new_pos)) {
292 components[selected_idx].position = new_pos;
293 return true;
294 }
295
296 return false;
297}
298
299void SimulatedAnnealingOptimizer::accept_move() {
300 // Move is already applied, nothing to do
301}
302
303void SimulatedAnnealingOptimizer::reject_move() {
304 // Restore old position
305 if (selected_component < components.size()) {
306 components[selected_component].position = old_position;
307 }
308}
309
310bool SimulatedAnnealingOptimizer::accept_probability(double delta_cost) const {
311 if (current_temperature <= 0) return false;
312
313 double probability = std::exp(-delta_cost / current_temperature);
314 std::uniform_real_distribution<double> uniform(0.0, 1.0);
315
316 return uniform(rng) < probability;
317}
318
319geometry::Point SimulatedAnnealingOptimizer::generate_random_position() const {
320 std::uniform_real_distribution<double> x_dist(placement_area.x,
321 placement_area.x + placement_area.width);
322 std::uniform_real_distribution<double> y_dist(placement_area.y,
323 placement_area.y + placement_area.height);
324
325 return geometry::Point(x_dist(rng), y_dist(rng));
326}
327
328bool SimulatedAnnealingOptimizer::is_position_valid(size_t comp_idx, const geometry::Point& pos) const {
329 const auto& comp = components[comp_idx];
330
331 // Check if component stays within placement area
332 if (pos.x < placement_area.x ||
333 pos.y < placement_area.y ||
334 pos.x + comp.shape.width > placement_area.x + placement_area.width ||
335 pos.y + comp.shape.height > placement_area.y + placement_area.height) {
336 return false;
337 }
338
339 return true;
340}
341
343 Statistics stats;
344 stats.total_moves = total_moves;
345 stats.accepted_moves = accepted_moves;
346 stats.improved_moves = improved_moves;
347 stats.acceptance_rate = total_moves > 0 ? static_cast<double>(accepted_moves) / total_moves : 0.0;
348 stats.improvement_rate = total_moves > 0 ? static_cast<double>(improved_moves) / total_moves : 0.0;
349 stats.final_cost = best_cost;
350
351 return stats;
352}
353
354// ForceDirectedPlacer implementation
355bool ForceDirectedPlacer::optimize(size_t max_iterations) {
356 std::cout << "Starting force-directed placement..." << std::endl;
357
358 std::vector<geometry::Point> velocities(components.size(), geometry::Point(0, 0));
359
360 for (size_t iter = 0; iter < max_iterations; ++iter) {
361 bool converged = true;
362
363 // Calculate forces for each component
364 for (size_t i = 0; i < components.size(); ++i) {
365 if (components[i]->is_fixed) continue;
366
367 geometry::Point total_force(0, 0);
368
369 // Net forces (attraction)
370 auto net_force = calculate_net_force(*components[i]);
371 total_force.x += net_force.x;
372 total_force.y += net_force.y;
373
374 // Repulsion forces
375 auto repulsion_force = calculate_repulsion_force(*components[i]);
376 total_force.x += repulsion_force.x;
377 total_force.y += repulsion_force.y;
378
379 // Boundary forces
380 auto boundary_force = calculate_boundary_force(*components[i]);
381 total_force.x += boundary_force.x;
382 total_force.y += boundary_force.y;
383
384 // Update velocity with damping
385 velocities[i].x = velocities[i].x * damping_factor + total_force.x * time_step;
386 velocities[i].y = velocities[i].y * damping_factor + total_force.y * time_step;
387
388 // Update position
389 components[i]->position.x += velocities[i].x * time_step;
390 components[i]->position.y += velocities[i].y * time_step;
391
392 // Check convergence
393 if (std::abs(velocities[i].x) > 0.1 || std::abs(velocities[i].y) > 0.1) {
394 converged = false;
395 }
396 }
397
398 if (iter % 100 == 0) {
399 std::cout << "Iteration " << iter << std::endl;
400 }
401
402 if (converged) {
403 std::cout << "Force-directed placement converged at iteration " << iter << std::endl;
404 return true;
405 }
406 }
407
408 std::cout << "Force-directed placement completed (max iterations reached)" << std::endl;
409 return false;
410}
411
412geometry::Point ForceDirectedPlacer::calculate_net_force(const Component& comp) const {
413 geometry::Point force(0, 0);
414
415 for (const auto& net : nets) {
416 bool is_connected = false;
417
418 // Check if this component is connected to the net
419 if (net.driver_component == comp.name) {
420 is_connected = true;
421 } else {
422 for (const auto& [sink_comp, sink_pin] : net.sinks) {
423 if (sink_comp == comp.name) {
424 is_connected = true;
425 break;
426 }
427 }
428 }
429
430 if (!is_connected) continue;
431
432 // Calculate center of mass of connected components
433 geometry::Point center_of_mass(0, 0);
434 int connected_count = 0;
435
436 for (const auto& other_comp : components) {
437 if (other_comp->name == comp.name) continue;
438
439 bool is_other_connected = false;
440 if (net.driver_component == other_comp->name) {
441 is_other_connected = true;
442 } else {
443 for (const auto& [sink_comp, sink_pin] : net.sinks) {
444 if (sink_comp == other_comp->name) {
445 is_other_connected = true;
446 break;
447 }
448 }
449 }
450
451 if (is_other_connected) {
452 center_of_mass.x += other_comp->position.x;
453 center_of_mass.y += other_comp->position.y;
454 connected_count++;
455 }
456 }
457
458 if (connected_count > 0) {
459 center_of_mass.x /= connected_count;
460 center_of_mass.y /= connected_count;
461
462 // Apply spring force towards center of mass
463 double dx = center_of_mass.x - comp.position.x;
464 double dy = center_of_mass.y - comp.position.y;
465
466 force.x += spring_constant * dx * net.weight;
467 force.y += spring_constant * dy * net.weight;
468 }
469 }
470
471 return force;
472}
473
474geometry::Point ForceDirectedPlacer::calculate_repulsion_force(const Component& comp) const {
475 geometry::Point force(0, 0);
476
477 for (const auto& other_comp : components) {
478 if (other_comp->name == comp.name) continue;
479
480 double dx = comp.position.x - other_comp->position.x;
481 double dy = comp.position.y - other_comp->position.y;
482 double distance_sq = dx * dx + dy * dy;
483
484 if (distance_sq > 0) {
485 double distance = std::sqrt(distance_sq);
486 double repulsion = repulsion_constant / distance_sq;
487
488 force.x += repulsion * dx / distance;
489 force.y += repulsion * dy / distance;
490 }
491 }
492
493 return force;
494}
495
496geometry::Point ForceDirectedPlacer::calculate_boundary_force(const Component& comp) const {
497 geometry::Point force(0, 0);
498
499 // Push components back into placement area
500 double left_dist = comp.position.x - placement_area.x;
501 double right_dist = (placement_area.x + placement_area.width) - (comp.position.x + comp.shape.width);
502 double bottom_dist = comp.position.y - placement_area.y;
503 double top_dist = (placement_area.y + placement_area.height) - (comp.position.y + comp.shape.height);
504
505 double boundary_strength = 100.0;
506
507 if (left_dist < 0) force.x += boundary_strength * (-left_dist);
508 if (right_dist < 0) force.x -= boundary_strength * (-right_dist);
509 if (bottom_dist < 0) force.y += boundary_strength * (-bottom_dist);
510 if (top_dist < 0) force.y -= boundary_strength * (-top_dist);
511
512 return force;
513}
514
515// OptimizerFactory implementation
516std::unique_ptr<SimulatedAnnealingOptimizer> OptimizerFactory::create_sa_optimizer(
517 const geometry::Rectangle& area,
518 const OptimizationConfig& config) {
519 return std::make_unique<SimulatedAnnealingOptimizer>(area, config);
520}
521
522std::unique_ptr<HierarchicalOptimizer> OptimizerFactory::create_hierarchical_optimizer(
523 const geometry::Rectangle& area,
524 const OptimizationConfig& config) {
525 return std::make_unique<HierarchicalOptimizer>(area, config);
526}
527
528std::unique_ptr<ForceDirectedPlacer> OptimizerFactory::create_force_directed_placer(
529 const geometry::Rectangle& area) {
530 return std::make_unique<ForceDirectedPlacer>(area);
531}
532
534 size_t component_count,
535 size_t net_count,
536 bool timing_critical) {
537
538 if (component_count > 100000) {
540 } else if (timing_critical) {
542 } else if (component_count > 1000) {
544 } else {
546 }
547}
548
549} // namespace optimization
550} // namespace zlayout
2D point with high-precision coordinates and utility methods
Definition point.hpp:23
double x
X coordinate.
Definition point.hpp:25
double y
Y coordinate.
Definition point.hpp:26
Axis-aligned rectangle for bounding boxes and simple EDA components.
Definition rectangle.hpp:26
bool optimize(size_t max_iterations=1000)
Run force-directed placement.
static std::unique_ptr< HierarchicalOptimizer > create_hierarchical_optimizer(const geometry::Rectangle &area, const OptimizationConfig &config=OptimizationConfig{})
static std::unique_ptr< SimulatedAnnealingOptimizer > create_sa_optimizer(const geometry::Rectangle &area, const OptimizationConfig &config=OptimizationConfig{})
static std::unique_ptr< ForceDirectedPlacer > create_force_directed_placer(const geometry::Rectangle &area)
static AlgorithmType recommend_algorithm(size_t component_count, size_t net_count, bool timing_critical=false)
Choose optimal algorithm based on problem characteristics.
CostResult optimize()
Run simulated annealing optimization.
Advanced EDA layout optimization algorithms.
double distance(const Point &p1, const Point &p2)
Calculate distance between two points.
Definition point.cpp:161
Main namespace for ZLayout library.
Definition component.hpp:20
Circuit component with connectivity information.
Net (electrical connection) between components.
Layout optimization objectives and constraints.