File size: 3,446 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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""
Coverage for PathfindingProblem's construction-time feasibility guards: a
robot's start/goal that is out of bounds, on an obstacle, or an unknown
graph node, or a start_time that leaves no time to plan within an explicit
horizon, must be rejected at construction time (InfeasibleProblemError),
not discovered later as a cryptic var_limit crash, a silently-unsatisfiable
QUBO, or a failed benchmark run.
"""

import pytest

from quantum.map import Grid, Graph
from quantum.robotConfiguration import RobotConfig
from quantum.pathFormulation import PathfindingProblem, InfeasibleProblemError


def _grid():
    return Grid(3, 3, obstacles=[(1, 1)])


def test_valid_start_and_goal_construct_without_error():
    robot = RobotConfig("a", start=(0, 0), goal=(2, 2))
    problem = PathfindingProblem(robot, grid=_grid())
    assert problem.robots["a"].start == (0, 0)


def test_start_on_obstacle_raises():
    robot = RobotConfig("a", start=(1, 1), goal=(2, 2))
    with pytest.raises(InfeasibleProblemError, match="obstacle"):
        PathfindingProblem(robot, grid=_grid())


def test_goal_on_obstacle_raises():
    robot = RobotConfig("a", start=(0, 0), goal=(1, 1))
    with pytest.raises(InfeasibleProblemError, match="obstacle"):
        PathfindingProblem(robot, grid=_grid())


@pytest.mark.parametrize("start", [(-1, 0), (3, 0), (0, -1), (0, 3)])
def test_out_of_bounds_start_raises(start):
    robot = RobotConfig("a", start=start, goal=(2, 2))
    with pytest.raises(InfeasibleProblemError, match="out of bounds"):
        PathfindingProblem(robot, grid=_grid())


def test_out_of_bounds_goal_raises():
    robot = RobotConfig("a", start=(0, 0), goal=(5, 5))
    with pytest.raises(InfeasibleProblemError, match="out of bounds"):
        PathfindingProblem(robot, grid=_grid())


def test_one_bad_robot_among_several_still_raises():
    robots = [
        RobotConfig("good", start=(0, 0), goal=(0, 2)),
        RobotConfig("bad", start=(1, 1), goal=(2, 0)),
    ]
    with pytest.raises(InfeasibleProblemError, match="'bad'"):
        PathfindingProblem(robots, grid=_grid())


def _graph():
    return Graph(nodes=[(0, 0), (0, 1), (1, 1)], edges=[(0, 1), (1, 2)])


def test_valid_graph_node_ids_construct_without_error():
    robot = RobotConfig("a", start=0, goal=2)
    problem = PathfindingProblem(robot, graph=_graph())
    assert problem.robots["a"].goal == 2


@pytest.mark.parametrize("goal", [-1, 3, 999])
def test_out_of_range_graph_node_id_raises(goal):
    robot = RobotConfig("a", start=0, goal=goal)
    with pytest.raises(InfeasibleProblemError, match="does not exist"):
        PathfindingProblem(robot, graph=_graph())


def test_start_time_beyond_horizon_raises():
    robot = RobotConfig("a", start=(0, 0), goal=(2, 2), start_time=5)
    with pytest.raises(InfeasibleProblemError, match="leaves no time"):
        PathfindingProblem(robot, grid=_grid(), T=5)


def test_start_time_at_horizon_raises():
    """start_time == T leaves a zero-length window -- also infeasible, not
    just start_time > T."""
    robot = RobotConfig("a", start=(0, 0), goal=(2, 2), start_time=4)
    with pytest.raises(InfeasibleProblemError, match="leaves no time"):
        PathfindingProblem(robot, grid=_grid(), T=4)


def test_start_time_within_horizon_is_fine():
    robot = RobotConfig("a", start=(0, 0), goal=(2, 2), start_time=2)
    problem = PathfindingProblem(robot, grid=_grid(), T=6)
    assert problem.robots["a"].T == 4