Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
|
@@ -151,9 +152,8 @@ class AdvancedGridOptimizer:
|
|
| 151 |
colors = self.color_schemes[self.current_scheme]
|
| 152 |
|
| 153 |
x_pos = 0
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
# Set up main plot with RPM green background
|
| 157 |
ax1.set_xlim(-5, stage_width + 5)
|
| 158 |
# Adjust y-limits for rear laneway
|
| 159 |
if self.md_load_type == 'rear':
|
|
@@ -292,43 +292,22 @@ class AdvancedGridOptimizer:
|
|
| 292 |
# Add lot information (positioned inside the lot)
|
| 293 |
lot_center_y = 8 + lot_height / 2 # Center of the lot
|
| 294 |
|
| 295 |
-
#
|
| 296 |
-
ax1.text(x_pos + width/2, lot_center_y + lot_height/3, f'L{lot_num}',
|
| 297 |
-
ha='center', va='center', fontsize=16, fontweight='bold', color='white')
|
| 298 |
-
|
| 299 |
-
# Width in middle
|
| 300 |
ax1.text(x_pos + width/2, lot_center_y, f'{width:.1f}m',
|
| 301 |
-
ha='center', va='center', fontsize=
|
| 302 |
-
|
| 303 |
-
# Lot type
|
| 304 |
-
if int(width) in self.lot_specifications:
|
| 305 |
-
spec = self.lot_specifications[int(width)]
|
| 306 |
-
elif width in self.lot_specifications:
|
| 307 |
-
spec = self.lot_specifications[width]
|
| 308 |
-
else:
|
| 309 |
-
closest_width = min(self.lot_specifications.keys(),
|
| 310 |
-
key=lambda x: abs(x - width))
|
| 311 |
-
spec = self.lot_specifications[closest_width]
|
| 312 |
-
spec = {**spec, 'type': 'Custom'}
|
| 313 |
|
| 314 |
-
|
| 315 |
if is_corner:
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
lot_type_text += f"\n{spec['build']}"
|
| 321 |
-
|
| 322 |
-
# Type label at bottom of lot interior
|
| 323 |
-
ax1.text(x_pos + width/2, lot_center_y - lot_height/3, lot_type_text,
|
| 324 |
-
ha='center', va='center', fontsize=11,
|
| 325 |
-
bbox=dict(boxstyle="round,pad=0.3", facecolor='#545D51',
|
| 326 |
-
edgecolor='white', alpha=0.9), color='white')
|
| 327 |
|
| 328 |
-
# Dimension lines
|
| 329 |
-
|
| 330 |
-
ax1.plot([x_pos, x_pos], [
|
| 331 |
-
ax1.plot([x_pos
|
|
|
|
| 332 |
|
| 333 |
# Add garage indicators for rear loaded
|
| 334 |
if self.md_load_type == 'rear':
|
|
@@ -340,7 +319,6 @@ class AdvancedGridOptimizer:
|
|
| 340 |
ax1.add_patch(garage)
|
| 341 |
|
| 342 |
x_pos += width
|
| 343 |
-
lot_num += 1
|
| 344 |
|
| 345 |
# Add rear alignment line across all lots
|
| 346 |
rear_y = 8 + lot_height
|
|
@@ -383,12 +361,18 @@ class AdvancedGridOptimizer:
|
|
| 383 |
for i in range(len(solution) - 1):
|
| 384 |
if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
|
| 385 |
slhc_pairs += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
| 386 |
else:
|
| 387 |
# MD metrics
|
| 388 |
narrow_count = sum(1 for w, _ in solution if w <= 6.0)
|
| 389 |
standard_count = sum(1 for w, _ in solution if 6.0 < w <= 8.0)
|
| 390 |
wide_count = sum(1 for w, _ in solution if w > 8.0)
|
| 391 |
slhc_pairs = 0 # Not applicable for MD
|
|
|
|
|
|
|
| 392 |
|
| 393 |
# Calculate actual total width and variance
|
| 394 |
total_width = sum(w for w, _ in solution)
|
|
@@ -409,9 +393,9 @@ class AdvancedGridOptimizer:
|
|
| 409 |
f"🎯 DIVERSITY: {diversity_score:.0%} ({unique_widths} types)",
|
| 410 |
f"📏 GRID VARIANCE: {variance:+.2f}m",
|
| 411 |
"",
|
| 412 |
-
f"{'Narrow (≤6m)' if self.development_mode == 'medium_density' else 'SLHC (≤10.5m)'}: {narrow_count
|
| 413 |
f"{'Standard (6-8m)' if self.development_mode == 'medium_density' else 'Standard (11-14m)'}: {standard_count} lots",
|
| 414 |
-
f"{'Wide (>8m)' if self.development_mode == 'medium_density' else 'Premium (>14m)'}: {wide_count
|
| 415 |
"",
|
| 416 |
f"{'🚗 Access: ' + ('Rear Laneway' if self.md_load_type == 'rear' else 'Front Loaded') if self.development_mode == 'medium_density' else f'🚗 SLHC Pairs: {slhc_pairs}'}",
|
| 417 |
yield_text
|
|
@@ -513,14 +497,12 @@ class AdvancedGridOptimizer:
|
|
| 513 |
best_solution = None
|
| 514 |
best_fitness = -float('inf')
|
| 515 |
|
| 516 |
-
#
|
| 517 |
if self.development_mode == 'medium_density':
|
| 518 |
min_internal = min(internal_widths) if internal_widths else 4.5
|
| 519 |
-
min_corner_width = base_corner_width - tolerance
|
| 520 |
else:
|
| 521 |
-
# Ensure corners are at least as wide as smallest internal lot
|
| 522 |
min_internal = min(internal_widths) if internal_widths else 8.5
|
| 523 |
-
|
| 524 |
|
| 525 |
# Try variations of corner widths within tolerance
|
| 526 |
variations = np.arange(min_corner_width,
|
|
@@ -538,11 +520,10 @@ class AdvancedGridOptimizer:
|
|
| 538 |
internal_solution = self.find_exact_solution_with_diversity(internal_width, internal_widths)
|
| 539 |
|
| 540 |
if internal_solution:
|
| 541 |
-
#
|
| 542 |
-
if
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
continue
|
| 546 |
|
| 547 |
# Build complete solution
|
| 548 |
solution = [(round(corner1, 1), 'corner')]
|
|
@@ -577,9 +558,10 @@ class AdvancedGridOptimizer:
|
|
| 577 |
|
| 578 |
# Strategy 2: Try flexible corners if enabled
|
| 579 |
if allow_custom_corners and standard_internal:
|
| 580 |
-
#
|
| 581 |
if self.development_mode == 'medium_density':
|
| 582 |
-
|
|
|
|
| 583 |
else:
|
| 584 |
# For conventional, use traditional corner widths
|
| 585 |
corner_bases = [11.0, 13.3, 14.8, 16.8, 14.0, 16.0]
|
|
@@ -602,15 +584,10 @@ class AdvancedGridOptimizer:
|
|
| 602 |
|
| 603 |
# Separate widths by size
|
| 604 |
all_widths = sorted(enabled_widths)
|
|
|
|
| 605 |
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
corner_options = all_widths
|
| 609 |
-
min_internal_width = min(all_widths) if all_widths else 4.5
|
| 610 |
-
else:
|
| 611 |
-
# For conventional, maintain hierarchy
|
| 612 |
-
min_internal_width = min(all_widths)
|
| 613 |
-
corner_options = [w for w in enabled_widths if w >= max(11.0, min_internal_width)]
|
| 614 |
|
| 615 |
best_solution = None
|
| 616 |
best_fitness = -float('inf')
|
|
@@ -632,11 +609,10 @@ class AdvancedGridOptimizer:
|
|
| 632 |
)
|
| 633 |
|
| 634 |
for internal_widths in internal_solutions:
|
| 635 |
-
#
|
| 636 |
-
if
|
| 637 |
-
|
| 638 |
-
if
|
| 639 |
-
continue
|
| 640 |
|
| 641 |
# Build complete solution
|
| 642 |
solution = [(corner1, 'corner')]
|
|
@@ -651,25 +627,22 @@ class AdvancedGridOptimizer:
|
|
| 651 |
best_fitness = fitness
|
| 652 |
best_solution = optimized
|
| 653 |
|
| 654 |
-
# If no good solution, try without strict corner rules
|
| 655 |
if not best_solution:
|
| 656 |
all_solutions = []
|
| 657 |
self.find_all_combinations_recursive(stage_width, sorted(enabled_widths),
|
| 658 |
[], all_solutions, 20)
|
| 659 |
|
| 660 |
for widths in all_solutions[:50]:
|
| 661 |
-
#
|
| 662 |
-
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
else:
|
| 665 |
-
|
| 666 |
-
sorted_widths = sorted(widths)
|
| 667 |
-
if len(sorted_widths) >= 2:
|
| 668 |
-
solution = [(sorted_widths[-1], 'corner')]
|
| 669 |
-
solution.extend([(w, 'standard') for w in sorted_widths[:-2]])
|
| 670 |
-
solution.append((sorted_widths[-2], 'corner'))
|
| 671 |
-
else:
|
| 672 |
-
solution = [(w, 'standard') for w in widths]
|
| 673 |
|
| 674 |
optimized = self.optimize_lot_grouping(solution)
|
| 675 |
fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
|
|
@@ -966,36 +939,40 @@ class AdvancedGridOptimizer:
|
|
| 966 |
fitness -= max_repetition * 500 # Penalty for too many of same width
|
| 967 |
fitness += diversity_ratio * 3000 # Bonus for good diversity ratio
|
| 968 |
|
| 969 |
-
#
|
| 970 |
-
if
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
-
corner_diff = abs(first_width - last_width)
|
| 990 |
-
if corner_diff < 0.1:
|
| 991 |
-
fitness += 1500 # Perfect match
|
| 992 |
-
elif corner_diff <= 1.0:
|
| 993 |
-
fitness += 1000 # Very good
|
| 994 |
-
elif corner_diff <= 2.0:
|
| 995 |
-
fitness += 500 # Good
|
| 996 |
-
else:
|
| 997 |
-
fitness -= 500 # Poor balance
|
| 998 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 999 |
# SLHC grouping bonus
|
| 1000 |
for i in range(len(solution) - 1):
|
| 1001 |
if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
|
|
|
|
| 1 |
+
|
| 2 |
import gradio as gr
|
| 3 |
import pandas as pd
|
| 4 |
import numpy as np
|
|
|
|
| 152 |
colors = self.color_schemes[self.current_scheme]
|
| 153 |
|
| 154 |
x_pos = 0
|
| 155 |
+
for i, (width, lot_type) in enumerate(solution):
|
| 156 |
+
# Get base color
|
|
|
|
| 157 |
ax1.set_xlim(-5, stage_width + 5)
|
| 158 |
# Adjust y-limits for rear laneway
|
| 159 |
if self.md_load_type == 'rear':
|
|
|
|
| 292 |
# Add lot information (positioned inside the lot)
|
| 293 |
lot_center_y = 8 + lot_height / 2 # Center of the lot
|
| 294 |
|
| 295 |
+
# Just show width in center (like conventional)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
ax1.text(x_pos + width/2, lot_center_y, f'{width:.1f}m',
|
| 297 |
+
ha='center', va='center', fontsize=16, fontweight='bold', color='white')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
+
# Only show CORNER label for corner lots - positioned lower
|
| 300 |
if is_corner:
|
| 301 |
+
ax1.text(x_pos + width/2, 8 + lot_height/4, "CORNER",
|
| 302 |
+
ha='center', va='center', fontsize=12,
|
| 303 |
+
bbox=dict(boxstyle="round,pad=0.3", facecolor='#545D51',
|
| 304 |
+
edgecolor='white', alpha=0.9, linewidth=1.5), color='white')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
+
# Dimension lines - make more visible
|
| 307 |
+
dim_y = 8 + lot_height + 2 # Just above the lot
|
| 308 |
+
ax1.plot([x_pos, x_pos + width], [dim_y, dim_y], 'w-', linewidth=1.5, alpha=0.6)
|
| 309 |
+
ax1.plot([x_pos, x_pos], [dim_y - 1, dim_y + 1], 'w-', linewidth=1.5, alpha=0.6)
|
| 310 |
+
ax1.plot([x_pos + width, x_pos + width], [dim_y - 1, dim_y + 1], 'w-', linewidth=1.5, alpha=0.6)
|
| 311 |
|
| 312 |
# Add garage indicators for rear loaded
|
| 313 |
if self.md_load_type == 'rear':
|
|
|
|
| 319 |
ax1.add_patch(garage)
|
| 320 |
|
| 321 |
x_pos += width
|
|
|
|
| 322 |
|
| 323 |
# Add rear alignment line across all lots
|
| 324 |
rear_y = 8 + lot_height
|
|
|
|
| 361 |
for i in range(len(solution) - 1):
|
| 362 |
if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
|
| 363 |
slhc_pairs += 1
|
| 364 |
+
|
| 365 |
+
# Set MD-specific variables to avoid reference errors
|
| 366 |
+
narrow_count = slhc_count
|
| 367 |
+
wide_count = premium_count
|
| 368 |
else:
|
| 369 |
# MD metrics
|
| 370 |
narrow_count = sum(1 for w, _ in solution if w <= 6.0)
|
| 371 |
standard_count = sum(1 for w, _ in solution if 6.0 < w <= 8.0)
|
| 372 |
wide_count = sum(1 for w, _ in solution if w > 8.0)
|
| 373 |
slhc_pairs = 0 # Not applicable for MD
|
| 374 |
+
slhc_count = narrow_count
|
| 375 |
+
premium_count = wide_count
|
| 376 |
|
| 377 |
# Calculate actual total width and variance
|
| 378 |
total_width = sum(w for w, _ in solution)
|
|
|
|
| 393 |
f"🎯 DIVERSITY: {diversity_score:.0%} ({unique_widths} types)",
|
| 394 |
f"📏 GRID VARIANCE: {variance:+.2f}m",
|
| 395 |
"",
|
| 396 |
+
f"{'Narrow (≤6m)' if self.development_mode == 'medium_density' else 'SLHC (≤10.5m)'}: {narrow_count} lots",
|
| 397 |
f"{'Standard (6-8m)' if self.development_mode == 'medium_density' else 'Standard (11-14m)'}: {standard_count} lots",
|
| 398 |
+
f"{'Wide (>8m)' if self.development_mode == 'medium_density' else 'Premium (>14m)'}: {wide_count} lots",
|
| 399 |
"",
|
| 400 |
f"{'🚗 Access: ' + ('Rear Laneway' if self.md_load_type == 'rear' else 'Front Loaded') if self.development_mode == 'medium_density' else f'🚗 SLHC Pairs: {slhc_pairs}'}",
|
| 401 |
yield_text
|
|
|
|
| 497 |
best_solution = None
|
| 498 |
best_fitness = -float('inf')
|
| 499 |
|
| 500 |
+
# Ensure corners are at least as wide as smallest internal lot
|
| 501 |
if self.development_mode == 'medium_density':
|
| 502 |
min_internal = min(internal_widths) if internal_widths else 4.5
|
|
|
|
| 503 |
else:
|
|
|
|
| 504 |
min_internal = min(internal_widths) if internal_widths else 8.5
|
| 505 |
+
min_corner_width = max(base_corner_width - tolerance, min_internal)
|
| 506 |
|
| 507 |
# Try variations of corner widths within tolerance
|
| 508 |
variations = np.arange(min_corner_width,
|
|
|
|
| 520 |
internal_solution = self.find_exact_solution_with_diversity(internal_width, internal_widths)
|
| 521 |
|
| 522 |
if internal_solution:
|
| 523 |
+
# Verify no internal lot is wider than corners
|
| 524 |
+
max_internal = max(internal_solution) if internal_solution else 0
|
| 525 |
+
if max_internal > min(corner1, corner2):
|
| 526 |
+
continue
|
|
|
|
| 527 |
|
| 528 |
# Build complete solution
|
| 529 |
solution = [(round(corner1, 1), 'corner')]
|
|
|
|
| 558 |
|
| 559 |
# Strategy 2: Try flexible corners if enabled
|
| 560 |
if allow_custom_corners and standard_internal:
|
| 561 |
+
# Use appropriate corner bases for each mode
|
| 562 |
if self.development_mode == 'medium_density':
|
| 563 |
+
# For MD, use the largest available widths as corner bases
|
| 564 |
+
corner_bases = sorted(enabled_widths, reverse=True)[:4]
|
| 565 |
else:
|
| 566 |
# For conventional, use traditional corner widths
|
| 567 |
corner_bases = [11.0, 13.3, 14.8, 16.8, 14.0, 16.0]
|
|
|
|
| 584 |
|
| 585 |
# Separate widths by size
|
| 586 |
all_widths = sorted(enabled_widths)
|
| 587 |
+
min_internal_width = min(all_widths) if all_widths else 4.5
|
| 588 |
|
| 589 |
+
# Corner lots must be at least as wide as smallest internal lot
|
| 590 |
+
corner_options = [w for w in enabled_widths if w >= max(11.0 if self.development_mode == 'conventional' else min_internal_width, min_internal_width)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
|
| 592 |
best_solution = None
|
| 593 |
best_fitness = -float('inf')
|
|
|
|
| 609 |
)
|
| 610 |
|
| 611 |
for internal_widths in internal_solutions:
|
| 612 |
+
# Verify no internal lot is wider than corners
|
| 613 |
+
max_internal = max(internal_widths) if internal_widths else 0
|
| 614 |
+
if max_internal > min(corner1, corner2):
|
| 615 |
+
continue # Skip if internal lots are wider than corners
|
|
|
|
| 616 |
|
| 617 |
# Build complete solution
|
| 618 |
solution = [(corner1, 'corner')]
|
|
|
|
| 627 |
best_fitness = fitness
|
| 628 |
best_solution = optimized
|
| 629 |
|
| 630 |
+
# If no good solution, try without strict corner rules but maintain size hierarchy
|
| 631 |
if not best_solution:
|
| 632 |
all_solutions = []
|
| 633 |
self.find_all_combinations_recursive(stage_width, sorted(enabled_widths),
|
| 634 |
[], all_solutions, 20)
|
| 635 |
|
| 636 |
for widths in all_solutions[:50]:
|
| 637 |
+
# Ensure corners are among the largest lots
|
| 638 |
+
sorted_widths = sorted(widths)
|
| 639 |
+
if len(sorted_widths) >= 2:
|
| 640 |
+
# Put two largest widths at corners
|
| 641 |
+
solution = [(sorted_widths[-1], 'corner')] # Largest
|
| 642 |
+
solution.extend([(w, 'standard') for w in sorted_widths[:-2]])
|
| 643 |
+
solution.append((sorted_widths[-2], 'corner')) # Second largest
|
| 644 |
else:
|
| 645 |
+
solution = [(w, 'standard') for w in widths]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
|
| 647 |
optimized = self.optimize_lot_grouping(solution)
|
| 648 |
fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
|
|
|
|
| 939 |
fitness -= max_repetition * 500 # Penalty for too many of same width
|
| 940 |
fitness += diversity_ratio * 3000 # Bonus for good diversity ratio
|
| 941 |
|
| 942 |
+
# Corner evaluation - apply to both conventional and MD
|
| 943 |
+
if len(solution) >= 2:
|
| 944 |
+
first_width = solution[0][0]
|
| 945 |
+
last_width = solution[-1][0]
|
| 946 |
+
|
| 947 |
+
# Get max internal width
|
| 948 |
+
internal_widths = [w for w, t in solution[1:-1]]
|
| 949 |
+
max_internal = max(internal_widths) if internal_widths else 0
|
| 950 |
+
|
| 951 |
+
# Penalty if corners are not wider than internals
|
| 952 |
+
if first_width <= max_internal:
|
| 953 |
+
fitness -= 2000
|
| 954 |
+
if last_width <= max_internal:
|
| 955 |
+
fitness -= 2000
|
| 956 |
+
|
| 957 |
+
# Bonus for good corners (wider than internals)
|
| 958 |
+
if first_width > max_internal:
|
| 959 |
+
fitness += 1000
|
| 960 |
+
if last_width > max_internal:
|
| 961 |
+
fitness += 1000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 962 |
|
| 963 |
+
# Balance bonus
|
| 964 |
+
corner_diff = abs(first_width - last_width)
|
| 965 |
+
if corner_diff < 0.1:
|
| 966 |
+
fitness += 1500 # Perfect match
|
| 967 |
+
elif corner_diff <= 1.0:
|
| 968 |
+
fitness += 1000 # Very good
|
| 969 |
+
elif corner_diff <= 2.0:
|
| 970 |
+
fitness += 500 # Good
|
| 971 |
+
else:
|
| 972 |
+
fitness -= 500 # Poor balance
|
| 973 |
+
|
| 974 |
+
# Mode-specific bonuses
|
| 975 |
+
if self.development_mode == 'conventional':
|
| 976 |
# SLHC grouping bonus
|
| 977 |
for i in range(len(solution) - 1):
|
| 978 |
if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
|