--promote-failures: reroute with the connections that failed moved first

Each round routes every connection that has failed so far ahead of the
rest, which keep their order. The promoted set only grows, so two groups
cannot take turns failing; the best round is kept.
This commit is contained in:
2026-09-27 22:34:06 -06:00
parent 0f5dacbd88
commit 01ca108831
7 changed files with 105 additions and 4 deletions
+20 -3
View File
@@ -19,7 +19,7 @@ import argparse
import sys
from freeroute.dsn.reader import parse_dsn
from freeroute.route.orders import best_of_orders
from freeroute.route.orders import best_of_orders, promote_failures
from freeroute.route.pipeline import (
build_exact_routing_result,
build_rooms_routing_result,
@@ -30,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", "orders"}),
"exact": frozenset({"shove", "diagonal", "rip_up", "max_passes", "seed", "orders"}),
"grid": frozenset({"rip_up", "max_passes", "seed", "orders", "promote"}),
"exact": frozenset({"shove", "diagonal", "rip_up", "max_passes", "seed", "orders", "promote"}),
"room": frozenset({"shove", "pack"}),
}
@@ -44,6 +44,7 @@ _FLAG_NAMES = {
"max_passes": "-mp/--max-passes",
"seed": "--seed",
"orders": "--orders",
"promote": "--promote-failures",
}
@@ -150,6 +151,13 @@ def build_parser() -> argparse.ArgumentParser:
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(
"--promote-failures",
type=int,
metavar="R",
help="grid and exact engines: up to R rounds, each routing the connections that "
"failed so far first; keeps the best round",
)
parser.add_argument(
"--jobs",
type=int,
@@ -177,6 +185,7 @@ def main(argv: list[str] | None = None) -> int:
("max_passes", args.max_passes is not None),
("seed", args.seed is not None),
("orders", args.orders is not None),
("promote", args.promote_failures is not None),
)
if asked
]
@@ -224,6 +233,14 @@ def main(argv: list[str] | None = None) -> int:
dsn, layers=layers, shove=args.shove, pack=args.pack,
edge_clearance_mm=args.edge_clearance,
)
elif args.promote_failures and args.promote_failures > 1:
if args.orders:
parser.error("--orders and --promote-failures cannot be combined")
result, rounds = promote_failures(
dsn_text, args.engine, options, rounds=args.promote_failures, seed=args.seed
)
counts = ", ".join(str(m) for m, _z in rounds)
print(f"freeroute: {len(rounds)} rounds, unrouted {counts}; kept the best", file=sys.stderr)
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