File size: 2,982 Bytes
a115eaf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""
Regression pin for the redundant-full-grid-build bug: /v1/plan used to call
builder.build() with no BFS-reduced window active (_active_cells still None)
right before solver.solve(), which immediately rebuilds it properly anyway —
free on tiny synthetic maps, but apply_one_hot()'s O(cells²) constraint loop
made it dominate wall time on real-sized maps (see builder/base_qubo.py's
_warn_if_unrestricted_build(), added alongside the fix).

This needs a map above _warn_if_unrestricted_build()'s 200-cell threshold —
no_obs50x50 (2,500 cells) — even though the robot's own path only needs a
tiny window: the bug was building the *whole grid* regardless of how short
the requested path was, so a small map would never actually exercise it.
The fast classical solver keeps this quick with no GPU/real hardware needed.
"""

from fastapi.testclient import TestClient

from api import app


def test_plan_does_not_trigger_unrestricted_build(capsys):
    with TestClient(app) as client:
        response = client.post(
            "/v1/plan",
            json={
                "map_id": "no_obs50x50",
                "solver": "dwave.fast",
                "format": "grid",
                "robots": [{"start": [2, 0], "goal": [0, 2]}],
                "penalty_set": "crash",
            },
        )

    assert response.status_code == 200, response.text

    # The actual regression pin: this warning only fires when build() ran
    # over the full grid instead of a BFS-reduced window — i.e. a
    # builder.build() call snuck back in before solver.solve() again.
    captured = capsys.readouterr()
    assert "unrestricted QUBO" not in captured.out


def test_cbs_profile_plans_on_grid_and_graph():
    """classic.cbs must route to the CBS builders in both /v1/plan formats —
    the endpoints used to branch ilp-vs-QUBO only, so a CBS profile fell
    through to a QUBOBuilder the CBSSolver can't consume."""
    with TestClient(app) as client:
        assert "classic.cbs" in client.get("/solvers").json()

        grid = client.post(
            "/v1/plan",
            json={
                "map_id": "obs5x5_hard",
                "solver": "classic.cbs",
                "format": "grid",
                "robots": [
                    {"id": "r0", "start": [0, 0], "goal": [4, 4]},
                    {"id": "r1", "start": [4, 0], "goal": [0, 4]},
                ],
            },
        )
        assert grid.status_code == 200, grid.text
        body = grid.json()
        assert body["solver_used"] == "classic.cbs"
        assert len(body["paths"]) == 2
        assert body["cost"] == body["cost"]  # finite, not NaN/inf

        graph = client.post(
            "/v1/plan",
            json={
                "map_id": "obs5x5_hard",
                "solver": "classic.cbs",
                "format": "graph",
                "robots": [{"start": [0, 0], "goal": [4, 4]}],
            },
        )
        assert graph.status_code == 200, graph.text