--orders: route several connection orders in parallel, keep the best
The default order is always one of the candidates, so the result is never worse than a plain run; ties go to the earlier candidate.
This commit is contained in:
+52
-21
@@ -19,6 +19,7 @@ import argparse
|
||||
import sys
|
||||
|
||||
from freeroute.dsn.reader import parse_dsn
|
||||
from freeroute.route.orders import best_of_orders
|
||||
from freeroute.route.pipeline import (
|
||||
build_exact_routing_result,
|
||||
build_rooms_routing_result,
|
||||
@@ -29,8 +30,8 @@ from freeroute.ses import write_ses
|
||||
#: which optional flags each engine actually implements. Anything not listed for
|
||||
#: an engine is rejected by :func:`_validate` rather than quietly ignored.
|
||||
ENGINE_OPTIONS: dict[str, frozenset[str]] = {
|
||||
"grid": frozenset({"rip_up", "max_passes", "seed"}),
|
||||
"exact": frozenset({"shove", "diagonal", "rip_up", "max_passes", "seed"}),
|
||||
"grid": frozenset({"rip_up", "max_passes", "seed", "orders"}),
|
||||
"exact": frozenset({"shove", "diagonal", "rip_up", "max_passes", "seed", "orders"}),
|
||||
"room": frozenset({"shove", "pack"}),
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ _FLAG_NAMES = {
|
||||
"rip_up": "--no-rip-up",
|
||||
"max_passes": "-mp/--max-passes",
|
||||
"seed": "--seed",
|
||||
"orders": "--orders",
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +143,19 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
help="grid and exact engines: shuffle the order connections are routed in; "
|
||||
"the same seed gives the same result (default: fewer-pin nets first)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--orders",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="grid and exact engines: route N connection orders in parallel and keep the "
|
||||
"one leaving the fewest connections unrouted (the default order is one of them)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--jobs",
|
||||
type=int,
|
||||
metavar="J",
|
||||
help="processes for --orders (default: one per order, up to the CPU count)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
@@ -161,6 +176,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
("rip_up", args.no_rip_up),
|
||||
("max_passes", args.max_passes is not None),
|
||||
("seed", args.seed is not None),
|
||||
("orders", args.orders is not None),
|
||||
)
|
||||
if asked
|
||||
]
|
||||
@@ -182,31 +198,46 @@ def main(argv: list[str] | None = None) -> int:
|
||||
rip_up = not args.no_rip_up
|
||||
max_passes = args.max_passes if args.max_passes is not None else 10
|
||||
|
||||
if args.orders is not None and args.orders < 1:
|
||||
parser.error("--orders must be at least 1")
|
||||
if args.engine == "exact":
|
||||
result = build_exact_routing_result(
|
||||
dsn,
|
||||
layers=layers,
|
||||
shove=args.shove,
|
||||
diagonal=args.diagonal,
|
||||
rip_up=rip_up,
|
||||
max_passes=max_passes,
|
||||
edge_clearance_mm=args.edge_clearance,
|
||||
resolution=2 if args.grid_resolution is None else args.grid_resolution,
|
||||
heuristic_weight=args.heuristic_weight,
|
||||
seed=args.seed,
|
||||
)
|
||||
elif args.engine == "room":
|
||||
options = {
|
||||
"layers": layers,
|
||||
"shove": args.shove,
|
||||
"diagonal": args.diagonal,
|
||||
"rip_up": rip_up,
|
||||
"max_passes": max_passes,
|
||||
"edge_clearance_mm": args.edge_clearance,
|
||||
"resolution": 2 if args.grid_resolution is None else args.grid_resolution,
|
||||
"heuristic_weight": args.heuristic_weight,
|
||||
}
|
||||
elif args.engine == "grid":
|
||||
options = {
|
||||
"layers": layers,
|
||||
"rip_up": rip_up,
|
||||
"max_passes": max_passes,
|
||||
"edge_clearance_mm": args.edge_clearance,
|
||||
"resolution": 1 if args.grid_resolution is None else args.grid_resolution,
|
||||
}
|
||||
if args.engine == "room":
|
||||
result = build_rooms_routing_result(
|
||||
dsn, layers=layers, shove=args.shove, pack=args.pack,
|
||||
edge_clearance_mm=args.edge_clearance,
|
||||
)
|
||||
else:
|
||||
result = build_routing_result(
|
||||
dsn, layers=layers, rip_up=rip_up, max_passes=max_passes,
|
||||
edge_clearance_mm=args.edge_clearance,
|
||||
resolution=1 if args.grid_resolution is None else args.grid_resolution,
|
||||
seed=args.seed,
|
||||
elif args.orders and args.orders > 1:
|
||||
result, kept, tried = best_of_orders(
|
||||
dsn_text, args.engine, options, orders=args.orders, seed=args.seed, jobs=args.jobs
|
||||
)
|
||||
counts = ", ".join(f"{'default' if s is None else s}: {m}" for s, (m, _z) in tried)
|
||||
print(
|
||||
f"freeroute: tried {len(tried)} orders, kept "
|
||||
f"{'the default order' if kept is None else f'--seed {kept}'} (unrouted {counts})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
elif args.engine == "exact":
|
||||
result = build_exact_routing_result(dsn, seed=args.seed, **options)
|
||||
else:
|
||||
result = build_routing_result(dsn, seed=args.seed, **options)
|
||||
|
||||
ses_text = write_ses(dsn, result)
|
||||
missing = [u for u in result.unrouted if not u[3]]
|
||||
|
||||
Reference in New Issue
Block a user