|
| 1 | +import pytest |
| 2 | + |
| 3 | +from yaramo.model import ( |
| 4 | + DbrefGeoNode, |
| 5 | + Edge, |
| 6 | + Node, |
| 7 | + Route, |
| 8 | + Signal, |
| 9 | + SignalDirection, |
| 10 | + SignalFunction, |
| 11 | + SignalKind, |
| 12 | + Topology, |
| 13 | + Wgs84GeoNode, |
| 14 | +) |
| 15 | +from yaramo.operations import Compare, CompareMode, CompareResult |
| 16 | + |
| 17 | + |
| 18 | +def test_identical_topologies(): |
| 19 | + topology = Topology() |
| 20 | + node_1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 21 | + node_2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 22 | + node_3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 23 | + node_4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 24 | + node_5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 25 | + node_6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 26 | + edge_1 = Edge(node_1, node_3) |
| 27 | + edge_2 = Edge(node_2, node_3) |
| 28 | + edge_3 = Edge(node_4, node_3) |
| 29 | + edge_4 = Edge(node_4, node_5) |
| 30 | + edge_5 = Edge(node_4, node_6) |
| 31 | + signal_1 = Signal( |
| 32 | + edge_3, 5, SignalDirection.IN, SignalFunction.Block_Signal, SignalKind.Hauptsignal |
| 33 | + ) |
| 34 | + edge_3.signals.append(signal_1) |
| 35 | + topology.add_nodes([node_1, node_2, node_3, node_4, node_5, node_6]) |
| 36 | + topology.add_edges([edge_1, edge_2, edge_3, edge_4, edge_5]) |
| 37 | + topology.add_signals([signal_1]) |
| 38 | + topology.update_edge_lengths() |
| 39 | + |
| 40 | + compare_modes = [CompareMode.EXACT, CompareMode.ISOMORPHIC] |
| 41 | + |
| 42 | + for compare_mode in compare_modes: |
| 43 | + result = Compare.compare( |
| 44 | + topology, topology, compare_mode, given_node_matching={node_1: node_1} |
| 45 | + ) |
| 46 | + assert result.node_distance == 0.0 |
| 47 | + assert result.edge_length_difference == 0.0 |
| 48 | + assert result.signal_distance == 0.0 |
| 49 | + |
| 50 | + |
| 51 | +def test_identical_topologies_but_ids(): |
| 52 | + topology_a = Topology() |
| 53 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 54 | + node_a2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 55 | + node_a3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 56 | + node_a4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 57 | + node_a5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 58 | + node_a6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 59 | + edge_a1 = Edge(node_a1, node_a3) |
| 60 | + edge_a2 = Edge(node_a2, node_a3) |
| 61 | + edge_a3 = Edge(node_a4, node_a3) |
| 62 | + edge_a4 = Edge(node_a4, node_a5) |
| 63 | + edge_a5 = Edge(node_a4, node_a6) |
| 64 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4, node_a5, node_a6]) |
| 65 | + topology_a.add_edges([edge_a1, edge_a2, edge_a3, edge_a4, edge_a5]) |
| 66 | + topology_a.update_edge_lengths() |
| 67 | + |
| 68 | + topology_b = Topology() |
| 69 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 70 | + node_b2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 71 | + node_b3 = Node(geo_node=DbrefGeoNode(12, 0)) # x differs from Node A3 |
| 72 | + node_b4 = Node(geo_node=DbrefGeoNode(20, 3)) # y differs from Node A4 |
| 73 | + node_b5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 74 | + node_b6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 75 | + edge_b1 = Edge(node_b1, node_b3) |
| 76 | + edge_b2 = Edge(node_b2, node_b3) |
| 77 | + edge_b3 = Edge(node_b4, node_b3) |
| 78 | + edge_b4 = Edge(node_b4, node_b5) |
| 79 | + edge_b5 = Edge(node_b4, node_b6) |
| 80 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4, node_b5, node_b6]) |
| 81 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3, edge_b4, edge_b5]) |
| 82 | + topology_b.update_edge_lengths() |
| 83 | + |
| 84 | + compare_modes = [CompareMode.ISOMORPHIC] |
| 85 | + |
| 86 | + for compare_mode in compare_modes: |
| 87 | + result = Compare.compare( |
| 88 | + topology_a, topology_b, compare_mode, given_node_matching={node_a1: node_b1} |
| 89 | + ) |
| 90 | + assert result.node_distance == 5.0 |
| 91 | + assert node_a1 in result.node_matching.element_matching |
| 92 | + assert result.node_matching.element_matching[node_a1] == node_b1 |
| 93 | + assert node_a3 in result.node_matching.element_matching |
| 94 | + assert result.node_matching.element_matching[node_a3] == node_b3 |
| 95 | + assert edge_a5 in result.edge_matching.element_matching |
| 96 | + assert result.edge_matching.element_matching[edge_a5] == edge_b5 |
| 97 | + |
| 98 | + |
| 99 | +def test_edge_diff_and_signal_distance(): |
| 100 | + topology_a = Topology() |
| 101 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 102 | + node_a2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 103 | + node_a3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 104 | + node_a4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 105 | + node_a5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 106 | + node_a6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 107 | + edge_a1 = Edge(node_a1, node_a3) |
| 108 | + edge_a2 = Edge(node_a2, node_a3) |
| 109 | + edge_a3 = Edge(node_a3, node_a4) |
| 110 | + edge_a4 = Edge(node_a4, node_a5) |
| 111 | + edge_a5 = Edge(node_a4, node_a6) |
| 112 | + signal_a1 = Signal( |
| 113 | + edge_a3, |
| 114 | + 2, |
| 115 | + SignalDirection.IN, |
| 116 | + SignalFunction.Block_Signal, |
| 117 | + SignalKind.Hauptsignal, |
| 118 | + name="a1", |
| 119 | + ) |
| 120 | + edge_a3.signals.append(signal_a1) |
| 121 | + signal_a2 = Signal( |
| 122 | + edge_a3, |
| 123 | + 7, |
| 124 | + SignalDirection.IN, |
| 125 | + SignalFunction.Block_Signal, |
| 126 | + SignalKind.Hauptsignal, |
| 127 | + name="a2", |
| 128 | + ) |
| 129 | + edge_a3.signals.append(signal_a2) |
| 130 | + signal_a3 = Signal( |
| 131 | + edge_a3, |
| 132 | + 5, |
| 133 | + SignalDirection.GEGEN, |
| 134 | + SignalFunction.Block_Signal, |
| 135 | + SignalKind.Hauptsignal, |
| 136 | + name="a3", |
| 137 | + ) |
| 138 | + edge_a3.signals.append(signal_a3) |
| 139 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4, node_a5, node_a6]) |
| 140 | + topology_a.add_edges([edge_a1, edge_a2, edge_a3, edge_a4, edge_a5]) |
| 141 | + topology_a.add_signals([signal_a1, signal_a2, signal_a3]) |
| 142 | + topology_a.update_edge_lengths() |
| 143 | + |
| 144 | + topology_b = Topology() |
| 145 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 146 | + node_b2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 147 | + node_b3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 148 | + node_b4 = Node(geo_node=DbrefGeoNode(22, 0)) |
| 149 | + node_b5 = Node(geo_node=DbrefGeoNode(32, 0)) |
| 150 | + node_b6 = Node(geo_node=DbrefGeoNode(32, 10)) |
| 151 | + edge_b1 = Edge(node_b1, node_b3) |
| 152 | + edge_b2 = Edge(node_b2, node_b3) |
| 153 | + edge_b3 = Edge(node_b4, node_b3) # Edge is reversed compared to edge_a3 |
| 154 | + edge_b4 = Edge(node_b4, node_b5) |
| 155 | + edge_b5 = Edge(node_b4, node_b6) |
| 156 | + signal_b1 = Signal( |
| 157 | + edge_b3, |
| 158 | + 8, |
| 159 | + SignalDirection.GEGEN, |
| 160 | + SignalFunction.Block_Signal, |
| 161 | + SignalKind.Hauptsignal, |
| 162 | + name="b1", |
| 163 | + ) |
| 164 | + edge_b3.signals.append(signal_b1) |
| 165 | + signal_b2 = Signal( |
| 166 | + edge_b3, |
| 167 | + 4, |
| 168 | + SignalDirection.GEGEN, |
| 169 | + SignalFunction.Block_Signal, |
| 170 | + SignalKind.Hauptsignal, |
| 171 | + name="b2", |
| 172 | + ) |
| 173 | + edge_b3.signals.append(signal_b2) |
| 174 | + signal_b3 = Signal( |
| 175 | + edge_b3, |
| 176 | + 6, |
| 177 | + SignalDirection.IN, |
| 178 | + SignalFunction.Block_Signal, |
| 179 | + SignalKind.Hauptsignal, |
| 180 | + name="b3", |
| 181 | + ) |
| 182 | + edge_b3.signals.append(signal_b3) |
| 183 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4, node_b5, node_b6]) |
| 184 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3, edge_b4, edge_b5]) |
| 185 | + topology_b.add_signals([signal_b1, signal_b2, signal_b3]) |
| 186 | + topology_b.update_edge_lengths() |
| 187 | + |
| 188 | + compare_modes = [CompareMode.ISOMORPHIC] |
| 189 | + |
| 190 | + for compare_mode in compare_modes: |
| 191 | + result = Compare.compare( |
| 192 | + topology_a, topology_b, compare_mode, given_node_matching={node_a1: node_b1} |
| 193 | + ) |
| 194 | + assert result.node_distance == 6.0 |
| 195 | + assert result.edge_length_difference == 2.0 |
| 196 | + assert edge_a3 in result.edge_matching.element_matching |
| 197 | + assert edge_b3 == result.edge_matching.element_matching[edge_a3] |
| 198 | + assert result.signal_distance == 4.0 |
| 199 | + assert signal_a1 in result.signal_matching.element_matching |
| 200 | + assert signal_b1 == result.signal_matching.element_matching[signal_a1] |
| 201 | + assert signal_a2 in result.signal_matching.element_matching |
| 202 | + assert signal_b2 == result.signal_matching.element_matching[signal_a2] |
| 203 | + assert signal_a3 in result.signal_matching.element_matching |
| 204 | + assert signal_b3 == result.signal_matching.element_matching[signal_a3] |
| 205 | + |
| 206 | + |
| 207 | +def test_exact_matching_with_overlapping_graph(): |
| 208 | + topology_a = Topology() |
| 209 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 210 | + node_a2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 211 | + node_a3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 212 | + node_a4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 213 | + node_a5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 214 | + node_a6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 215 | + edge_a1 = Edge(node_a1, node_a3) |
| 216 | + edge_a2 = Edge(node_a2, node_a3) |
| 217 | + edge_a3 = Edge(node_a3, node_a4) |
| 218 | + edge_a4 = Edge(node_a4, node_a5) |
| 219 | + edge_a5 = Edge(node_a4, node_a6) |
| 220 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4, node_a5, node_a6]) |
| 221 | + topology_a.add_edges([edge_a1, edge_a2, edge_a3, edge_a4, edge_a5]) |
| 222 | + topology_a.update_edge_lengths() |
| 223 | + |
| 224 | + topology_b = Topology() |
| 225 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0), uuid=node_a1.uuid) |
| 226 | + node_b2 = Node(geo_node=DbrefGeoNode(0, 10), uuid=node_a2.uuid) |
| 227 | + node_b3 = Node(geo_node=DbrefGeoNode(12, 0), uuid=node_a3.uuid) # x differs to Node A3 |
| 228 | + node_b4 = Node(geo_node=DbrefGeoNode(20, 1), uuid=node_a4.uuid) # y differs to Node A3 |
| 229 | + node_b5 = Node(geo_node=DbrefGeoNode(-10, 0)) |
| 230 | + node_b6 = Node(geo_node=DbrefGeoNode(-10, 10)) |
| 231 | + edge_b1 = Edge(node_b1, node_b3, uuid=edge_a1.uuid) |
| 232 | + edge_b2 = Edge(node_b2, node_b3, uuid=edge_a2.uuid) |
| 233 | + edge_b3 = Edge(node_b3, node_b4, uuid=edge_a3.uuid) |
| 234 | + edge_b4 = Edge(node_b5, node_b1) |
| 235 | + edge_b5 = Edge(node_b6, node_b1) |
| 236 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4, node_b5, node_b6]) |
| 237 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3, edge_b4, edge_b5]) |
| 238 | + topology_b.update_edge_lengths() |
| 239 | + |
| 240 | + result = Compare.compare(topology_a, topology_b, CompareMode.EXACT) |
| 241 | + |
| 242 | + assert result.node_distance == 3.0 |
| 243 | + assert node_a1 in result.node_matching.element_matching |
| 244 | + assert node_b1 == result.node_matching.element_matching[node_a1] |
| 245 | + assert node_a2 in result.node_matching.element_matching |
| 246 | + assert node_b2 == result.node_matching.element_matching[node_a2] |
| 247 | + assert node_a5 in result.node_matching.not_found_in_b |
| 248 | + assert node_a6 in result.node_matching.not_found_in_b |
| 249 | + assert node_b5 in result.node_matching.not_found_in_a |
| 250 | + assert node_b6 in result.node_matching.not_found_in_a |
| 251 | + |
| 252 | + |
| 253 | +def test_non_isomorphic_topologies_different_node_and_edge_count(): |
| 254 | + topology_a = Topology() |
| 255 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 256 | + node_a2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 257 | + node_a3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 258 | + node_a4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 259 | + node_a5 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 260 | + node_a6 = Node(geo_node=DbrefGeoNode(30, 10)) |
| 261 | + edge_a1 = Edge(node_a1, node_a3) |
| 262 | + edge_a2 = Edge(node_a2, node_a3) |
| 263 | + edge_a3 = Edge(node_a4, node_a3) |
| 264 | + edge_a4 = Edge(node_a4, node_a5) |
| 265 | + edge_a5 = Edge(node_a4, node_a6) |
| 266 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4, node_a5, node_a6]) |
| 267 | + topology_a.add_edges([edge_a1, edge_a2, edge_a3, edge_a4, edge_a5]) |
| 268 | + |
| 269 | + topology_b = Topology() |
| 270 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 271 | + node_b2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 272 | + node_b3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 273 | + node_b4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 274 | + edge_b1 = Edge(node_b1, node_b3) |
| 275 | + edge_b2 = Edge(node_b2, node_b3) |
| 276 | + edge_b3 = Edge(node_b4, node_b3) |
| 277 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4]) |
| 278 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3]) |
| 279 | + |
| 280 | + with pytest.raises(ValueError): |
| 281 | + result = Compare.compare( |
| 282 | + topology_a, topology_b, CompareMode.ISOMORPHIC, given_node_matching={node_a1: node_b1} |
| 283 | + ) |
| 284 | + |
| 285 | + |
| 286 | +def test_non_isomorphic_topologies_same_node_and_edge_count(): |
| 287 | + topology_a = Topology() |
| 288 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 289 | + node_a2 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 290 | + node_a3 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 291 | + node_a4 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 292 | + node_a5 = Node(geo_node=DbrefGeoNode(40, 10)) |
| 293 | + node_a6 = Node(geo_node=DbrefGeoNode(40, 0)) |
| 294 | + edge_a1 = Edge(node_a1, node_a2) |
| 295 | + edge_a2 = Edge(node_a2, node_a3) |
| 296 | + edge_a2b = Edge(node_a2, node_a3) |
| 297 | + edge_a2b.intermediate_geo_nodes.extend([DbrefGeoNode(12, 5), DbrefGeoNode(18, 5)]) |
| 298 | + edge_a3 = Edge(node_a3, node_a4) |
| 299 | + edge_a4 = Edge(node_a4, node_a5) |
| 300 | + edge_a5 = Edge(node_a4, node_a6) |
| 301 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4, node_a5, node_a6]) |
| 302 | + topology_a.add_edges([edge_a1, edge_a2, edge_a2b, edge_a3, edge_a4, edge_a5]) |
| 303 | + |
| 304 | + topology_b = Topology() |
| 305 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 306 | + node_b2 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 307 | + node_b3 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 308 | + node_b4 = Node(geo_node=DbrefGeoNode(30, 0)) |
| 309 | + node_b5 = Node(geo_node=DbrefGeoNode(13, 5)) |
| 310 | + node_b6 = Node(geo_node=DbrefGeoNode(23, 5)) |
| 311 | + edge_b1 = Edge(node_b1, node_b2) |
| 312 | + edge_b2 = Edge(node_b2, node_b3) |
| 313 | + edge_b3 = Edge(node_b3, node_b4) |
| 314 | + edge_b4 = Edge(node_b2, node_b5) |
| 315 | + edge_b5 = Edge(node_b3, node_b5) |
| 316 | + edge_b6 = Edge(node_b5, node_b6) |
| 317 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4, node_b5, node_b6]) |
| 318 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3, edge_b4, edge_b5, edge_b6]) |
| 319 | + |
| 320 | + with pytest.raises(ValueError): |
| 321 | + result = Compare.compare( |
| 322 | + topology_a, topology_b, CompareMode.ISOMORPHIC, given_node_matching={node_a1: node_b1} |
| 323 | + ) |
| 324 | + |
| 325 | + |
| 326 | +def test_isomorphic_topologies_without_given_node_matching(): |
| 327 | + topology_a = Topology() |
| 328 | + node_a1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 329 | + node_a2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 330 | + node_a3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 331 | + node_a4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 332 | + edge_a1 = Edge(node_a1, node_a3) |
| 333 | + edge_a2 = Edge(node_a2, node_a3) |
| 334 | + edge_a3 = Edge(node_a4, node_a3) |
| 335 | + topology_a.add_nodes([node_a1, node_a2, node_a3, node_a4]) |
| 336 | + topology_a.add_edges([edge_a1, edge_a2, edge_a3]) |
| 337 | + |
| 338 | + topology_b = Topology() |
| 339 | + node_b1 = Node(geo_node=DbrefGeoNode(0, 0)) |
| 340 | + node_b2 = Node(geo_node=DbrefGeoNode(0, 10)) |
| 341 | + node_b3 = Node(geo_node=DbrefGeoNode(10, 0)) |
| 342 | + node_b4 = Node(geo_node=DbrefGeoNode(20, 0)) |
| 343 | + edge_b1 = Edge(node_b1, node_b3) |
| 344 | + edge_b2 = Edge(node_b2, node_b3) |
| 345 | + edge_b3 = Edge(node_b3, node_b4) |
| 346 | + topology_b.add_nodes([node_b1, node_b2, node_b3, node_b4]) |
| 347 | + topology_b.add_edges([edge_b1, edge_b2, edge_b3]) |
| 348 | + |
| 349 | + with pytest.raises(ValueError): |
| 350 | + result = Compare.compare(topology_a, topology_b, CompareMode.ISOMORPHIC) |
0 commit comments