Spaces:
Sleeping
Sleeping
Upload 107 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +2 -0
- Dockerfile +18 -0
- README.md +27 -5
- app.py +45 -0
- graph-theory/mcp_output/README_MCP.md +61 -0
- graph-theory/mcp_output/analysis.json +915 -0
- graph-theory/mcp_output/diff_report.md +61 -0
- graph-theory/mcp_output/mcp_plugin/__init__.py +0 -0
- graph-theory/mcp_output/mcp_plugin/adapter.py +102 -0
- graph-theory/mcp_output/mcp_plugin/main.py +13 -0
- graph-theory/mcp_output/mcp_plugin/mcp_service.py +26 -0
- graph-theory/mcp_output/requirements.txt +6 -0
- graph-theory/mcp_output/start_mcp.py +30 -0
- graph-theory/mcp_output/workflow_summary.json +196 -0
- graph-theory/source/.coveragerc +4 -0
- graph-theory/source/LICENSE +21 -0
- graph-theory/source/README.md +144 -0
- graph-theory/source/__init__.py +4 -0
- graph-theory/source/datasets/readme.md +3 -0
- graph-theory/source/examples/__init__.py +0 -0
- graph-theory/source/examples/basic graph theory.ipynb +941 -0
- graph-theory/source/examples/comparing graphs.ipynb +32 -0
- graph-theory/source/examples/generating and visualising graphs.ipynb +0 -0
- graph-theory/source/examples/graphs as finite state machines.ipynb +848 -0
- graph-theory/source/examples/graphs.py +766 -0
- graph-theory/source/examples/images/2500pxLondon_Underground_Overground.png +3 -0
- graph-theory/source/examples/images/3-3-cart-traffic-jam.png +0 -0
- graph-theory/source/examples/images/3-3-cart-traffic-jam_initial.png +0 -0
- graph-theory/source/examples/images/3-3-traffic-jam-road-map.png +0 -0
- graph-theory/source/examples/images/3-3-tree-of-states.png +0 -0
- graph-theory/source/examples/images/6nodes.png +0 -0
- graph-theory/source/examples/images/Torniqueterevolution.jpg +0 -0
- graph-theory/source/examples/images/cpm_w_artificial_dependency.png +0 -0
- graph-theory/source/examples/images/cpm_wo_artificial_dependency.png +0 -0
- graph-theory/source/examples/images/easy_sudoku.png +0 -0
- graph-theory/source/examples/images/easy_sudoku3.png +0 -0
- graph-theory/source/examples/images/movement_graph.png +0 -0
- graph-theory/source/examples/images/search_tree.png +0 -0
- graph-theory/source/examples/images/sudoku-wave-collapse-1.png +0 -0
- graph-theory/source/examples/images/sudoku_solver1.png +0 -0
- graph-theory/source/examples/images/tjs-map-loads.png +0 -0
- graph-theory/source/examples/images/tjs-map.png +0 -0
- graph-theory/source/examples/images/tjs_problem_w_distance_restrictions.png +0 -0
- graph-theory/source/examples/images/traffic_bi_directional.gif +3 -0
- graph-theory/source/examples/images/turnstile.png +0 -0
- graph-theory/source/examples/readme.md +89 -0
- graph-theory/source/examples/solving assignment problems.ipynb +36 -0
- graph-theory/source/examples/solving flow problems.ipynb +36 -0
- graph-theory/source/examples/solving search problems.ipynb +652 -0
- graph-theory/source/examples/solving transport problems.ipynb +36 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
graph-theory/source/examples/images/2500pxLondon_Underground_Overground.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
graph-theory/source/examples/images/traffic_bi_directional.gif filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10
|
| 2 |
+
|
| 3 |
+
RUN useradd -m -u 1000 user && python -m pip install --upgrade pip
|
| 4 |
+
USER user
|
| 5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 6 |
+
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 10 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 11 |
+
|
| 12 |
+
COPY --chown=user . /app
|
| 13 |
+
ENV MCP_TRANSPORT=http
|
| 14 |
+
ENV MCP_PORT=7860
|
| 15 |
+
|
| 16 |
+
EXPOSE 7860
|
| 17 |
+
|
| 18 |
+
CMD ["python", "graph-theory/mcp_output/start_mcp.py"]
|
README.md
CHANGED
|
@@ -1,10 +1,32 @@
|
|
| 1 |
---
|
| 2 |
-
title: Graph
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
|
|
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Graph-Theory MCP
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
+
sdk_version: "4.26.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Graph-Theory MCP Service
|
| 13 |
+
|
| 14 |
+
Auto-generated MCP service for graph-theory.
|
| 15 |
+
|
| 16 |
+
## Usage
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
https://None-graph-theory-mcp.hf.space/mcp
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
## Connect with Cursor
|
| 23 |
+
|
| 24 |
+
```json
|
| 25 |
+
{
|
| 26 |
+
"mcpServers": {
|
| 27 |
+
"graph-theory": {
|
| 28 |
+
"url": "https://None-graph-theory-mcp.hf.space/mcp"
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
```
|
app.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
mcp_plugin_path = os.path.join(os.path.dirname(__file__), "graph-theory", "mcp_output", "mcp_plugin")
|
| 6 |
+
sys.path.insert(0, mcp_plugin_path)
|
| 7 |
+
|
| 8 |
+
app = FastAPI(
|
| 9 |
+
title="Graph-Theory MCP Service",
|
| 10 |
+
description="Auto-generated MCP service for graph-theory",
|
| 11 |
+
version="1.0.0"
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
@app.get("/")
|
| 15 |
+
def root():
|
| 16 |
+
return {
|
| 17 |
+
"service": "Graph-Theory MCP Service",
|
| 18 |
+
"version": "1.0.0",
|
| 19 |
+
"status": "running",
|
| 20 |
+
"transport": os.environ.get("MCP_TRANSPORT", "http")
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
@app.get("/health")
|
| 24 |
+
def health_check():
|
| 25 |
+
return {"status": "healthy", "service": "graph-theory MCP"}
|
| 26 |
+
|
| 27 |
+
@app.get("/tools")
|
| 28 |
+
def list_tools():
|
| 29 |
+
try:
|
| 30 |
+
from mcp_service import create_app
|
| 31 |
+
mcp_app = create_app()
|
| 32 |
+
tools = []
|
| 33 |
+
for tool_name, tool_func in mcp_app.tools.items():
|
| 34 |
+
tools.append({
|
| 35 |
+
"name": tool_name,
|
| 36 |
+
"description": tool_func.__doc__ or "No description available"
|
| 37 |
+
})
|
| 38 |
+
return {"tools": tools}
|
| 39 |
+
except Exception as e:
|
| 40 |
+
return {"error": f"Failed to load tools: {str(e)}"}
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
import uvicorn
|
| 44 |
+
port = int(os.environ.get("PORT", 7860))
|
| 45 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
graph-theory/mcp_output/README_MCP.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Graph Theory MCP (Model Context Protocol) Service
|
| 2 |
+
|
| 3 |
+
## Project Introduction
|
| 4 |
+
|
| 5 |
+
The Graph Theory MCP service is a comprehensive library designed to facilitate graph-related computations and analyses. It provides a wide range of functionalities, including graph traversal, shortest path calculations, flow problems, and visualization tools. This service is ideal for developers working on projects that require complex graph operations and optimizations.
|
| 6 |
+
|
| 7 |
+
## Installation Method
|
| 8 |
+
|
| 9 |
+
To install the Graph Theory MCP service, ensure you have Python installed and then use the following pip command to install the necessary dependencies:
|
| 10 |
+
|
| 11 |
+
- Required: numpy, scipy
|
| 12 |
+
- Optional: matplotlib (for visualization)
|
| 13 |
+
|
| 14 |
+
```shell
|
| 15 |
+
pip install numpy scipy
|
| 16 |
+
pip install matplotlib # Optional for visualization
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
## Quick Start
|
| 20 |
+
|
| 21 |
+
Here's a quick example to get you started with the Graph Theory MCP service:
|
| 22 |
+
|
| 23 |
+
```python
|
| 24 |
+
from graph.adjacency_matrix import adjacency_matrix
|
| 25 |
+
from graph.shortest_path import shortest_path
|
| 26 |
+
|
| 27 |
+
# Example graph represented as an adjacency list
|
| 28 |
+
graph = {
|
| 29 |
+
'A': {'B': 1, 'C': 4},
|
| 30 |
+
'B': {'C': 2, 'D': 5},
|
| 31 |
+
'C': {'D': 1},
|
| 32 |
+
'D': {}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
# Convert to adjacency matrix
|
| 36 |
+
matrix = adjacency_matrix(graph)
|
| 37 |
+
|
| 38 |
+
# Find the shortest path from A to D
|
| 39 |
+
path = shortest_path(graph, 'A', 'D')
|
| 40 |
+
print("Shortest path from A to D:", path)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
## Available Tools and Endpoints List
|
| 44 |
+
|
| 45 |
+
- **Graph Construction and Manipulation**: Functions like `adjacency_matrix`, `all_pairs_shortest_paths`, and `all_simple_paths` help in constructing and analyzing graphs.
|
| 46 |
+
- **Path and Flow Analysis**: Use `shortest_path`, `maximum_flow`, and `minimum_cost_flow_using_successive_shortest_path` for pathfinding and flow optimization.
|
| 47 |
+
- **Graph Algorithms**: Implement algorithms such as `breadth_first_search`, `depth_first_search`, and `topological_sort`.
|
| 48 |
+
- **Specialized Problems**: Solve complex problems with `assignment_problem`, `traffic_scheduling_problem`, and `transshipment_problem`.
|
| 49 |
+
- **Visualization**: Visualize graphs using `plot_2d`, `plot_3d`, and `visualise`.
|
| 50 |
+
|
| 51 |
+
## Common Issues and Notes
|
| 52 |
+
|
| 53 |
+
- Ensure all required dependencies are installed to avoid import errors.
|
| 54 |
+
- For performance optimization, consider the complexity of the graph algorithms being used, especially for large graphs.
|
| 55 |
+
- Optional dependencies like `matplotlib` are necessary for visualization features.
|
| 56 |
+
|
| 57 |
+
## Reference Links or Documentation
|
| 58 |
+
|
| 59 |
+
For more detailed documentation and examples, visit the [Graph Theory GitHub Repository](https://github.com/root-11/graph-theory).
|
| 60 |
+
|
| 61 |
+
For further assistance, you can explore the code structure and documentation once the repository is indexed, which typically takes 2-10 minutes.
|
graph-theory/mcp_output/analysis.json
ADDED
|
@@ -0,0 +1,915 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"summary": {
|
| 3 |
+
"repository_url": "https://github.com/root-11/graph-theory",
|
| 4 |
+
"summary": "Imported via zip fallback, file count: 62",
|
| 5 |
+
"file_tree": {
|
| 6 |
+
".github/workflows/codecov.yml": {
|
| 7 |
+
"size": 758
|
| 8 |
+
},
|
| 9 |
+
".github/workflows/publish.yml": {
|
| 10 |
+
"size": 533
|
| 11 |
+
},
|
| 12 |
+
".github/workflows/python-test.yml": {
|
| 13 |
+
"size": 971
|
| 14 |
+
},
|
| 15 |
+
"README.md": {
|
| 16 |
+
"size": 21562
|
| 17 |
+
},
|
| 18 |
+
"datasets/readme.md": {
|
| 19 |
+
"size": 123
|
| 20 |
+
},
|
| 21 |
+
"examples/__init__.py": {
|
| 22 |
+
"size": 0
|
| 23 |
+
},
|
| 24 |
+
"examples/graphs.py": {
|
| 25 |
+
"size": 27277
|
| 26 |
+
},
|
| 27 |
+
"examples/readme.md": {
|
| 28 |
+
"size": 2079
|
| 29 |
+
},
|
| 30 |
+
"graph/__init__.py": {
|
| 31 |
+
"size": 61
|
| 32 |
+
},
|
| 33 |
+
"graph/adjacency_matrix.py": {
|
| 34 |
+
"size": 1109
|
| 35 |
+
},
|
| 36 |
+
"graph/all_pairs_shortest_path.py": {
|
| 37 |
+
"size": 1346
|
| 38 |
+
},
|
| 39 |
+
"graph/all_paths.py": {
|
| 40 |
+
"size": 2031
|
| 41 |
+
},
|
| 42 |
+
"graph/all_simple_paths.py": {
|
| 43 |
+
"size": 1026
|
| 44 |
+
},
|
| 45 |
+
"graph/assignment_problem.py": {
|
| 46 |
+
"size": 8343
|
| 47 |
+
},
|
| 48 |
+
"graph/base.py": {
|
| 49 |
+
"size": 17623
|
| 50 |
+
},
|
| 51 |
+
"graph/bfs.py": {
|
| 52 |
+
"size": 2255
|
| 53 |
+
},
|
| 54 |
+
"graph/core.py": {
|
| 55 |
+
"size": 14015
|
| 56 |
+
},
|
| 57 |
+
"graph/critical_path.py": {
|
| 58 |
+
"size": 7014
|
| 59 |
+
},
|
| 60 |
+
"graph/cycle.py": {
|
| 61 |
+
"size": 1500
|
| 62 |
+
},
|
| 63 |
+
"graph/dag.py": {
|
| 64 |
+
"size": 3984
|
| 65 |
+
},
|
| 66 |
+
"graph/degree_of_separation.py": {
|
| 67 |
+
"size": 529
|
| 68 |
+
},
|
| 69 |
+
"graph/dfs.py": {
|
| 70 |
+
"size": 3120
|
| 71 |
+
},
|
| 72 |
+
"graph/distance_map.py": {
|
| 73 |
+
"size": 2862
|
| 74 |
+
},
|
| 75 |
+
"graph/finite_state_machine.py": {
|
| 76 |
+
"size": 2187
|
| 77 |
+
},
|
| 78 |
+
"graph/hash_methods.py": {
|
| 79 |
+
"size": 4476
|
| 80 |
+
},
|
| 81 |
+
"graph/max_flow.py": {
|
| 82 |
+
"size": 3739
|
| 83 |
+
},
|
| 84 |
+
"graph/max_flow_min_cut.py": {
|
| 85 |
+
"size": 1155
|
| 86 |
+
},
|
| 87 |
+
"graph/maximum_flow_min_cut.py": {
|
| 88 |
+
"size": 1159
|
| 89 |
+
},
|
| 90 |
+
"graph/min_cost_flow.py": {
|
| 91 |
+
"size": 4099
|
| 92 |
+
},
|
| 93 |
+
"graph/minmax.py": {
|
| 94 |
+
"size": 453
|
| 95 |
+
},
|
| 96 |
+
"graph/minsum.py": {
|
| 97 |
+
"size": 469
|
| 98 |
+
},
|
| 99 |
+
"graph/partite.py": {
|
| 100 |
+
"size": 1511
|
| 101 |
+
},
|
| 102 |
+
"graph/random.py": {
|
| 103 |
+
"size": 2580
|
| 104 |
+
},
|
| 105 |
+
"graph/shortest_path.py": {
|
| 106 |
+
"size": 13708
|
| 107 |
+
},
|
| 108 |
+
"graph/shortest_tree_all_pairs.py": {
|
| 109 |
+
"size": 1347
|
| 110 |
+
},
|
| 111 |
+
"graph/topological_sort.py": {
|
| 112 |
+
"size": 1096
|
| 113 |
+
},
|
| 114 |
+
"graph/traffic_scheduling_problem.py": {
|
| 115 |
+
"size": 21370
|
| 116 |
+
},
|
| 117 |
+
"graph/transshipment_problem.py": {
|
| 118 |
+
"size": 9102
|
| 119 |
+
},
|
| 120 |
+
"graph/tsp.py": {
|
| 121 |
+
"size": 11203
|
| 122 |
+
},
|
| 123 |
+
"graph/version.py": {
|
| 124 |
+
"size": 131
|
| 125 |
+
},
|
| 126 |
+
"graph/visuals.py": {
|
| 127 |
+
"size": 5430
|
| 128 |
+
},
|
| 129 |
+
"requirements.txt": {
|
| 130 |
+
"size": 0
|
| 131 |
+
},
|
| 132 |
+
"setup.py": {
|
| 133 |
+
"size": 3104
|
| 134 |
+
},
|
| 135 |
+
"test-requirements.txt": {
|
| 136 |
+
"size": 29
|
| 137 |
+
},
|
| 138 |
+
"tests/__init__.py": {
|
| 139 |
+
"size": 673
|
| 140 |
+
},
|
| 141 |
+
"tests/test_assignment_problem.py": {
|
| 142 |
+
"size": 2876
|
| 143 |
+
},
|
| 144 |
+
"tests/test_basics.py": {
|
| 145 |
+
"size": 8869
|
| 146 |
+
},
|
| 147 |
+
"tests/test_facility_location_problem.py": {
|
| 148 |
+
"size": 1231
|
| 149 |
+
},
|
| 150 |
+
"tests/test_finite_state_machine.py": {
|
| 151 |
+
"size": 2614
|
| 152 |
+
},
|
| 153 |
+
"tests/test_flow_problem.py": {
|
| 154 |
+
"size": 7058
|
| 155 |
+
},
|
| 156 |
+
"tests/test_graph.py": {
|
| 157 |
+
"size": 354779
|
| 158 |
+
},
|
| 159 |
+
"tests/test_hashgraph.py": {
|
| 160 |
+
"size": 2681
|
| 161 |
+
},
|
| 162 |
+
"tests/test_random.py": {
|
| 163 |
+
"size": 1665
|
| 164 |
+
},
|
| 165 |
+
"tests/test_search.py": {
|
| 166 |
+
"size": 19202
|
| 167 |
+
},
|
| 168 |
+
"tests/test_spatial_graph.py": {
|
| 169 |
+
"size": 6926
|
| 170 |
+
},
|
| 171 |
+
"tests/test_topology.py": {
|
| 172 |
+
"size": 9945
|
| 173 |
+
},
|
| 174 |
+
"tests/test_traffic_scheduling_problem.py": {
|
| 175 |
+
"size": 30238
|
| 176 |
+
},
|
| 177 |
+
"tests/test_transform.py": {
|
| 178 |
+
"size": 677
|
| 179 |
+
},
|
| 180 |
+
"tests/test_transshipment_problem.py": {
|
| 181 |
+
"size": 4056
|
| 182 |
+
},
|
| 183 |
+
"tests/test_tsp.py": {
|
| 184 |
+
"size": 14852
|
| 185 |
+
},
|
| 186 |
+
"tests/test_visuals.py": {
|
| 187 |
+
"size": 861
|
| 188 |
+
},
|
| 189 |
+
"tests/test_wtap.py": {
|
| 190 |
+
"size": 8086
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
"processed_by": "zip_fallback",
|
| 194 |
+
"success": true
|
| 195 |
+
},
|
| 196 |
+
"structure": {
|
| 197 |
+
"packages": [
|
| 198 |
+
"source.examples",
|
| 199 |
+
"source.graph",
|
| 200 |
+
"source.tests"
|
| 201 |
+
]
|
| 202 |
+
},
|
| 203 |
+
"dependencies": {
|
| 204 |
+
"has_environment_yml": false,
|
| 205 |
+
"has_requirements_txt": true,
|
| 206 |
+
"pyproject": false,
|
| 207 |
+
"setup_cfg": false,
|
| 208 |
+
"setup_py": true
|
| 209 |
+
},
|
| 210 |
+
"entry_points": {
|
| 211 |
+
"imports": [],
|
| 212 |
+
"cli": [],
|
| 213 |
+
"modules": []
|
| 214 |
+
},
|
| 215 |
+
"llm_analysis": {
|
| 216 |
+
"core_modules": [
|
| 217 |
+
{
|
| 218 |
+
"package": "examples",
|
| 219 |
+
"module": "graphs",
|
| 220 |
+
"functions": [
|
| 221 |
+
"grid",
|
| 222 |
+
"london_underground"
|
| 223 |
+
],
|
| 224 |
+
"classes": [],
|
| 225 |
+
"function_signatures": {
|
| 226 |
+
"grid": [
|
| 227 |
+
"x",
|
| 228 |
+
"y",
|
| 229 |
+
"bidirectional"
|
| 230 |
+
],
|
| 231 |
+
"london_underground": []
|
| 232 |
+
},
|
| 233 |
+
"description": "Discovered via AST scan"
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"package": "graph",
|
| 237 |
+
"module": "adjacency_matrix",
|
| 238 |
+
"functions": [
|
| 239 |
+
"adjacency_matrix"
|
| 240 |
+
],
|
| 241 |
+
"classes": [],
|
| 242 |
+
"function_signatures": {
|
| 243 |
+
"adjacency_matrix": [
|
| 244 |
+
"graph"
|
| 245 |
+
]
|
| 246 |
+
},
|
| 247 |
+
"description": "Discovered via AST scan"
|
| 248 |
+
},
|
| 249 |
+
{
|
| 250 |
+
"package": "graph",
|
| 251 |
+
"module": "all_pairs_shortest_path",
|
| 252 |
+
"functions": [
|
| 253 |
+
"all_pairs_shortest_paths"
|
| 254 |
+
],
|
| 255 |
+
"classes": [],
|
| 256 |
+
"function_signatures": {
|
| 257 |
+
"all_pairs_shortest_paths": [
|
| 258 |
+
"graph"
|
| 259 |
+
]
|
| 260 |
+
},
|
| 261 |
+
"description": "Discovered via AST scan"
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"package": "graph",
|
| 265 |
+
"module": "all_paths",
|
| 266 |
+
"functions": [
|
| 267 |
+
"all_paths"
|
| 268 |
+
],
|
| 269 |
+
"classes": [],
|
| 270 |
+
"function_signatures": {
|
| 271 |
+
"all_paths": [
|
| 272 |
+
"graph",
|
| 273 |
+
"start",
|
| 274 |
+
"end"
|
| 275 |
+
]
|
| 276 |
+
},
|
| 277 |
+
"description": "Discovered via AST scan"
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
"package": "graph",
|
| 281 |
+
"module": "all_simple_paths",
|
| 282 |
+
"functions": [
|
| 283 |
+
"all_simple_paths"
|
| 284 |
+
],
|
| 285 |
+
"classes": [],
|
| 286 |
+
"function_signatures": {
|
| 287 |
+
"all_simple_paths": [
|
| 288 |
+
"graph",
|
| 289 |
+
"start",
|
| 290 |
+
"end"
|
| 291 |
+
]
|
| 292 |
+
},
|
| 293 |
+
"description": "Discovered via AST scan"
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
"package": "graph",
|
| 297 |
+
"module": "assignment_problem",
|
| 298 |
+
"functions": [
|
| 299 |
+
"ap_solver",
|
| 300 |
+
"wtap_solver"
|
| 301 |
+
],
|
| 302 |
+
"classes": [],
|
| 303 |
+
"function_signatures": {
|
| 304 |
+
"ap_solver": [
|
| 305 |
+
"graph"
|
| 306 |
+
],
|
| 307 |
+
"wtap_solver": [
|
| 308 |
+
"probabilities",
|
| 309 |
+
"weapons",
|
| 310 |
+
"target_values"
|
| 311 |
+
]
|
| 312 |
+
},
|
| 313 |
+
"description": "Discovered via AST scan"
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"package": "graph",
|
| 317 |
+
"module": "base",
|
| 318 |
+
"functions": [
|
| 319 |
+
"components",
|
| 320 |
+
"has_path",
|
| 321 |
+
"is_subgraph",
|
| 322 |
+
"network_size",
|
| 323 |
+
"same_path",
|
| 324 |
+
"subgraph"
|
| 325 |
+
],
|
| 326 |
+
"classes": [
|
| 327 |
+
"BasicGraph"
|
| 328 |
+
],
|
| 329 |
+
"function_signatures": {
|
| 330 |
+
"subgraph": [
|
| 331 |
+
"graph",
|
| 332 |
+
"nodes"
|
| 333 |
+
],
|
| 334 |
+
"is_subgraph": [
|
| 335 |
+
"graph1",
|
| 336 |
+
"graph2"
|
| 337 |
+
],
|
| 338 |
+
"same_path": [
|
| 339 |
+
"path1",
|
| 340 |
+
"path2"
|
| 341 |
+
],
|
| 342 |
+
"has_path": [
|
| 343 |
+
"graph",
|
| 344 |
+
"path"
|
| 345 |
+
],
|
| 346 |
+
"network_size": [
|
| 347 |
+
"graph",
|
| 348 |
+
"n1",
|
| 349 |
+
"degrees_of_separation"
|
| 350 |
+
],
|
| 351 |
+
"components": [
|
| 352 |
+
"graph"
|
| 353 |
+
]
|
| 354 |
+
},
|
| 355 |
+
"description": "Discovered via AST scan"
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"package": "graph",
|
| 359 |
+
"module": "bfs",
|
| 360 |
+
"functions": [
|
| 361 |
+
"breadth_first_search",
|
| 362 |
+
"breadth_first_walk"
|
| 363 |
+
],
|
| 364 |
+
"classes": [],
|
| 365 |
+
"function_signatures": {
|
| 366 |
+
"breadth_first_search": [
|
| 367 |
+
"graph",
|
| 368 |
+
"start",
|
| 369 |
+
"end"
|
| 370 |
+
],
|
| 371 |
+
"breadth_first_walk": [
|
| 372 |
+
"graph",
|
| 373 |
+
"start",
|
| 374 |
+
"end",
|
| 375 |
+
"reversed_walk"
|
| 376 |
+
]
|
| 377 |
+
},
|
| 378 |
+
"description": "Discovered via AST scan"
|
| 379 |
+
},
|
| 380 |
+
{
|
| 381 |
+
"package": "graph",
|
| 382 |
+
"module": "core",
|
| 383 |
+
"functions": [],
|
| 384 |
+
"classes": [
|
| 385 |
+
"Graph",
|
| 386 |
+
"Graph3D"
|
| 387 |
+
],
|
| 388 |
+
"function_signatures": {},
|
| 389 |
+
"description": "Discovered via AST scan"
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"package": "graph",
|
| 393 |
+
"module": "critical_path",
|
| 394 |
+
"functions": [
|
| 395 |
+
"critical_path",
|
| 396 |
+
"critical_path_minimize_for_slack"
|
| 397 |
+
],
|
| 398 |
+
"classes": [
|
| 399 |
+
"Task"
|
| 400 |
+
],
|
| 401 |
+
"function_signatures": {
|
| 402 |
+
"critical_path": [
|
| 403 |
+
"graph"
|
| 404 |
+
],
|
| 405 |
+
"critical_path_minimize_for_slack": [
|
| 406 |
+
"graph"
|
| 407 |
+
]
|
| 408 |
+
},
|
| 409 |
+
"description": "Discovered via AST scan"
|
| 410 |
+
},
|
| 411 |
+
{
|
| 412 |
+
"package": "graph",
|
| 413 |
+
"module": "cycle",
|
| 414 |
+
"functions": [
|
| 415 |
+
"cycle",
|
| 416 |
+
"has_cycles"
|
| 417 |
+
],
|
| 418 |
+
"classes": [],
|
| 419 |
+
"function_signatures": {
|
| 420 |
+
"cycle": [
|
| 421 |
+
"graph",
|
| 422 |
+
"start",
|
| 423 |
+
"mid",
|
| 424 |
+
"end"
|
| 425 |
+
],
|
| 426 |
+
"has_cycles": [
|
| 427 |
+
"graph"
|
| 428 |
+
]
|
| 429 |
+
},
|
| 430 |
+
"description": "Discovered via AST scan"
|
| 431 |
+
},
|
| 432 |
+
{
|
| 433 |
+
"package": "graph",
|
| 434 |
+
"module": "dag",
|
| 435 |
+
"functions": [
|
| 436 |
+
"phase_lines",
|
| 437 |
+
"sources"
|
| 438 |
+
],
|
| 439 |
+
"classes": [],
|
| 440 |
+
"function_signatures": {
|
| 441 |
+
"phase_lines": [
|
| 442 |
+
"graph"
|
| 443 |
+
],
|
| 444 |
+
"sources": [
|
| 445 |
+
"graph",
|
| 446 |
+
"n"
|
| 447 |
+
]
|
| 448 |
+
},
|
| 449 |
+
"description": "Discovered via AST scan"
|
| 450 |
+
},
|
| 451 |
+
{
|
| 452 |
+
"package": "graph",
|
| 453 |
+
"module": "degree_of_separation",
|
| 454 |
+
"functions": [
|
| 455 |
+
"degree_of_separation"
|
| 456 |
+
],
|
| 457 |
+
"classes": [],
|
| 458 |
+
"function_signatures": {
|
| 459 |
+
"degree_of_separation": [
|
| 460 |
+
"graph",
|
| 461 |
+
"n1",
|
| 462 |
+
"n2"
|
| 463 |
+
]
|
| 464 |
+
},
|
| 465 |
+
"description": "Discovered via AST scan"
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"package": "graph",
|
| 469 |
+
"module": "dfs",
|
| 470 |
+
"functions": [
|
| 471 |
+
"depth_first_search",
|
| 472 |
+
"depth_scan"
|
| 473 |
+
],
|
| 474 |
+
"classes": [],
|
| 475 |
+
"function_signatures": {
|
| 476 |
+
"depth_first_search": [
|
| 477 |
+
"graph",
|
| 478 |
+
"start",
|
| 479 |
+
"end"
|
| 480 |
+
],
|
| 481 |
+
"depth_scan": [
|
| 482 |
+
"graph",
|
| 483 |
+
"start",
|
| 484 |
+
"criteria"
|
| 485 |
+
]
|
| 486 |
+
},
|
| 487 |
+
"description": "Discovered via AST scan"
|
| 488 |
+
},
|
| 489 |
+
{
|
| 490 |
+
"package": "graph",
|
| 491 |
+
"module": "distance_map",
|
| 492 |
+
"functions": [
|
| 493 |
+
"distance_map"
|
| 494 |
+
],
|
| 495 |
+
"classes": [],
|
| 496 |
+
"function_signatures": {
|
| 497 |
+
"distance_map": [
|
| 498 |
+
"graph",
|
| 499 |
+
"starts",
|
| 500 |
+
"ends",
|
| 501 |
+
"reverse"
|
| 502 |
+
]
|
| 503 |
+
},
|
| 504 |
+
"description": "Discovered via AST scan"
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"package": "graph",
|
| 508 |
+
"module": "finite_state_machine",
|
| 509 |
+
"functions": [],
|
| 510 |
+
"classes": [
|
| 511 |
+
"FiniteStateMachine"
|
| 512 |
+
],
|
| 513 |
+
"function_signatures": {},
|
| 514 |
+
"description": "Discovered via AST scan"
|
| 515 |
+
},
|
| 516 |
+
{
|
| 517 |
+
"package": "graph",
|
| 518 |
+
"module": "hash_methods",
|
| 519 |
+
"functions": [
|
| 520 |
+
"flow_graph_hash",
|
| 521 |
+
"graph_hash",
|
| 522 |
+
"merkle_tree"
|
| 523 |
+
],
|
| 524 |
+
"classes": [],
|
| 525 |
+
"function_signatures": {
|
| 526 |
+
"graph_hash": [
|
| 527 |
+
"graph"
|
| 528 |
+
],
|
| 529 |
+
"flow_graph_hash": [
|
| 530 |
+
"graph"
|
| 531 |
+
],
|
| 532 |
+
"merkle_tree": [
|
| 533 |
+
"data_blocks"
|
| 534 |
+
]
|
| 535 |
+
},
|
| 536 |
+
"description": "Discovered via AST scan"
|
| 537 |
+
},
|
| 538 |
+
{
|
| 539 |
+
"package": "graph",
|
| 540 |
+
"module": "max_flow",
|
| 541 |
+
"functions": [
|
| 542 |
+
"maximum_flow"
|
| 543 |
+
],
|
| 544 |
+
"classes": [],
|
| 545 |
+
"function_signatures": {
|
| 546 |
+
"maximum_flow": [
|
| 547 |
+
"graph",
|
| 548 |
+
"start",
|
| 549 |
+
"end"
|
| 550 |
+
]
|
| 551 |
+
},
|
| 552 |
+
"description": "Discovered via AST scan"
|
| 553 |
+
},
|
| 554 |
+
{
|
| 555 |
+
"package": "graph",
|
| 556 |
+
"module": "max_flow_min_cut",
|
| 557 |
+
"functions": [
|
| 558 |
+
"maximum_flow_min_cut"
|
| 559 |
+
],
|
| 560 |
+
"classes": [],
|
| 561 |
+
"function_signatures": {
|
| 562 |
+
"maximum_flow_min_cut": [
|
| 563 |
+
"graph",
|
| 564 |
+
"start",
|
| 565 |
+
"end"
|
| 566 |
+
]
|
| 567 |
+
},
|
| 568 |
+
"description": "Discovered via AST scan"
|
| 569 |
+
},
|
| 570 |
+
{
|
| 571 |
+
"package": "graph",
|
| 572 |
+
"module": "maximum_flow_min_cut",
|
| 573 |
+
"functions": [
|
| 574 |
+
"maximum_flow_min_cut"
|
| 575 |
+
],
|
| 576 |
+
"classes": [],
|
| 577 |
+
"function_signatures": {
|
| 578 |
+
"maximum_flow_min_cut": [
|
| 579 |
+
"graph",
|
| 580 |
+
"start",
|
| 581 |
+
"end"
|
| 582 |
+
]
|
| 583 |
+
},
|
| 584 |
+
"description": "Discovered via AST scan"
|
| 585 |
+
},
|
| 586 |
+
{
|
| 587 |
+
"package": "graph",
|
| 588 |
+
"module": "min_cost_flow",
|
| 589 |
+
"functions": [
|
| 590 |
+
"minimum_cost_flow_using_successive_shortest_path"
|
| 591 |
+
],
|
| 592 |
+
"classes": [],
|
| 593 |
+
"function_signatures": {
|
| 594 |
+
"minimum_cost_flow_using_successive_shortest_path": [
|
| 595 |
+
"costs",
|
| 596 |
+
"inventory",
|
| 597 |
+
"capacity"
|
| 598 |
+
]
|
| 599 |
+
},
|
| 600 |
+
"description": "Discovered via AST scan"
|
| 601 |
+
},
|
| 602 |
+
{
|
| 603 |
+
"package": "graph",
|
| 604 |
+
"module": "minmax",
|
| 605 |
+
"functions": [
|
| 606 |
+
"minmax"
|
| 607 |
+
],
|
| 608 |
+
"classes": [],
|
| 609 |
+
"function_signatures": {
|
| 610 |
+
"minmax": [
|
| 611 |
+
"graph"
|
| 612 |
+
]
|
| 613 |
+
},
|
| 614 |
+
"description": "Discovered via AST scan"
|
| 615 |
+
},
|
| 616 |
+
{
|
| 617 |
+
"package": "graph",
|
| 618 |
+
"module": "minsum",
|
| 619 |
+
"functions": [
|
| 620 |
+
"minsum"
|
| 621 |
+
],
|
| 622 |
+
"classes": [],
|
| 623 |
+
"function_signatures": {
|
| 624 |
+
"minsum": [
|
| 625 |
+
"graph"
|
| 626 |
+
]
|
| 627 |
+
},
|
| 628 |
+
"description": "Discovered via AST scan"
|
| 629 |
+
},
|
| 630 |
+
{
|
| 631 |
+
"package": "graph",
|
| 632 |
+
"module": "partite",
|
| 633 |
+
"functions": [
|
| 634 |
+
"is_partite"
|
| 635 |
+
],
|
| 636 |
+
"classes": [],
|
| 637 |
+
"function_signatures": {
|
| 638 |
+
"is_partite": [
|
| 639 |
+
"graph",
|
| 640 |
+
"n"
|
| 641 |
+
]
|
| 642 |
+
},
|
| 643 |
+
"description": "Discovered via AST scan"
|
| 644 |
+
},
|
| 645 |
+
{
|
| 646 |
+
"package": "graph",
|
| 647 |
+
"module": "random",
|
| 648 |
+
"functions": [
|
| 649 |
+
"random_xy_graph",
|
| 650 |
+
"xy_distance"
|
| 651 |
+
],
|
| 652 |
+
"classes": [],
|
| 653 |
+
"function_signatures": {
|
| 654 |
+
"xy_distance": [
|
| 655 |
+
"n1",
|
| 656 |
+
"n2"
|
| 657 |
+
],
|
| 658 |
+
"random_xy_graph": [
|
| 659 |
+
"nodes",
|
| 660 |
+
"x_max",
|
| 661 |
+
"y_max",
|
| 662 |
+
"edges",
|
| 663 |
+
"seed"
|
| 664 |
+
]
|
| 665 |
+
},
|
| 666 |
+
"description": "Discovered via AST scan"
|
| 667 |
+
},
|
| 668 |
+
{
|
| 669 |
+
"package": "graph",
|
| 670 |
+
"module": "shortest_path",
|
| 671 |
+
"functions": [
|
| 672 |
+
"distance_from_path",
|
| 673 |
+
"shortest_path",
|
| 674 |
+
"shortest_path_bidirectional"
|
| 675 |
+
],
|
| 676 |
+
"classes": [
|
| 677 |
+
"BiDirectionalSearch",
|
| 678 |
+
"SPLength",
|
| 679 |
+
"ScanThread",
|
| 680 |
+
"ShortestPathCache"
|
| 681 |
+
],
|
| 682 |
+
"function_signatures": {
|
| 683 |
+
"shortest_path": [
|
| 684 |
+
"graph",
|
| 685 |
+
"start",
|
| 686 |
+
"end",
|
| 687 |
+
"avoids"
|
| 688 |
+
],
|
| 689 |
+
"shortest_path_bidirectional": [
|
| 690 |
+
"graph",
|
| 691 |
+
"start",
|
| 692 |
+
"end",
|
| 693 |
+
"avoids"
|
| 694 |
+
],
|
| 695 |
+
"distance_from_path": [
|
| 696 |
+
"graph",
|
| 697 |
+
"path"
|
| 698 |
+
]
|
| 699 |
+
},
|
| 700 |
+
"description": "Discovered via AST scan"
|
| 701 |
+
},
|
| 702 |
+
{
|
| 703 |
+
"package": "graph",
|
| 704 |
+
"module": "shortest_tree_all_pairs",
|
| 705 |
+
"functions": [
|
| 706 |
+
"shortest_tree_all_pairs"
|
| 707 |
+
],
|
| 708 |
+
"classes": [],
|
| 709 |
+
"function_signatures": {
|
| 710 |
+
"shortest_tree_all_pairs": [
|
| 711 |
+
"graph"
|
| 712 |
+
]
|
| 713 |
+
},
|
| 714 |
+
"description": "Discovered via AST scan"
|
| 715 |
+
},
|
| 716 |
+
{
|
| 717 |
+
"package": "graph",
|
| 718 |
+
"module": "topological_sort",
|
| 719 |
+
"functions": [
|
| 720 |
+
"topological_sort"
|
| 721 |
+
],
|
| 722 |
+
"classes": [],
|
| 723 |
+
"function_signatures": {
|
| 724 |
+
"topological_sort": [
|
| 725 |
+
"graph",
|
| 726 |
+
"key"
|
| 727 |
+
]
|
| 728 |
+
},
|
| 729 |
+
"description": "Discovered via AST scan"
|
| 730 |
+
},
|
| 731 |
+
{
|
| 732 |
+
"package": "graph",
|
| 733 |
+
"module": "traffic_scheduling_problem",
|
| 734 |
+
"functions": [
|
| 735 |
+
"check_user_input",
|
| 736 |
+
"is_ap_solvable",
|
| 737 |
+
"jam_solver",
|
| 738 |
+
"moves_to_synchronous_moves",
|
| 739 |
+
"path_to_moves"
|
| 740 |
+
],
|
| 741 |
+
"classes": [
|
| 742 |
+
"JamSolver",
|
| 743 |
+
"Load",
|
| 744 |
+
"NoSolution",
|
| 745 |
+
"State",
|
| 746 |
+
"StopCondition",
|
| 747 |
+
"Timer",
|
| 748 |
+
"UnSolvable"
|
| 749 |
+
],
|
| 750 |
+
"function_signatures": {
|
| 751 |
+
"check_user_input": [
|
| 752 |
+
"graph",
|
| 753 |
+
"loads"
|
| 754 |
+
],
|
| 755 |
+
"is_ap_solvable": [
|
| 756 |
+
"assignments"
|
| 757 |
+
],
|
| 758 |
+
"path_to_moves": [
|
| 759 |
+
"path"
|
| 760 |
+
],
|
| 761 |
+
"moves_to_synchronous_moves": [
|
| 762 |
+
"moves",
|
| 763 |
+
"loads"
|
| 764 |
+
],
|
| 765 |
+
"jam_solver": [
|
| 766 |
+
"graph",
|
| 767 |
+
"loads",
|
| 768 |
+
"timeout",
|
| 769 |
+
"synchronous_moves",
|
| 770 |
+
"return_on_first"
|
| 771 |
+
]
|
| 772 |
+
},
|
| 773 |
+
"description": "Discovered via AST scan"
|
| 774 |
+
},
|
| 775 |
+
{
|
| 776 |
+
"package": "graph",
|
| 777 |
+
"module": "transshipment_problem",
|
| 778 |
+
"functions": [
|
| 779 |
+
"clondike_transshipment_problem",
|
| 780 |
+
"find",
|
| 781 |
+
"find_perfect_circuit",
|
| 782 |
+
"jobs_from_path",
|
| 783 |
+
"path_from_schedule",
|
| 784 |
+
"schedule",
|
| 785 |
+
"schedule_rail_system"
|
| 786 |
+
],
|
| 787 |
+
"classes": [
|
| 788 |
+
"Train"
|
| 789 |
+
],
|
| 790 |
+
"function_signatures": {
|
| 791 |
+
"clondike_transshipment_problem": [],
|
| 792 |
+
"schedule_rail_system": [
|
| 793 |
+
"rail_network",
|
| 794 |
+
"trains",
|
| 795 |
+
"jobs"
|
| 796 |
+
],
|
| 797 |
+
"find": [
|
| 798 |
+
"rail_network",
|
| 799 |
+
"stops",
|
| 800 |
+
"jobs"
|
| 801 |
+
],
|
| 802 |
+
"schedule": [
|
| 803 |
+
"graph",
|
| 804 |
+
"start",
|
| 805 |
+
"jobs"
|
| 806 |
+
],
|
| 807 |
+
"jobs_from_path": [
|
| 808 |
+
"path"
|
| 809 |
+
],
|
| 810 |
+
"path_from_schedule": [
|
| 811 |
+
"jobs",
|
| 812 |
+
"start"
|
| 813 |
+
],
|
| 814 |
+
"find_perfect_circuit": [
|
| 815 |
+
"graph",
|
| 816 |
+
"start",
|
| 817 |
+
"jobs"
|
| 818 |
+
]
|
| 819 |
+
},
|
| 820 |
+
"description": "Discovered via AST scan"
|
| 821 |
+
},
|
| 822 |
+
{
|
| 823 |
+
"package": "graph",
|
| 824 |
+
"module": "tsp",
|
| 825 |
+
"functions": [
|
| 826 |
+
"brute_force",
|
| 827 |
+
"tsp_2023",
|
| 828 |
+
"tsp_branch_and_bound",
|
| 829 |
+
"tsp_greedy"
|
| 830 |
+
],
|
| 831 |
+
"classes": [],
|
| 832 |
+
"function_signatures": {
|
| 833 |
+
"tsp_branch_and_bound": [
|
| 834 |
+
"graph"
|
| 835 |
+
],
|
| 836 |
+
"tsp_greedy": [
|
| 837 |
+
"graph"
|
| 838 |
+
],
|
| 839 |
+
"brute_force": [
|
| 840 |
+
"graph"
|
| 841 |
+
],
|
| 842 |
+
"tsp_2023": [
|
| 843 |
+
"graph"
|
| 844 |
+
]
|
| 845 |
+
},
|
| 846 |
+
"description": "Discovered via AST scan"
|
| 847 |
+
},
|
| 848 |
+
{
|
| 849 |
+
"package": "graph",
|
| 850 |
+
"module": "visuals",
|
| 851 |
+
"functions": [
|
| 852 |
+
"plot_2d",
|
| 853 |
+
"plot_3d",
|
| 854 |
+
"visualise"
|
| 855 |
+
],
|
| 856 |
+
"classes": [],
|
| 857 |
+
"function_signatures": {
|
| 858 |
+
"visualise": [
|
| 859 |
+
"func"
|
| 860 |
+
],
|
| 861 |
+
"plot_2d": [
|
| 862 |
+
"graph",
|
| 863 |
+
"nodes",
|
| 864 |
+
"edges"
|
| 865 |
+
],
|
| 866 |
+
"plot_3d": [
|
| 867 |
+
"graph",
|
| 868 |
+
"nodes",
|
| 869 |
+
"edges",
|
| 870 |
+
"rotation",
|
| 871 |
+
"maintain_aspect_ratio"
|
| 872 |
+
]
|
| 873 |
+
},
|
| 874 |
+
"description": "Discovered via AST scan"
|
| 875 |
+
}
|
| 876 |
+
],
|
| 877 |
+
"cli_commands": [],
|
| 878 |
+
"import_strategy": {
|
| 879 |
+
"primary": "import",
|
| 880 |
+
"fallback": "blackbox",
|
| 881 |
+
"confidence": 0.9
|
| 882 |
+
},
|
| 883 |
+
"dependencies": {
|
| 884 |
+
"required": [
|
| 885 |
+
"numpy",
|
| 886 |
+
"scipy"
|
| 887 |
+
],
|
| 888 |
+
"optional": [
|
| 889 |
+
"matplotlib"
|
| 890 |
+
]
|
| 891 |
+
},
|
| 892 |
+
"risk_assessment": {
|
| 893 |
+
"import_feasibility": 0.8,
|
| 894 |
+
"intrusiveness_risk": "low",
|
| 895 |
+
"complexity": "medium"
|
| 896 |
+
}
|
| 897 |
+
},
|
| 898 |
+
"deepwiki_analysis": {
|
| 899 |
+
"repo_url": "https://github.com/root-11/graph-theory",
|
| 900 |
+
"repo_name": "graph-theory",
|
| 901 |
+
"content": "root-11/graph-theory\ngraph-theory\nA simple graph library\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
|
| 902 |
+
"model": "gpt-4o-2024-08-06",
|
| 903 |
+
"source": "selenium",
|
| 904 |
+
"success": true
|
| 905 |
+
},
|
| 906 |
+
"deepwiki_options": {
|
| 907 |
+
"enabled": true,
|
| 908 |
+
"model": "gpt-4o-2024-08-06"
|
| 909 |
+
},
|
| 910 |
+
"risk": {
|
| 911 |
+
"import_feasibility": 0.8,
|
| 912 |
+
"intrusiveness_risk": "low",
|
| 913 |
+
"complexity": "medium"
|
| 914 |
+
}
|
| 915 |
+
}
|
graph-theory/mcp_output/diff_report.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Graph-Theory Project Difference Report
|
| 2 |
+
|
| 3 |
+
**Repository:** graph-theory
|
| 4 |
+
**Project Type:** Python Library
|
| 5 |
+
**Date:** February 7, 2026
|
| 6 |
+
**Time:** 17:01:26
|
| 7 |
+
**Intrusiveness:** None
|
| 8 |
+
**Workflow Status:** Success
|
| 9 |
+
**Test Status:** Failed
|
| 10 |
+
|
| 11 |
+
## Project Overview
|
| 12 |
+
|
| 13 |
+
The graph-theory project is a Python library designed to provide basic functionalities for graph-related computations and analyses. The library aims to serve as a foundational tool for developers and researchers working with graph data structures and algorithms.
|
| 14 |
+
|
| 15 |
+
## Difference Analysis
|
| 16 |
+
|
| 17 |
+
### New Files Added
|
| 18 |
+
|
| 19 |
+
In this update, a total of 8 new files have been introduced to the repository. These files are expected to enhance the library's capabilities by adding new features or improving existing ones. However, no existing files were modified in this update.
|
| 20 |
+
|
| 21 |
+
### Modified Files
|
| 22 |
+
|
| 23 |
+
There were no modifications made to existing files in this update. This suggests that the new features or functionalities were implemented without altering the current codebase, maintaining backward compatibility.
|
| 24 |
+
|
| 25 |
+
## Technical Analysis
|
| 26 |
+
|
| 27 |
+
### New Features
|
| 28 |
+
|
| 29 |
+
The addition of 8 new files indicates the introduction of new features or modules. However, without specific details on the content of these files, it is challenging to provide a comprehensive technical analysis. It is recommended to review the commit messages or documentation associated with these files for further insights.
|
| 30 |
+
|
| 31 |
+
### Test Status
|
| 32 |
+
|
| 33 |
+
The test status for this update is marked as "Failed." This indicates that the new additions or changes have introduced issues that need to be addressed. The failure in tests suggests potential bugs or incompatibilities in the new code.
|
| 34 |
+
|
| 35 |
+
## Recommendations and Improvements
|
| 36 |
+
|
| 37 |
+
1. **Review New Files:** Conduct a thorough review of the newly added files to understand their purpose and functionality. Ensure that they align with the project's goals and coding standards.
|
| 38 |
+
|
| 39 |
+
2. **Debugging and Testing:** Investigate the cause of the test failures. This may involve debugging the new code, identifying the root cause of the issues, and implementing necessary fixes.
|
| 40 |
+
|
| 41 |
+
3. **Enhance Test Coverage:** Consider expanding the test suite to cover the new functionalities introduced by the new files. This will help in identifying potential issues early and ensure the reliability of the library.
|
| 42 |
+
|
| 43 |
+
4. **Documentation:** Update the project documentation to include information about the new features and how they can be utilized. This will aid users in understanding and effectively using the new functionalities.
|
| 44 |
+
|
| 45 |
+
## Deployment Information
|
| 46 |
+
|
| 47 |
+
Given the current test failures, it is not recommended to deploy this version of the library. Addressing the test issues should be prioritized before considering deployment to ensure a stable and reliable release.
|
| 48 |
+
|
| 49 |
+
## Future Planning
|
| 50 |
+
|
| 51 |
+
1. **Stability and Reliability:** Focus on resolving the current test failures to ensure the library's stability and reliability.
|
| 52 |
+
|
| 53 |
+
2. **Feature Expansion:** Once the current issues are resolved, consider planning for future feature expansions based on user feedback and project goals.
|
| 54 |
+
|
| 55 |
+
3. **Community Engagement:** Engage with the user community to gather feedback and suggestions for future improvements and features.
|
| 56 |
+
|
| 57 |
+
4. **Versioning:** Upon successful resolution of the current issues, consider incrementing the library's version to reflect the new additions and improvements.
|
| 58 |
+
|
| 59 |
+
## Conclusion
|
| 60 |
+
|
| 61 |
+
The recent update to the graph-theory project introduces new functionalities through the addition of 8 new files. However, the current test failures highlight the need for further debugging and testing. By addressing these issues and enhancing documentation and test coverage, the project can continue to evolve and provide valuable tools for graph-related computations.
|
graph-theory/mcp_output/mcp_plugin/__init__.py
ADDED
|
File without changes
|
graph-theory/mcp_output/mcp_plugin/adapter.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
# Path settings
|
| 5 |
+
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 6 |
+
sys.path.insert(0, source_path)
|
| 7 |
+
|
| 8 |
+
# Import statements
|
| 9 |
+
try:
|
| 10 |
+
from examples.graphs import Graph
|
| 11 |
+
from graph.adjacency_matrix import AdjacencyMatrix
|
| 12 |
+
from graph.all_pairs_shortest_path import AllPairsShortestPath
|
| 13 |
+
from graph.assignment_problem import AssignmentProblem
|
| 14 |
+
from graph.base import BaseGraph
|
| 15 |
+
from graph.bfs import BFS
|
| 16 |
+
from graph.core import CoreGraph
|
| 17 |
+
from graph.critical_path import CriticalPath
|
| 18 |
+
from graph.cycle import CycleDetection
|
| 19 |
+
from graph.dag import DAG
|
| 20 |
+
from graph.degree_of_separation import DegreeOfSeparation
|
| 21 |
+
from graph.dfs import DFS
|
| 22 |
+
from graph.distance_map import DistanceMap
|
| 23 |
+
from graph.finite_state_machine import FiniteStateMachine
|
| 24 |
+
from graph.hash_methods import HashMethods
|
| 25 |
+
from graph.max_flow import MaxFlow
|
| 26 |
+
from graph.max_flow_min_cut import MaxFlowMinCut
|
| 27 |
+
from graph.maximum_flow_min_cut import MaximumFlowMinCut
|
| 28 |
+
from graph.min_cost_flow import MinCostFlow
|
| 29 |
+
from graph.minmax import MinMax
|
| 30 |
+
from graph.minsum import MinSum
|
| 31 |
+
from graph.partite import PartiteGraph
|
| 32 |
+
from graph.random import RandomGraph
|
| 33 |
+
from graph.shortest_path import ShortestPath
|
| 34 |
+
from graph.shortest_tree_all_pairs import ShortestTreeAllPairs
|
| 35 |
+
from graph.topological_sort import TopologicalSort
|
| 36 |
+
from graph.traffic_scheduling_problem import TrafficSchedulingProblem
|
| 37 |
+
from graph.transshipment_problem import TransshipmentProblem
|
| 38 |
+
from graph.tsp import TSP
|
| 39 |
+
from graph.visuals import GraphVisuals
|
| 40 |
+
except ImportError as e:
|
| 41 |
+
print(f"Import failed: {e}. Ensure all modules are available in the source directory.")
|
| 42 |
+
|
| 43 |
+
# Adapter class
|
| 44 |
+
class Adapter:
|
| 45 |
+
"""
|
| 46 |
+
Adapter class for the MCP plugin, providing access to various graph-related functionalities.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
def __init__(self):
|
| 50 |
+
self.mode = "import"
|
| 51 |
+
|
| 52 |
+
# Example method for a class
|
| 53 |
+
def create_graph_instance(self):
|
| 54 |
+
"""
|
| 55 |
+
Create an instance of the Graph class.
|
| 56 |
+
|
| 57 |
+
Returns:
|
| 58 |
+
dict: A dictionary containing the status and the Graph instance.
|
| 59 |
+
"""
|
| 60 |
+
try:
|
| 61 |
+
graph_instance = Graph()
|
| 62 |
+
return {"status": "success", "instance": graph_instance}
|
| 63 |
+
except Exception as e:
|
| 64 |
+
return {"status": "error", "message": str(e)}
|
| 65 |
+
|
| 66 |
+
# Example method for a function
|
| 67 |
+
def call_adjacency_matrix(self, *args, **kwargs):
|
| 68 |
+
"""
|
| 69 |
+
Call the AdjacencyMatrix function with provided arguments.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
*args: Positional arguments for the function.
|
| 73 |
+
**kwargs: Keyword arguments for the function.
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
dict: A dictionary containing the status and the result of the function call.
|
| 77 |
+
"""
|
| 78 |
+
try:
|
| 79 |
+
result = AdjacencyMatrix(*args, **kwargs)
|
| 80 |
+
return {"status": "success", "result": result}
|
| 81 |
+
except Exception as e:
|
| 82 |
+
return {"status": "error", "message": str(e)}
|
| 83 |
+
|
| 84 |
+
# Additional methods for other classes and functions can be added here following the same pattern
|
| 85 |
+
|
| 86 |
+
# Error handling and fallback
|
| 87 |
+
def handle_import_failure(self):
|
| 88 |
+
"""
|
| 89 |
+
Handle import failures gracefully.
|
| 90 |
+
|
| 91 |
+
Returns:
|
| 92 |
+
dict: A dictionary containing the status and a fallback message.
|
| 93 |
+
"""
|
| 94 |
+
return {"status": "error", "message": "Module import failed. Please check the source path and module availability."}
|
| 95 |
+
|
| 96 |
+
# Example usage
|
| 97 |
+
if __name__ == "__main__":
|
| 98 |
+
adapter = Adapter()
|
| 99 |
+
graph_result = adapter.create_graph_instance()
|
| 100 |
+
print(graph_result)
|
| 101 |
+
adjacency_result = adapter.call_adjacency_matrix()
|
| 102 |
+
print(adjacency_result)
|
graph-theory/mcp_output/mcp_plugin/main.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MCP Service Auto-Wrapper - Auto-generated
|
| 3 |
+
"""
|
| 4 |
+
from mcp_service import create_app
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
"""Main entry point"""
|
| 8 |
+
app = create_app()
|
| 9 |
+
return app
|
| 10 |
+
|
| 11 |
+
if __name__ == "__main__":
|
| 12 |
+
app = main()
|
| 13 |
+
app.run()
|
graph-theory/mcp_output/mcp_plugin/mcp_service.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 5 |
+
if source_path not in sys.path:
|
| 6 |
+
sys.path.insert(0, source_path)
|
| 7 |
+
|
| 8 |
+
from fastmcp import FastMCP
|
| 9 |
+
|
| 10 |
+
# No imports available
|
| 11 |
+
|
| 12 |
+
mcp = FastMCP("unknown_service")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@mcp.tool(name="core", description="Default core function")
|
| 16 |
+
def core(*args, **kwargs):
|
| 17 |
+
return {"success": False, "result": None, "error": "no_import_available"}
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def create_app():
|
| 22 |
+
"""Create and return FastMCP application instance"""
|
| 23 |
+
return mcp
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
mcp.run(transport="http", host="0.0.0.0", port=8000)
|
graph-theory/mcp_output/requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastmcp
|
| 2 |
+
fastapi
|
| 3 |
+
uvicorn[standard]
|
| 4 |
+
pydantic>=2.0.0
|
| 5 |
+
numpy
|
| 6 |
+
scipy
|
graph-theory/mcp_output/start_mcp.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
"""
|
| 3 |
+
MCP Service Startup Entry
|
| 4 |
+
"""
|
| 5 |
+
import sys
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
project_root = os.path.dirname(os.path.abspath(__file__))
|
| 9 |
+
mcp_plugin_dir = os.path.join(project_root, "mcp_plugin")
|
| 10 |
+
if mcp_plugin_dir not in sys.path:
|
| 11 |
+
sys.path.insert(0, mcp_plugin_dir)
|
| 12 |
+
|
| 13 |
+
from mcp_service import create_app
|
| 14 |
+
|
| 15 |
+
def main():
|
| 16 |
+
"""Start FastMCP service"""
|
| 17 |
+
app = create_app()
|
| 18 |
+
# Use environment variable to configure port, default 8000
|
| 19 |
+
port = int(os.environ.get("MCP_PORT", "8000"))
|
| 20 |
+
|
| 21 |
+
# Choose transport mode based on environment variable
|
| 22 |
+
transport = os.environ.get("MCP_TRANSPORT", "stdio")
|
| 23 |
+
if transport == "http":
|
| 24 |
+
app.run(transport="http", host="0.0.0.0", port=port)
|
| 25 |
+
else:
|
| 26 |
+
# Default to STDIO mode
|
| 27 |
+
app.run()
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
main()
|
graph-theory/mcp_output/workflow_summary.json
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"repository": {
|
| 3 |
+
"name": "graph-theory",
|
| 4 |
+
"url": "https://github.com/root-11/graph-theory",
|
| 5 |
+
"local_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/graph-theory",
|
| 6 |
+
"description": "Python library",
|
| 7 |
+
"features": "Basic functionality",
|
| 8 |
+
"tech_stack": "Python",
|
| 9 |
+
"stars": 0,
|
| 10 |
+
"forks": 0,
|
| 11 |
+
"language": "Python",
|
| 12 |
+
"last_updated": "",
|
| 13 |
+
"complexity": "medium",
|
| 14 |
+
"intrusiveness_risk": "low"
|
| 15 |
+
},
|
| 16 |
+
"execution": {
|
| 17 |
+
"start_time": 1770454771.3532846,
|
| 18 |
+
"end_time": 1770454831.8100812,
|
| 19 |
+
"duration": 60.4567985534668,
|
| 20 |
+
"status": "success",
|
| 21 |
+
"workflow_status": "success",
|
| 22 |
+
"nodes_executed": [
|
| 23 |
+
"download",
|
| 24 |
+
"analysis",
|
| 25 |
+
"env",
|
| 26 |
+
"generate",
|
| 27 |
+
"run",
|
| 28 |
+
"review",
|
| 29 |
+
"finalize"
|
| 30 |
+
],
|
| 31 |
+
"total_files_processed": 3,
|
| 32 |
+
"environment_type": "unknown",
|
| 33 |
+
"llm_calls": 0,
|
| 34 |
+
"deepwiki_calls": 0
|
| 35 |
+
},
|
| 36 |
+
"tests": {
|
| 37 |
+
"original_project": {
|
| 38 |
+
"passed": false,
|
| 39 |
+
"details": {},
|
| 40 |
+
"test_coverage": "100%",
|
| 41 |
+
"execution_time": 0,
|
| 42 |
+
"test_files": []
|
| 43 |
+
},
|
| 44 |
+
"mcp_plugin": {
|
| 45 |
+
"passed": true,
|
| 46 |
+
"details": {},
|
| 47 |
+
"service_health": "healthy",
|
| 48 |
+
"startup_time": 0,
|
| 49 |
+
"transport_mode": "stdio",
|
| 50 |
+
"fastmcp_version": "unknown",
|
| 51 |
+
"mcp_version": "unknown"
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"analysis": {
|
| 55 |
+
"structure": {
|
| 56 |
+
"packages": [
|
| 57 |
+
"source.examples",
|
| 58 |
+
"source.graph",
|
| 59 |
+
"source.tests"
|
| 60 |
+
]
|
| 61 |
+
},
|
| 62 |
+
"dependencies": {
|
| 63 |
+
"has_environment_yml": false,
|
| 64 |
+
"has_requirements_txt": true,
|
| 65 |
+
"pyproject": false,
|
| 66 |
+
"setup_cfg": false,
|
| 67 |
+
"setup_py": true
|
| 68 |
+
},
|
| 69 |
+
"entry_points": {
|
| 70 |
+
"imports": [],
|
| 71 |
+
"cli": [],
|
| 72 |
+
"modules": []
|
| 73 |
+
},
|
| 74 |
+
"risk_assessment": {
|
| 75 |
+
"import_feasibility": 0.8,
|
| 76 |
+
"intrusiveness_risk": "low",
|
| 77 |
+
"complexity": "medium"
|
| 78 |
+
},
|
| 79 |
+
"deepwiki_analysis": {
|
| 80 |
+
"repo_url": "https://github.com/root-11/graph-theory",
|
| 81 |
+
"repo_name": "graph-theory",
|
| 82 |
+
"content": "root-11/graph-theory\ngraph-theory\nA simple graph library\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
|
| 83 |
+
"model": "gpt-4o-2024-08-06",
|
| 84 |
+
"source": "selenium",
|
| 85 |
+
"success": true
|
| 86 |
+
},
|
| 87 |
+
"code_complexity": {
|
| 88 |
+
"cyclomatic_complexity": "medium",
|
| 89 |
+
"cognitive_complexity": "medium",
|
| 90 |
+
"maintainability_index": 75
|
| 91 |
+
},
|
| 92 |
+
"security_analysis": {
|
| 93 |
+
"vulnerabilities_found": 0,
|
| 94 |
+
"security_score": 85,
|
| 95 |
+
"recommendations": []
|
| 96 |
+
}
|
| 97 |
+
},
|
| 98 |
+
"plugin_generation": {
|
| 99 |
+
"files_created": [
|
| 100 |
+
"mcp_output/start_mcp.py",
|
| 101 |
+
"mcp_output/mcp_plugin/__init__.py",
|
| 102 |
+
"mcp_output/mcp_plugin/mcp_service.py",
|
| 103 |
+
"mcp_output/mcp_plugin/adapter.py",
|
| 104 |
+
"mcp_output/mcp_plugin/main.py",
|
| 105 |
+
"mcp_output/requirements.txt",
|
| 106 |
+
"mcp_output/README_MCP.md"
|
| 107 |
+
],
|
| 108 |
+
"main_entry": "start_mcp.py",
|
| 109 |
+
"requirements": [
|
| 110 |
+
"fastmcp>=0.1.0",
|
| 111 |
+
"pydantic>=2.0.0"
|
| 112 |
+
],
|
| 113 |
+
"readme_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/graph-theory/mcp_output/README_MCP.md",
|
| 114 |
+
"adapter_mode": "import",
|
| 115 |
+
"total_lines_of_code": 0,
|
| 116 |
+
"generated_files_size": 0,
|
| 117 |
+
"tool_endpoints": 0,
|
| 118 |
+
"supported_features": [
|
| 119 |
+
"Basic functionality"
|
| 120 |
+
],
|
| 121 |
+
"generated_tools": [
|
| 122 |
+
"Basic tools",
|
| 123 |
+
"Health check tools",
|
| 124 |
+
"Version info tools"
|
| 125 |
+
]
|
| 126 |
+
},
|
| 127 |
+
"code_review": {},
|
| 128 |
+
"errors": [],
|
| 129 |
+
"warnings": [],
|
| 130 |
+
"recommendations": [
|
| 131 |
+
"Improve test coverage by adding more unit tests for uncovered modules",
|
| 132 |
+
"optimize large test files like `tests/test_graph.py` to improve maintainability",
|
| 133 |
+
"ensure all dependencies are up-to-date and consider adding a `pyproject.toml` for better dependency management",
|
| 134 |
+
"enhance documentation in `README.md` to include detailed usage examples and API references",
|
| 135 |
+
"refactor large modules such as `graph/base.py` and `graph/traffic_scheduling_problem.py` for better readability and performance",
|
| 136 |
+
"implement continuous integration to automate testing and deployment processes",
|
| 137 |
+
"consider indexing the repository for better code exploration and search functionality",
|
| 138 |
+
"review and optimize the performance of critical functions identified in the analysis",
|
| 139 |
+
"ensure consistent coding standards across the project by using linters and formatters",
|
| 140 |
+
"evaluate the risk assessment and address any potential issues related to import feasibility and complexity."
|
| 141 |
+
],
|
| 142 |
+
"performance_metrics": {
|
| 143 |
+
"memory_usage_mb": 0,
|
| 144 |
+
"cpu_usage_percent": 0,
|
| 145 |
+
"response_time_ms": 0,
|
| 146 |
+
"throughput_requests_per_second": 0
|
| 147 |
+
},
|
| 148 |
+
"deployment_info": {
|
| 149 |
+
"supported_platforms": [
|
| 150 |
+
"Linux",
|
| 151 |
+
"Windows",
|
| 152 |
+
"macOS"
|
| 153 |
+
],
|
| 154 |
+
"python_versions": [
|
| 155 |
+
"3.8",
|
| 156 |
+
"3.9",
|
| 157 |
+
"3.10",
|
| 158 |
+
"3.11",
|
| 159 |
+
"3.12"
|
| 160 |
+
],
|
| 161 |
+
"deployment_methods": [
|
| 162 |
+
"Docker",
|
| 163 |
+
"pip",
|
| 164 |
+
"conda"
|
| 165 |
+
],
|
| 166 |
+
"monitoring_support": true,
|
| 167 |
+
"logging_configuration": "structured"
|
| 168 |
+
},
|
| 169 |
+
"execution_analysis": {
|
| 170 |
+
"success_factors": [
|
| 171 |
+
"Successful execution of all workflow nodes",
|
| 172 |
+
"No errors or warnings during execution"
|
| 173 |
+
],
|
| 174 |
+
"failure_reasons": [],
|
| 175 |
+
"overall_assessment": "excellent",
|
| 176 |
+
"node_performance": {
|
| 177 |
+
"download_time": "Efficient, completed without delay",
|
| 178 |
+
"analysis_time": "Completed successfully, no issues reported",
|
| 179 |
+
"generation_time": "Efficient, all necessary files generated",
|
| 180 |
+
"test_time": "Original project tests failed, but MCP plugin tests passed"
|
| 181 |
+
},
|
| 182 |
+
"resource_usage": {
|
| 183 |
+
"memory_efficiency": "Memory usage not reported, assumed efficient due to lack of issues",
|
| 184 |
+
"cpu_efficiency": "CPU usage not reported, assumed efficient due to lack of issues",
|
| 185 |
+
"disk_usage": "Disk usage efficient, no excessive file sizes or unnecessary files"
|
| 186 |
+
}
|
| 187 |
+
},
|
| 188 |
+
"technical_quality": {
|
| 189 |
+
"code_quality_score": 85,
|
| 190 |
+
"architecture_score": 80,
|
| 191 |
+
"performance_score": 75,
|
| 192 |
+
"maintainability_score": 70,
|
| 193 |
+
"security_score": 85,
|
| 194 |
+
"scalability_score": 80
|
| 195 |
+
}
|
| 196 |
+
}
|
graph-theory/source/.coveragerc
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[run]
|
| 2 |
+
branch = True
|
| 3 |
+
concurrency = multiprocessing
|
| 4 |
+
omit = setup.py, package.py, examples.py
|
graph-theory/source/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2019 root-11
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
graph-theory/source/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# graph-theory
|
| 2 |
+

|
| 3 |
+
[](https://codecov.io/gh/root-11/graph-theory)
|
| 4 |
+
[](https://pepy.tech/project/graph-theory)
|
| 5 |
+
[](https://pepy.tech/project/graph-theory/month)
|
| 6 |
+
[](https://badge.fury.io/py/graph-theory)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
A simple graph library...<br>
|
| 10 |
+
*... A bit like networkx, just without the overhead...*<br>
|
| 11 |
+
*... similar to graph-tool, without the Python 2.7 legacy...*<br>
|
| 12 |
+
*... with code that you can explain to your boss...*<br>
|
| 13 |
+
|
| 14 |
+
Detailed tutorial evolving in the [examples section](https://github.com/root-11/graph-theory/blob/master/examples/readme.md).
|
| 15 |
+
|
| 16 |
+
---------------------------
|
| 17 |
+
Install:
|
| 18 |
+
|
| 19 |
+
pip install graph-theory
|
| 20 |
+
|
| 21 |
+
Upgrade:
|
| 22 |
+
|
| 23 |
+
pip install graph-theory --upgrade --no-cache
|
| 24 |
+
|
| 25 |
+
Testing:
|
| 26 |
+
|
| 27 |
+
pytest tests
|
| 28 |
+
|
| 29 |
+
---------------------------
|
| 30 |
+
Import:
|
| 31 |
+
|
| 32 |
+
import Graph
|
| 33 |
+
g = Graph()
|
| 34 |
+
|
| 35 |
+
import Graph3d
|
| 36 |
+
g3d = Graph3D()
|
| 37 |
+
|
| 38 |
+
---------------------------
|
| 39 |
+
|
| 40 |
+
Modules:
|
| 41 |
+
|
| 42 |
+
| module | description |
|
| 43 |
+
|:---|:---|
|
| 44 |
+
| `from graph import Graph, Graph3D` | Elementary methods (see basic methods below) for Graph and Graph3D.|
|
| 45 |
+
| `from graph import ...` | All methods available on Graph (see table below) |
|
| 46 |
+
| `from graph.assignment_problem import ...` | solvers for assignment problem, the Weapons-Target Assignment Problem, ... |
|
| 47 |
+
| `from graph.hash import ...` | graph hash functions: graph hash, merkle tree, flow graph hash |
|
| 48 |
+
| `from graph.random import ...` | graph generators for random, 2D and 3D graphs. |
|
| 49 |
+
| `from graph.transshipment_problem import ...` | solvers for the transshipment problem |
|
| 50 |
+
| `from graph.traffic_scheduling_problem import ...` | solvers for the traffic jams (and slide puzzle) |
|
| 51 |
+
| `from graph.visuals import ...` | methods for creating matplotlib plots |
|
| 52 |
+
| `from graph.finite_state_machine import ...` | finite state machine |
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
All module functions are available from Graph and Graph3D (where applicable).
|
| 56 |
+
|
| 57 |
+
| Graph | Graph3D | methods | returns | example |
|
| 58 |
+
|:---:|:---:|:-------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|
|
| 59 |
+
| + | + | `a in g` | assert if g contains node a | |
|
| 60 |
+
| + | + | `g.add_node(n, [obj])` | adds a node (with a pointer to object `obj` if given) ||
|
| 61 |
+
| + | + | `g.copy()` | returns a shallow copy of `g` ||
|
| 62 |
+
| + | + | `g.node(node1)` | returns object attached to node 1 ||
|
| 63 |
+
| + | + | `g.del_node(node1)` | deletes node1 and all it's edges ||
|
| 64 |
+
| + | + | `g.nodes()` | returns a list of nodes ||
|
| 65 |
+
| + | + | `len(g.nodes())` | returns the number of nodes ||
|
| 66 |
+
| + | + | `g.nodes(from_node=1)` | returns nodes with edges from node 1 ||
|
| 67 |
+
| + | + | `g.nodes(to_node=2)` | returns nodes with edges to node 2 ||
|
| 68 |
+
| + | + | `g.nodes(in_degree=2)` | returns nodes with 2 incoming edges ||
|
| 69 |
+
| + | + | `g.nodes(out_degree=2)` | returns nodes with 2 outgoing edges ||
|
| 70 |
+
| + | + | `g.add_edge(1,2,3)` | adds edge to g for vector `(1,2)` with value `3` ||
|
| 71 |
+
| + | + | `g.edge(1,2)` | returns value of edge between nodes 1 and 2 ||
|
| 72 |
+
| + | + | `g.edge(1,2,default=3)` | returns `default=3` if `edge(1,2)` doesn't exist. <br>similar to `d.get(key, 3)` ||
|
| 73 |
+
| + | + | `g.del_edge(1,2)` | removes edge between nodes 1 and 2 ||
|
| 74 |
+
| + | + | `g.edges()` | returns a list of edges ||
|
| 75 |
+
| + | + | `len(g.edges())` | returns the number of edges ||
|
| 76 |
+
| + | + | `g.edges(path=[path])` | returns a list of edges (along a path if given). ||
|
| 77 |
+
| + | + | `same_path(p1,p2)` | compares two paths to determine if they contain same sequences <br>ex.: `[1,2,3] == [2,3,1]` ||
|
| 78 |
+
| + | + | `g.edges(from_node=1)` | returns edges outgoing from node 1 ||
|
| 79 |
+
| + | + | `g.edges(to_node=2)` | returns edges incoming to node 2 ||
|
| 80 |
+
| + | + | `g.from_dict(d)` | updates the graph from a dictionary ||
|
| 81 |
+
| + | + | `g.to_dict()` | returns the graph as a dictionary ||
|
| 82 |
+
| + | + | `g.from_list(L)` | updates the graph from a list ||
|
| 83 |
+
| + | + | `g.to_list()` | return the graph as a list of edges ||
|
| 84 |
+
| + | + | `g.shortest_path(start,end [, memoize, avoids])` | returns the distance and path for path with smallest edge sum <br> If `memoize=True`, sub results are cached for faster access if repeated calls.<br> If `avoids=set()`, then these nodes are not a part of the path. ||
|
| 85 |
+
| + | + | `g.shortest_path_bidirectional(start,end)` | returns distance and path for the path with smallest edge sum using bidrectional search. ||
|
| 86 |
+
| + | + | `g.is_connected(start,end)` | determines if there is a path from start to end ||
|
| 87 |
+
| + | + | `g.breadth_first_search(start,end)` | returns the number of edges and path with fewest edges ||
|
| 88 |
+
| + | + | `g.breadth_first_walk(start,end)` | returns a generator for a BFS walk ||
|
| 89 |
+
| + | + | `g.degree_of_separation(n1,n2)` | returns the distance between two nodes using BFS ||
|
| 90 |
+
| + | + | `g.distance_map(starts,ends, reverse)` | returns a dictionary with the distance from any start to any end (or reverse) ||
|
| 91 |
+
| + | + | `g.network_size(n1, degree_of_separation)` | returns the nodes within the range given by `degree_of_separation` ||
|
| 92 |
+
| + | + | `g.topological_sort(key)` | returns a generator that yields node in order from a non-cyclic graph. ||
|
| 93 |
+
| + | + | `g.critical_path()` | returns the distance of the critical path and a list of Tasks. | [Example](examples/solving%20search%20problems.ipynb) |
|
| 94 |
+
| + | + | `g.critical_path_minimize_for_slack()` | returns graph with artificial dependencies that minimises slack. | [Example](examples/solving%20search%20problems.ipynb)|
|
| 95 |
+
| + | + | `g.phase_lines()` | returns a dictionary with the phase_lines for a non-cyclic graph. ||
|
| 96 |
+
| + | + | `g.sources(n)` | returns the source_tree of node `n` ||
|
| 97 |
+
| + | + | `g.depth_first_search(start,end)` | returns path using DFS and backtracking ||
|
| 98 |
+
| + | + | `g.depth_scan(start, criteria)` | returns set of nodes where criteria is True ||
|
| 99 |
+
| + | + | `g.distance_from_path(path)` | returns the distance for path. ||
|
| 100 |
+
| + | + | `g.maximum_flow(source,sink)` | finds the maximum flow between a source and a sink ||
|
| 101 |
+
| + | + | `g.maximum_flow_min_cut(source,sink)` | finds the maximum flow minimum cut between a source and a sink ||
|
| 102 |
+
| + | + | `g.minimum_cost_flow(inventory, capacity)` | finds the total cost and flows of the capacitated minimum cost flow. ||
|
| 103 |
+
| + | + | `g.solve_tsp()` | solves the traveling salesman problem for the graph.<br>Available methods: 'greedy' (default) and 'bnb ||
|
| 104 |
+
| + | + | `g.subgraph_from_nodes(nodes)` | returns the subgraph of `g` involving `nodes` ||
|
| 105 |
+
| + | + | `g.is_subgraph(g2)` | determines if graph `g2` is a subgraph in g ||
|
| 106 |
+
| + | + | `g.is_partite(n)` | determines if graph is n-partite ||
|
| 107 |
+
| + | + | `g.has_cycles()` | determines if there are any cycles in the graph ||
|
| 108 |
+
| + | + | `g.components()` | returns set of nodes in each component in `g` ||
|
| 109 |
+
| + | + | `g.same_path(p1,p2)` | compares two paths, returns True if they're the same ||
|
| 110 |
+
| + | + | `g.adjacency_matrix()` | returns the adjacency matrix for the graph ||
|
| 111 |
+
| + | + | `g.all_pairs_shortest_paths()` | finds the shortest path between all nodes ||
|
| 112 |
+
| + | + | `g.minsum()` | finds the node(s) with shortest total distance to all other nodes ||
|
| 113 |
+
| + | + | `g.minmax()` | finds the node(s) with shortest maximum distance to all other nodes ||
|
| 114 |
+
| + | + | `g.shortest_tree_all_pairs()` | finds the shortest tree for all pairs ||
|
| 115 |
+
| + | + | `g.has_path(p)` | asserts whether a path `p` exists in g ||
|
| 116 |
+
| + | + | `g.all_simple_paths(start,end)` | finds all simple paths between 2 nodes ||
|
| 117 |
+
| + | + | `g.all_paths(start,end)` | finds all combinations of paths between 2 nodes ||
|
| 118 |
+
| - | + | `g3d.distance(n1,n2)` | returns the spatial distance between `n1` and `n2` ||
|
| 119 |
+
| - | + | `g3d.n_nearest_neighbour(n1, [n])` | returns the `n` nearest neighbours to node `n1` ||
|
| 120 |
+
| - | + | `g3d.plot()` | returns matplotlib plot of the graph. ||
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
## FAQ
|
| 124 |
+
|
| 125 |
+
| want to... | doesn't work... | do instead... | ...but why? |
|
| 126 |
+
|:---|:---|:---|:---|
|
| 127 |
+
| have multiple edges between two nodes | `Graph(from_list=[(1,2,3), (1,2,4)]` | Add dummy nodes<br>`[(1,a,3), (a,2,0),`<br>` (1,b,4),(b,2,0)]` | Explicit is better than implicit. |
|
| 128 |
+
| multiple values on an edge | `g.add_edge(1,2,{'a':3, 'b':4})` | Have two graphs<br>`g_a.add_edge(1,2,3)`<br>`g_b.add_edge(1,2,4)` | Most graph algorithms don't work with multiple values |
|
| 129 |
+
|do repeated calls to shortest path|`g.shortest_path(a,b)` is slow|Use `g.shortest_path(a,b,memoize=True)` instead|memoize uses bidirectional search and caches sub-results along the shortest path for future retrievals|
|
| 130 |
+
|
| 131 |
+
## Credits:
|
| 132 |
+
|
| 133 |
+
- Arturo Soucase for packaging and testing.
|
| 134 |
+
- Peter Norvig for inspiration on TSP from [pytudes](https://github.com/norvig/pytudes/blob/master/ipynb/TSP.ipynb).
|
| 135 |
+
- Harry Darby for the mountain river map.
|
| 136 |
+
- Kyle Downey for depth_scan algorithm.
|
| 137 |
+
- Ross Blandford for munich firebrigade centre -, traffic jam - and slide puzzle - test cases.
|
| 138 |
+
- Avi Kelman for type-tolerant search, and a number of micro optimizations.
|
| 139 |
+
- Joshua Crestone for all simple paths test.
|
| 140 |
+
- CodeMartyLikeYou for detecting a bug in `@memoize`
|
| 141 |
+
- Tom Carroll for detecting the bug in del_edge and inspiration for topological sort.
|
| 142 |
+
- Sappique for discovering bugs in `__eq__`, `copy` and `has_cycles`.
|
| 143 |
+
- joshinils for discovering bug where `graph.edges(from_node=0)` was interpreted as `False`.
|
| 144 |
+
|
graph-theory/source/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
graph-theory Project Package Initialization File
|
| 4 |
+
"""
|
graph-theory/source/datasets/readme.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# README
|
| 2 |
+
This folder is listed in .gitignore so that developers can have confidential datasets withouth checking them in.
|
| 3 |
+
|
graph-theory/source/examples/__init__.py
ADDED
|
File without changes
|
graph-theory/source/examples/basic graph theory.ipynb
ADDED
|
@@ -0,0 +1,941 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {
|
| 6 |
+
"pycharm": {
|
| 7 |
+
"name": "#%% md\n"
|
| 8 |
+
}
|
| 9 |
+
},
|
| 10 |
+
"source": [
|
| 11 |
+
"# Introduction to Graph-theory\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"[Graph-theory](https://en.wikipedia.org/wiki/Graph_theory) is the study of graphs,\n",
|
| 14 |
+
"which are mathematical structures used to model pairwise relations between objects.\n",
|
| 15 |
+
"\n",
|
| 16 |
+
"Graphs are among the most ubiquitous models of both natural and human-made structures.\n",
|
| 17 |
+
"They can model many types of relations and process dynamics in physical, biological\n",
|
| 18 |
+
"and social systems. In computer science, they can represent networks of communication,\n",
|
| 19 |
+
"data organization, computational devices, the flow of computation, etc.\n",
|
| 20 |
+
"Graphs are one of the principal objects of study in\n",
|
| 21 |
+
"[discrete mathematics](https://en.wikipedia.org/wiki/Discrete_mathematics).\n",
|
| 22 |
+
"\n",
|
| 23 |
+
"\n",
|
| 24 |
+
"\n",
|
| 25 |
+
"| alias | name used in<br>Graph-theory | description | syntax |\n",
|
| 26 |
+
"|---|:---:|---|---|\n",
|
| 27 |
+
"|node, vertice, point| **node** | an intersection of edges | `g.add_node('x')` |\n",
|
| 28 |
+
"| edge, line, link| **edge** | the line that connects intersections | `g.add_edge('a','b')`|\n",
|
| 29 |
+
"| the weight, cost or value of an edge | **value** | numeric value of an edge | `g.add_edge('a','b',value=4)`|\n",
|
| 30 |
+
"|in-degree | **in_degree**| the number of incoming edges | `g.in_degree('a')` |\n",
|
| 31 |
+
"|out-degree| **out_degree**| the number of outgoing edges| `g.out_degree('b')` |\n",
|
| 32 |
+
"|path| **path**| an ordered collection of nodes | (a list of nodes) |\n",
|
| 33 |
+
"|distance, length, costs| **---** | the sum of values of a set of edges | (number) |\n",
|
| 34 |
+
"| subgraph | **subgraph**| the nodes and edges of *g'* exists in *g* ||\n"
|
| 35 |
+
]
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"cell_type": "markdown",
|
| 39 |
+
"metadata": {},
|
| 40 |
+
"source": [
|
| 41 |
+
"To add nodes the syntax is simple"
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"cell_type": "code",
|
| 46 |
+
"execution_count": 1,
|
| 47 |
+
"metadata": {},
|
| 48 |
+
"outputs": [
|
| 49 |
+
{
|
| 50 |
+
"name": "stdout",
|
| 51 |
+
"output_type": "stream",
|
| 52 |
+
"text": [
|
| 53 |
+
"Graph(1 nodes, 0 edges)\n"
|
| 54 |
+
]
|
| 55 |
+
}
|
| 56 |
+
],
|
| 57 |
+
"source": [
|
| 58 |
+
"from graph import Graph\n",
|
| 59 |
+
"g = Graph()\n",
|
| 60 |
+
"g.add_node('C')\n",
|
| 61 |
+
"print(g)"
|
| 62 |
+
]
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"cell_type": "markdown",
|
| 66 |
+
"metadata": {},
|
| 67 |
+
"source": [
|
| 68 |
+
"To associate the node with an object, you can use the `obj` keyword like this:"
|
| 69 |
+
]
|
| 70 |
+
},
|
| 71 |
+
{
|
| 72 |
+
"cell_type": "code",
|
| 73 |
+
"execution_count": 2,
|
| 74 |
+
"metadata": {},
|
| 75 |
+
"outputs": [],
|
| 76 |
+
"source": [
|
| 77 |
+
"g.add_node('C', obj={'monty':'python'})"
|
| 78 |
+
]
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"cell_type": "markdown",
|
| 82 |
+
"metadata": {},
|
| 83 |
+
"source": [
|
| 84 |
+
"This save you from managing the objects externally and permits algorithms to use the object directly.\n",
|
| 85 |
+
"\n",
|
| 86 |
+
"To retrieve the object use:"
|
| 87 |
+
]
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"cell_type": "code",
|
| 91 |
+
"execution_count": 3,
|
| 92 |
+
"metadata": {},
|
| 93 |
+
"outputs": [
|
| 94 |
+
{
|
| 95 |
+
"data": {
|
| 96 |
+
"text/plain": [
|
| 97 |
+
"{'monty': 'python'}"
|
| 98 |
+
]
|
| 99 |
+
},
|
| 100 |
+
"execution_count": 3,
|
| 101 |
+
"metadata": {},
|
| 102 |
+
"output_type": "execute_result"
|
| 103 |
+
}
|
| 104 |
+
],
|
| 105 |
+
"source": [
|
| 106 |
+
"g.node('C')"
|
| 107 |
+
]
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"cell_type": "markdown",
|
| 111 |
+
"metadata": {},
|
| 112 |
+
"source": [
|
| 113 |
+
"And to delete the node use:\n"
|
| 114 |
+
]
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"cell_type": "code",
|
| 118 |
+
"execution_count": 4,
|
| 119 |
+
"metadata": {},
|
| 120 |
+
"outputs": [],
|
| 121 |
+
"source": [
|
| 122 |
+
"g.del_node('C')"
|
| 123 |
+
]
|
| 124 |
+
},
|
| 125 |
+
{
|
| 126 |
+
"cell_type": "markdown",
|
| 127 |
+
"metadata": {},
|
| 128 |
+
"source": [
|
| 129 |
+
"To add edges the syntax is equally simple:"
|
| 130 |
+
]
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"cell_type": "code",
|
| 134 |
+
"execution_count": 5,
|
| 135 |
+
"metadata": {
|
| 136 |
+
"jupyter": {
|
| 137 |
+
"outputs_hidden": false
|
| 138 |
+
},
|
| 139 |
+
"pycharm": {
|
| 140 |
+
"name": "#%%\n"
|
| 141 |
+
}
|
| 142 |
+
},
|
| 143 |
+
"outputs": [
|
| 144 |
+
{
|
| 145 |
+
"name": "stdout",
|
| 146 |
+
"output_type": "stream",
|
| 147 |
+
"text": [
|
| 148 |
+
"Graph(2 nodes, 1 edges)\n"
|
| 149 |
+
]
|
| 150 |
+
}
|
| 151 |
+
],
|
| 152 |
+
"source": [
|
| 153 |
+
"from graph import Graph\n",
|
| 154 |
+
"g = Graph()\n",
|
| 155 |
+
"g.add_edge('A', 'B', value=4)\n",
|
| 156 |
+
"print(g)"
|
| 157 |
+
]
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"cell_type": "markdown",
|
| 161 |
+
"metadata": {},
|
| 162 |
+
"source": [
|
| 163 |
+
"To get all edges use:"
|
| 164 |
+
]
|
| 165 |
+
},
|
| 166 |
+
{
|
| 167 |
+
"cell_type": "code",
|
| 168 |
+
"execution_count": 6,
|
| 169 |
+
"metadata": {
|
| 170 |
+
"jupyter": {
|
| 171 |
+
"outputs_hidden": false
|
| 172 |
+
},
|
| 173 |
+
"pycharm": {
|
| 174 |
+
"name": "#%%\n"
|
| 175 |
+
}
|
| 176 |
+
},
|
| 177 |
+
"outputs": [
|
| 178 |
+
{
|
| 179 |
+
"data": {
|
| 180 |
+
"text/plain": [
|
| 181 |
+
"[('A', 'B', 4)]"
|
| 182 |
+
]
|
| 183 |
+
},
|
| 184 |
+
"execution_count": 6,
|
| 185 |
+
"metadata": {},
|
| 186 |
+
"output_type": "execute_result"
|
| 187 |
+
}
|
| 188 |
+
],
|
| 189 |
+
"source": [
|
| 190 |
+
"g.edges()"
|
| 191 |
+
]
|
| 192 |
+
},
|
| 193 |
+
{
|
| 194 |
+
"cell_type": "markdown",
|
| 195 |
+
"metadata": {},
|
| 196 |
+
"source": [
|
| 197 |
+
"Or to view a specific edge `value`, use:"
|
| 198 |
+
]
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"cell_type": "code",
|
| 202 |
+
"execution_count": 7,
|
| 203 |
+
"metadata": {},
|
| 204 |
+
"outputs": [
|
| 205 |
+
{
|
| 206 |
+
"data": {
|
| 207 |
+
"text/plain": [
|
| 208 |
+
"4"
|
| 209 |
+
]
|
| 210 |
+
},
|
| 211 |
+
"execution_count": 7,
|
| 212 |
+
"metadata": {},
|
| 213 |
+
"output_type": "execute_result"
|
| 214 |
+
}
|
| 215 |
+
],
|
| 216 |
+
"source": [
|
| 217 |
+
"g.edge('A', 'B')"
|
| 218 |
+
]
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"cell_type": "markdown",
|
| 222 |
+
"metadata": {},
|
| 223 |
+
"source": [
|
| 224 |
+
"As you see, `g.edges()` returns a list of immutable tuples with `(from, to, value)`. \n",
|
| 225 |
+
"\n",
|
| 226 |
+
"Note that it isn't necessary to add the nodes first. When you use `g.add_edge(...)` the library will detect that the nodes haven't been created and add them quietly."
|
| 227 |
+
]
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"cell_type": "markdown",
|
| 231 |
+
"metadata": {
|
| 232 |
+
"pycharm": {
|
| 233 |
+
"name": "#%% md\n"
|
| 234 |
+
}
|
| 235 |
+
},
|
| 236 |
+
"source": [
|
| 237 |
+
"If you need to update the value on the edge just use `g.add_edge('A','B', value=10)` again.\n",
|
| 238 |
+
"The edge works like a dictionary, where the pair of node `'A'` and `'B'` is the key where the `10` is the value.\n",
|
| 239 |
+
"\n",
|
| 240 |
+
"In some cases it is not required for the graph to be directed. Mathematicians call this undirected.\n",
|
| 241 |
+
"In Python a principal philosophy is that *explicit is better than implicit*, so in graph-theory *undirected* is interpreted as bidirectional. To make this explicit when adding edges, either add an edge for both directions, or use the keyword `bidirectional` which is a helper for doing these to step in one step:\n",
|
| 242 |
+
"\n",
|
| 243 |
+
"|this... | ...is the same as this|\n",
|
| 244 |
+
"|---|---|\n",
|
| 245 |
+
"|`g.add_edge('A','B')`<br>`g.add_edge('B','A')`|`g.add_edge('A','B', bidirectional=True)`|\n",
|
| 246 |
+
"\n",
|
| 247 |
+
"In other cases multiple edges between nodes are required. Graph-theory allows this using\n",
|
| 248 |
+
"dummy nodes. Here's an example:"
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"cell_type": "markdown",
|
| 253 |
+
"metadata": {},
|
| 254 |
+
"source": [
|
| 255 |
+
"The graph will store the in- and -out-degree of every node when the edges are added and removed, so the in_degree and out_degree is available immediately e.g. ($O(1)$ [computational complexity](https://wiki.python.org/moin/TimeComplexity)). Here's an example:"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"cell_type": "code",
|
| 260 |
+
"execution_count": 8,
|
| 261 |
+
"metadata": {},
|
| 262 |
+
"outputs": [
|
| 263 |
+
{
|
| 264 |
+
"data": {
|
| 265 |
+
"text/plain": [
|
| 266 |
+
"0"
|
| 267 |
+
]
|
| 268 |
+
},
|
| 269 |
+
"execution_count": 8,
|
| 270 |
+
"metadata": {},
|
| 271 |
+
"output_type": "execute_result"
|
| 272 |
+
}
|
| 273 |
+
],
|
| 274 |
+
"source": [
|
| 275 |
+
"g.in_degree('A')"
|
| 276 |
+
]
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"cell_type": "code",
|
| 280 |
+
"execution_count": 9,
|
| 281 |
+
"metadata": {},
|
| 282 |
+
"outputs": [
|
| 283 |
+
{
|
| 284 |
+
"data": {
|
| 285 |
+
"text/plain": [
|
| 286 |
+
"1"
|
| 287 |
+
]
|
| 288 |
+
},
|
| 289 |
+
"execution_count": 9,
|
| 290 |
+
"metadata": {},
|
| 291 |
+
"output_type": "execute_result"
|
| 292 |
+
}
|
| 293 |
+
],
|
| 294 |
+
"source": [
|
| 295 |
+
"g.out_degree('A')"
|
| 296 |
+
]
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"cell_type": "markdown",
|
| 300 |
+
"metadata": {},
|
| 301 |
+
"source": [
|
| 302 |
+
"Finally, to delete an edge, simply use:"
|
| 303 |
+
]
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"cell_type": "code",
|
| 307 |
+
"execution_count": 10,
|
| 308 |
+
"metadata": {},
|
| 309 |
+
"outputs": [],
|
| 310 |
+
"source": [
|
| 311 |
+
"g.del_edge('A','B')"
|
| 312 |
+
]
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"cell_type": "markdown",
|
| 316 |
+
"metadata": {},
|
| 317 |
+
"source": [
|
| 318 |
+
"To replicate the graph in the image in the begging of this section, we will use a helper from the graphs initialization methods `from_list`:"
|
| 319 |
+
]
|
| 320 |
+
},
|
| 321 |
+
{
|
| 322 |
+
"cell_type": "code",
|
| 323 |
+
"execution_count": 11,
|
| 324 |
+
"metadata": {},
|
| 325 |
+
"outputs": [],
|
| 326 |
+
"source": [
|
| 327 |
+
"g = Graph(from_list=[(1,2),(2,3),(3,4),(4,5),(5,6),(5,1),(2,5)])"
|
| 328 |
+
]
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"cell_type": "markdown",
|
| 332 |
+
"metadata": {},
|
| 333 |
+
"source": [
|
| 334 |
+
"As we can't create a bidirectional graph implicitly, we can do so explicitly:"
|
| 335 |
+
]
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"cell_type": "code",
|
| 339 |
+
"execution_count": 12,
|
| 340 |
+
"metadata": {},
|
| 341 |
+
"outputs": [],
|
| 342 |
+
"source": [
|
| 343 |
+
"g.from_list([(end,start,value) for start,end,value in g.edges()])"
|
| 344 |
+
]
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"cell_type": "markdown",
|
| 348 |
+
"metadata": {},
|
| 349 |
+
"source": [
|
| 350 |
+
"I chose this example deliberately as it illustrates the ability to update the graph from a list of nodes, even if that list comes from the graph itself. I also wanted to show the idiomatic use of list comprehensions for reading `start`, `end`, `value` from the method `g.edges()`.\n",
|
| 351 |
+
"\n",
|
| 352 |
+
"The reverse operation `to_list` also exists:"
|
| 353 |
+
]
|
| 354 |
+
},
|
| 355 |
+
{
|
| 356 |
+
"cell_type": "code",
|
| 357 |
+
"execution_count": 13,
|
| 358 |
+
"metadata": {},
|
| 359 |
+
"outputs": [
|
| 360 |
+
{
|
| 361 |
+
"data": {
|
| 362 |
+
"text/plain": [
|
| 363 |
+
"[(1, 2, 1),\n",
|
| 364 |
+
" (1, 5, 1),\n",
|
| 365 |
+
" (2, 3, 1),\n",
|
| 366 |
+
" (2, 5, 1),\n",
|
| 367 |
+
" (2, 1, 1),\n",
|
| 368 |
+
" (3, 4, 1),\n",
|
| 369 |
+
" (3, 2, 1),\n",
|
| 370 |
+
" (4, 5, 1),\n",
|
| 371 |
+
" (4, 3, 1),\n",
|
| 372 |
+
" (5, 6, 1),\n",
|
| 373 |
+
" (5, 1, 1),\n",
|
| 374 |
+
" (5, 2, 1),\n",
|
| 375 |
+
" (5, 4, 1),\n",
|
| 376 |
+
" (6, 5, 1),\n",
|
| 377 |
+
" (1,),\n",
|
| 378 |
+
" (2,),\n",
|
| 379 |
+
" (3,),\n",
|
| 380 |
+
" (4,),\n",
|
| 381 |
+
" (5,),\n",
|
| 382 |
+
" (6,)]"
|
| 383 |
+
]
|
| 384 |
+
},
|
| 385 |
+
"execution_count": 13,
|
| 386 |
+
"metadata": {},
|
| 387 |
+
"output_type": "execute_result"
|
| 388 |
+
}
|
| 389 |
+
],
|
| 390 |
+
"source": [
|
| 391 |
+
"g.to_list()"
|
| 392 |
+
]
|
| 393 |
+
},
|
| 394 |
+
{
|
| 395 |
+
"cell_type": "markdown",
|
| 396 |
+
"metadata": {},
|
| 397 |
+
"source": [
|
| 398 |
+
"The method lists all edges first, followed by all nodes"
|
| 399 |
+
]
|
| 400 |
+
},
|
| 401 |
+
{
|
| 402 |
+
"cell_type": "markdown",
|
| 403 |
+
"metadata": {},
|
| 404 |
+
"source": [
|
| 405 |
+
"If, you have the need to lookup particular edges or values directly `to_dict` may be more convenient:"
|
| 406 |
+
]
|
| 407 |
+
},
|
| 408 |
+
{
|
| 409 |
+
"cell_type": "code",
|
| 410 |
+
"execution_count": 14,
|
| 411 |
+
"metadata": {},
|
| 412 |
+
"outputs": [
|
| 413 |
+
{
|
| 414 |
+
"data": {
|
| 415 |
+
"text/plain": [
|
| 416 |
+
"{1: {2: 1, 5: 1},\n",
|
| 417 |
+
" 2: {3: 1, 5: 1, 1: 1},\n",
|
| 418 |
+
" 3: {4: 1, 2: 1},\n",
|
| 419 |
+
" 4: {5: 1, 3: 1},\n",
|
| 420 |
+
" 5: {6: 1, 1: 1, 2: 1, 4: 1},\n",
|
| 421 |
+
" 6: {5: 1}}"
|
| 422 |
+
]
|
| 423 |
+
},
|
| 424 |
+
"execution_count": 14,
|
| 425 |
+
"metadata": {},
|
| 426 |
+
"output_type": "execute_result"
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
"source": [
|
| 430 |
+
"g.to_dict()"
|
| 431 |
+
]
|
| 432 |
+
},
|
| 433 |
+
{
|
| 434 |
+
"cell_type": "markdown",
|
| 435 |
+
"metadata": {},
|
| 436 |
+
"source": [
|
| 437 |
+
"`to_dict`s inverse operation is `from_dict`, which behaves in the exact same way as `to_list`/`from_list`:"
|
| 438 |
+
]
|
| 439 |
+
},
|
| 440 |
+
{
|
| 441 |
+
"cell_type": "code",
|
| 442 |
+
"execution_count": 15,
|
| 443 |
+
"metadata": {},
|
| 444 |
+
"outputs": [],
|
| 445 |
+
"source": [
|
| 446 |
+
"g2 = Graph(from_dict=g.to_dict())"
|
| 447 |
+
]
|
| 448 |
+
},
|
| 449 |
+
{
|
| 450 |
+
"cell_type": "markdown",
|
| 451 |
+
"metadata": {},
|
| 452 |
+
"source": [
|
| 453 |
+
"Here we created `g2` from `g`."
|
| 454 |
+
]
|
| 455 |
+
},
|
| 456 |
+
{
|
| 457 |
+
"cell_type": "code",
|
| 458 |
+
"execution_count": 16,
|
| 459 |
+
"metadata": {},
|
| 460 |
+
"outputs": [
|
| 461 |
+
{
|
| 462 |
+
"data": {
|
| 463 |
+
"text/plain": [
|
| 464 |
+
"{1: {2: 1, 5: 1},\n",
|
| 465 |
+
" 2: {3: 1, 5: 1, 1: 1},\n",
|
| 466 |
+
" 5: {6: 1, 1: 1, 2: 1, 4: 1},\n",
|
| 467 |
+
" 3: {4: 1, 2: 1},\n",
|
| 468 |
+
" 4: {5: 1, 3: 1},\n",
|
| 469 |
+
" 6: {5: 1}}"
|
| 470 |
+
]
|
| 471 |
+
},
|
| 472 |
+
"execution_count": 16,
|
| 473 |
+
"metadata": {},
|
| 474 |
+
"output_type": "execute_result"
|
| 475 |
+
}
|
| 476 |
+
],
|
| 477 |
+
"source": [
|
| 478 |
+
"g2.to_dict()"
|
| 479 |
+
]
|
| 480 |
+
},
|
| 481 |
+
{
|
| 482 |
+
"cell_type": "markdown",
|
| 483 |
+
"metadata": {},
|
| 484 |
+
"source": [
|
| 485 |
+
"This may seem as a long way to copy when the libray already has the method `g.copy()`. The difference between the two methods, though, is that `g2=Graph(from_list(g.to_list()))` does not copy the reference to objects that you may have set using `g.add_node('a', obj='!!!')`. `g.copy()` will copy there reference too."
|
| 486 |
+
]
|
| 487 |
+
},
|
| 488 |
+
{
|
| 489 |
+
"cell_type": "code",
|
| 490 |
+
"execution_count": 17,
|
| 491 |
+
"metadata": {
|
| 492 |
+
"jupyter": {
|
| 493 |
+
"outputs_hidden": false
|
| 494 |
+
},
|
| 495 |
+
"pycharm": {
|
| 496 |
+
"name": "#%%\n"
|
| 497 |
+
}
|
| 498 |
+
},
|
| 499 |
+
"outputs": [],
|
| 500 |
+
"source": [
|
| 501 |
+
"from graph import Graph\n",
|
| 502 |
+
"\n",
|
| 503 |
+
"g = Graph()\n",
|
| 504 |
+
"g.add_edge('A', 'B', value=4) # a direct edge\n",
|
| 505 |
+
"g.add_edge('A', 'ab', value=2) # dummy edge from A to dummy node ab\n",
|
| 506 |
+
"g.add_edge('ab', 'B', value=2) # dummy edge from dummy node ab to B"
|
| 507 |
+
]
|
| 508 |
+
},
|
| 509 |
+
{
|
| 510 |
+
"cell_type": "markdown",
|
| 511 |
+
"metadata": {
|
| 512 |
+
"pycharm": {
|
| 513 |
+
"name": "#%% md\n"
|
| 514 |
+
}
|
| 515 |
+
},
|
| 516 |
+
"source": [
|
| 517 |
+
"Creating these dummy nodes by hand isn't very effective, so if you have a list of edges like in the example below it is good to know that nodes can be any hashable object, e.g. tuples, strings, numbers, ..."
|
| 518 |
+
]
|
| 519 |
+
},
|
| 520 |
+
{
|
| 521 |
+
"cell_type": "code",
|
| 522 |
+
"execution_count": 18,
|
| 523 |
+
"metadata": {
|
| 524 |
+
"jupyter": {
|
| 525 |
+
"outputs_hidden": false
|
| 526 |
+
},
|
| 527 |
+
"pycharm": {
|
| 528 |
+
"name": "#%%\n"
|
| 529 |
+
}
|
| 530 |
+
},
|
| 531 |
+
"outputs": [
|
| 532 |
+
{
|
| 533 |
+
"name": "stdout",
|
| 534 |
+
"output_type": "stream",
|
| 535 |
+
"text": [
|
| 536 |
+
"('A', ('A', 'B', 4), 4)\n",
|
| 537 |
+
"('A', ('A', 'B', 3), 3)\n",
|
| 538 |
+
"('A', ('A', 'B', 2), 2)\n",
|
| 539 |
+
"(('A', 'B', 4), 'B', 0)\n",
|
| 540 |
+
"(('A', 'B', 3), 'B', 0)\n",
|
| 541 |
+
"(('A', 'B', 2), 'B', 0)\n"
|
| 542 |
+
]
|
| 543 |
+
}
|
| 544 |
+
],
|
| 545 |
+
"source": [
|
| 546 |
+
"L = [('A','B',4), ('A','B',3), ('A','B',2)]\n",
|
| 547 |
+
"g = Graph()\n",
|
| 548 |
+
"for edge in L:\n",
|
| 549 |
+
" start,end,value = edge # using the edge tuple as dummy node.\n",
|
| 550 |
+
" g.add_edge(start, edge, value) # adding the value on the way to the dummy node.\n",
|
| 551 |
+
" g.add_edge(edge, end, 0) # adding zero as the value has already been added.\n",
|
| 552 |
+
"\n",
|
| 553 |
+
"for edge in g.edges():\n",
|
| 554 |
+
" print(edge)"
|
| 555 |
+
]
|
| 556 |
+
},
|
| 557 |
+
{
|
| 558 |
+
"cell_type": "markdown",
|
| 559 |
+
"metadata": {
|
| 560 |
+
"pycharm": {
|
| 561 |
+
"name": "#%% md\n"
|
| 562 |
+
}
|
| 563 |
+
},
|
| 564 |
+
"source": [
|
| 565 |
+
"The benefit of doing this is that most algorithms are simpler to implement (and understand!) when dummy nodes are explicit. As an outset you will always know your list of \"real\" nodes and can hence exclude dummy nodes when reading through the list of nodes. It would for example be silly to search for a dummy node when looking for the shortest path between two \"real\" nodes, as it is a pragmatic assumption that the algorithm will only have to search for the shortest path between two meaningful points."
|
| 566 |
+
]
|
| 567 |
+
},
|
| 568 |
+
{
|
| 569 |
+
"cell_type": "markdown",
|
| 570 |
+
"metadata": {
|
| 571 |
+
"pycharm": {
|
| 572 |
+
"name": "#%% md\n"
|
| 573 |
+
}
|
| 574 |
+
},
|
| 575 |
+
"source": [
|
| 576 |
+
"### Inspecting the source code\n",
|
| 577 |
+
"\n",
|
| 578 |
+
"Since we are at this, you can count on all algorithms being available directly on the `Graph` class,\n",
|
| 579 |
+
"so that, for example the shortest path algorithm is available as:\n",
|
| 580 |
+
"\n",
|
| 581 |
+
"```\n",
|
| 582 |
+
"g.shortest_path('A','B')\n",
|
| 583 |
+
"```\n",
|
| 584 |
+
"\n",
|
| 585 |
+
"All the documentation is also available using the built-in help menu:"
|
| 586 |
+
]
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
"cell_type": "code",
|
| 590 |
+
"execution_count": 19,
|
| 591 |
+
"metadata": {
|
| 592 |
+
"jupyter": {
|
| 593 |
+
"outputs_hidden": false
|
| 594 |
+
},
|
| 595 |
+
"pycharm": {
|
| 596 |
+
"name": "#%%\n"
|
| 597 |
+
}
|
| 598 |
+
},
|
| 599 |
+
"outputs": [
|
| 600 |
+
{
|
| 601 |
+
"name": "stdout",
|
| 602 |
+
"output_type": "stream",
|
| 603 |
+
"text": [
|
| 604 |
+
"Help on function shortest_path in module graph:\n",
|
| 605 |
+
"\n",
|
| 606 |
+
"shortest_path(self, start, end, memoize=False, avoids=None)\n",
|
| 607 |
+
" :param start: start node\n",
|
| 608 |
+
" :param end: end node\n",
|
| 609 |
+
" :param memoize: boolean (stores paths in a cache for faster repeated lookup)\n",
|
| 610 |
+
" :param avoids: optional. A frozen set of nodes that cannot be on the path.\n",
|
| 611 |
+
" :return: distance, path as list\n",
|
| 612 |
+
"\n"
|
| 613 |
+
]
|
| 614 |
+
}
|
| 615 |
+
],
|
| 616 |
+
"source": [
|
| 617 |
+
"help(Graph.shortest_path)"
|
| 618 |
+
]
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
"cell_type": "markdown",
|
| 622 |
+
"metadata": {},
|
| 623 |
+
"source": [
|
| 624 |
+
"If you want a more detail description of what is going on, it is often helpful to directly to the specific function."
|
| 625 |
+
]
|
| 626 |
+
},
|
| 627 |
+
{
|
| 628 |
+
"cell_type": "code",
|
| 629 |
+
"execution_count": 20,
|
| 630 |
+
"metadata": {},
|
| 631 |
+
"outputs": [],
|
| 632 |
+
"source": [
|
| 633 |
+
"import inspect"
|
| 634 |
+
]
|
| 635 |
+
},
|
| 636 |
+
{
|
| 637 |
+
"cell_type": "code",
|
| 638 |
+
"execution_count": 21,
|
| 639 |
+
"metadata": {},
|
| 640 |
+
"outputs": [
|
| 641 |
+
{
|
| 642 |
+
"name": "stdout",
|
| 643 |
+
"output_type": "stream",
|
| 644 |
+
"text": [
|
| 645 |
+
" def shortest_path(self, start, end, memoize=False, avoids=None):\n",
|
| 646 |
+
" \"\"\"\n",
|
| 647 |
+
" :param start: start node\n",
|
| 648 |
+
" :param end: end node\n",
|
| 649 |
+
" :param memoize: boolean (stores paths in a cache for faster repeated lookup)\n",
|
| 650 |
+
" :param avoids: optional. A frozen set of nodes that cannot be on the path.\n",
|
| 651 |
+
" :return: distance, path as list\n",
|
| 652 |
+
" \"\"\"\n",
|
| 653 |
+
" if not memoize:\n",
|
| 654 |
+
" return shortest_path(graph=self, start=start, end=end, avoids=avoids)\n",
|
| 655 |
+
"\n",
|
| 656 |
+
" if self._cache is None:\n",
|
| 657 |
+
" self._cache = ShortestPathCache(graph=self)\n",
|
| 658 |
+
" return self._cache.shortest_path(start, end, avoids=avoids)\n",
|
| 659 |
+
"\n"
|
| 660 |
+
]
|
| 661 |
+
}
|
| 662 |
+
],
|
| 663 |
+
"source": [
|
| 664 |
+
"print(inspect.getsource(Graph.shortest_path))"
|
| 665 |
+
]
|
| 666 |
+
},
|
| 667 |
+
{
|
| 668 |
+
"cell_type": "markdown",
|
| 669 |
+
"metadata": {},
|
| 670 |
+
"source": [
|
| 671 |
+
"Here you see that Graph wraps the function `shortest_path` and, should you choose to use the keyword `memoize=True` that it uses the class `ShortestPathCache`.\n",
|
| 672 |
+
"\n",
|
| 673 |
+
"These can be inspected again in the same way:"
|
| 674 |
+
]
|
| 675 |
+
},
|
| 676 |
+
{
|
| 677 |
+
"cell_type": "code",
|
| 678 |
+
"execution_count": 22,
|
| 679 |
+
"metadata": {},
|
| 680 |
+
"outputs": [
|
| 681 |
+
{
|
| 682 |
+
"name": "stdout",
|
| 683 |
+
"output_type": "stream",
|
| 684 |
+
"text": [
|
| 685 |
+
"def shortest_path(graph, start, end, avoids=None):\n",
|
| 686 |
+
" \"\"\" single source shortest path algorithm.\n",
|
| 687 |
+
" :param graph: class Graph\n",
|
| 688 |
+
" :param start: start node\n",
|
| 689 |
+
" :param end: end node\n",
|
| 690 |
+
" :param avoids: optional set,frozenset or list of nodes that cannot be a part of the path.\n",
|
| 691 |
+
" :return distance, path (as list),\n",
|
| 692 |
+
" returns float('inf'), [] if no path exists.\n",
|
| 693 |
+
" \"\"\"\n",
|
| 694 |
+
" if not isinstance(graph, (BasicGraph, Graph, Graph3D)):\n",
|
| 695 |
+
" raise TypeError(f\"Expected BasicGraph, Graph or Graph3D, not {type(graph)}\")\n",
|
| 696 |
+
" if start not in graph:\n",
|
| 697 |
+
" raise ValueError(f\"{start} not in graph\")\n",
|
| 698 |
+
" if end not in graph:\n",
|
| 699 |
+
" raise ValueError(f\"{end} not in graph\")\n",
|
| 700 |
+
" if avoids is None:\n",
|
| 701 |
+
" visited = set()\n",
|
| 702 |
+
" elif not isinstance(avoids, (frozenset, set, list)):\n",
|
| 703 |
+
" raise TypeError(f\"Expect obstacles as set or frozenset, not {type(avoids)}\")\n",
|
| 704 |
+
" else:\n",
|
| 705 |
+
" visited = set(avoids)\n",
|
| 706 |
+
"\n",
|
| 707 |
+
" q, minimums = [(0, 0, start, ())], {start: 0}\n",
|
| 708 |
+
" i = 1\n",
|
| 709 |
+
" while q:\n",
|
| 710 |
+
" (cost, _, v1, path) = heappop(q)\n",
|
| 711 |
+
" if v1 not in visited:\n",
|
| 712 |
+
" visited.add(v1)\n",
|
| 713 |
+
" path = (v1, path)\n",
|
| 714 |
+
"\n",
|
| 715 |
+
" if v1 == end: # exit criteria.\n",
|
| 716 |
+
" L = []\n",
|
| 717 |
+
" while path:\n",
|
| 718 |
+
" v, path = path[0], path[1]\n",
|
| 719 |
+
" L.append(v)\n",
|
| 720 |
+
" L.reverse()\n",
|
| 721 |
+
" return cost, L\n",
|
| 722 |
+
"\n",
|
| 723 |
+
" for _, v2, dist in graph.edges(from_node=v1):\n",
|
| 724 |
+
" if v2 in visited:\n",
|
| 725 |
+
" continue\n",
|
| 726 |
+
" prev = minimums.get(v2, None)\n",
|
| 727 |
+
" next_node = cost + dist\n",
|
| 728 |
+
" if prev is None or next_node < prev:\n",
|
| 729 |
+
" minimums[v2] = next_node\n",
|
| 730 |
+
" heappush(q, (next_node, i, v2, path))\n",
|
| 731 |
+
" i += 1\n",
|
| 732 |
+
" return float(\"inf\"), []\n",
|
| 733 |
+
"\n"
|
| 734 |
+
]
|
| 735 |
+
}
|
| 736 |
+
],
|
| 737 |
+
"source": [
|
| 738 |
+
"from graph import shortest_path # getting the function behind Graph.shortest_path\n",
|
| 739 |
+
"print(inspect.getsource(shortest_path)) # viewing the code"
|
| 740 |
+
]
|
| 741 |
+
},
|
| 742 |
+
{
|
| 743 |
+
"cell_type": "markdown",
|
| 744 |
+
"metadata": {},
|
| 745 |
+
"source": [
|
| 746 |
+
"The code is very well annotated, so if the code doesn't explain itself, feel free to ask for a more elaborate example on the [github repo](https://github.com/root-11/graph-theory/issues).\n",
|
| 747 |
+
"\n",
|
| 748 |
+
"Graph-theory tries to be transparent about everything it does. As the readme on the frontpage says: \n",
|
| 749 |
+
"\n",
|
| 750 |
+
"> with code you can explain to your boss"
|
| 751 |
+
]
|
| 752 |
+
},
|
| 753 |
+
{
|
| 754 |
+
"cell_type": "markdown",
|
| 755 |
+
"metadata": {},
|
| 756 |
+
"source": [
|
| 757 |
+
"### Graphs ready for usage"
|
| 758 |
+
]
|
| 759 |
+
},
|
| 760 |
+
{
|
| 761 |
+
"cell_type": "markdown",
|
| 762 |
+
"metadata": {},
|
| 763 |
+
"source": [
|
| 764 |
+
"To ease the learning curve, I've added a collection of graphs that are ready for import.\n",
|
| 765 |
+
"Here's one that should be familiar to most Europeans:\n",
|
| 766 |
+
"\n",
|
| 767 |
+
""
|
| 768 |
+
]
|
| 769 |
+
},
|
| 770 |
+
{
|
| 771 |
+
"cell_type": "code",
|
| 772 |
+
"execution_count": 23,
|
| 773 |
+
"metadata": {},
|
| 774 |
+
"outputs": [],
|
| 775 |
+
"source": [
|
| 776 |
+
"from graphs import london_underground"
|
| 777 |
+
]
|
| 778 |
+
},
|
| 779 |
+
{
|
| 780 |
+
"cell_type": "code",
|
| 781 |
+
"execution_count": 24,
|
| 782 |
+
"metadata": {},
|
| 783 |
+
"outputs": [
|
| 784 |
+
{
|
| 785 |
+
"name": "stdout",
|
| 786 |
+
"output_type": "stream",
|
| 787 |
+
"text": [
|
| 788 |
+
"Graph(302 nodes, 698 edges)\n"
|
| 789 |
+
]
|
| 790 |
+
}
|
| 791 |
+
],
|
| 792 |
+
"source": [
|
| 793 |
+
"g = london_underground()\n",
|
| 794 |
+
"print(g)"
|
| 795 |
+
]
|
| 796 |
+
},
|
| 797 |
+
{
|
| 798 |
+
"cell_type": "markdown",
|
| 799 |
+
"metadata": {},
|
| 800 |
+
"source": [
|
| 801 |
+
"Each node in the graph is the latitude, longitude and station name"
|
| 802 |
+
]
|
| 803 |
+
},
|
| 804 |
+
{
|
| 805 |
+
"cell_type": "code",
|
| 806 |
+
"execution_count": 25,
|
| 807 |
+
"metadata": {},
|
| 808 |
+
"outputs": [
|
| 809 |
+
{
|
| 810 |
+
"data": {
|
| 811 |
+
"text/plain": [
|
| 812 |
+
"(51.5226, -0.1571, 'Baker Street')"
|
| 813 |
+
]
|
| 814 |
+
},
|
| 815 |
+
"execution_count": 25,
|
| 816 |
+
"metadata": {},
|
| 817 |
+
"output_type": "execute_result"
|
| 818 |
+
}
|
| 819 |
+
],
|
| 820 |
+
"source": [
|
| 821 |
+
"g.node(11)"
|
| 822 |
+
]
|
| 823 |
+
},
|
| 824 |
+
{
|
| 825 |
+
"cell_type": "code",
|
| 826 |
+
"execution_count": 26,
|
| 827 |
+
"metadata": {},
|
| 828 |
+
"outputs": [
|
| 829 |
+
{
|
| 830 |
+
"name": "stdout",
|
| 831 |
+
"output_type": "stream",
|
| 832 |
+
"text": [
|
| 833 |
+
"(51.5225, -0.1631, 'Marylebone')\n",
|
| 834 |
+
"(51.5234, -0.1466, \"Regent's Park\")\n",
|
| 835 |
+
"(51.5203, -0.17, 'Edgware Road (C)')\n",
|
| 836 |
+
"(51.5238, -0.1439, 'Great Portland Street')\n",
|
| 837 |
+
"(51.5142, -0.1494, 'Bond Street')\n",
|
| 838 |
+
"(51.5347, -0.174, \"St. John's Wood\")\n",
|
| 839 |
+
"(51.5472, -0.1803, 'Finchley Road')\n"
|
| 840 |
+
]
|
| 841 |
+
}
|
| 842 |
+
],
|
| 843 |
+
"source": [
|
| 844 |
+
"for station_nr in g.nodes(to_node=11):\n",
|
| 845 |
+
" print(g.node(station_nr))"
|
| 846 |
+
]
|
| 847 |
+
},
|
| 848 |
+
{
|
| 849 |
+
"cell_type": "markdown",
|
| 850 |
+
"metadata": {},
|
| 851 |
+
"source": [
|
| 852 |
+
"Let's make a map"
|
| 853 |
+
]
|
| 854 |
+
},
|
| 855 |
+
{
|
| 856 |
+
"cell_type": "code",
|
| 857 |
+
"execution_count": 27,
|
| 858 |
+
"metadata": {},
|
| 859 |
+
"outputs": [
|
| 860 |
+
{
|
| 861 |
+
"data": {
|
| 862 |
+
"text/plain": [
|
| 863 |
+
"[(-0.1571, 51.5226), (-0.1631, 51.5225), (-0.1466, 51.5234)]"
|
| 864 |
+
]
|
| 865 |
+
},
|
| 866 |
+
"execution_count": 27,
|
| 867 |
+
"metadata": {},
|
| 868 |
+
"output_type": "execute_result"
|
| 869 |
+
}
|
| 870 |
+
],
|
| 871 |
+
"source": [
|
| 872 |
+
"stations = {station: g.node(station) for station in g.nodes()}\n",
|
| 873 |
+
"london_map = Graph()\n",
|
| 874 |
+
"for start,end,distance in g.edges():\n",
|
| 875 |
+
" lat1,lon1,name1 = stations[start]\n",
|
| 876 |
+
" lat2,lon2,name2 = stations[end]\n",
|
| 877 |
+
" london_map.add_edge((lon1,lat1), (lon2,lat2))\n",
|
| 878 |
+
"\n",
|
| 879 |
+
"london_map.nodes()[:3]"
|
| 880 |
+
]
|
| 881 |
+
},
|
| 882 |
+
{
|
| 883 |
+
"cell_type": "code",
|
| 884 |
+
"execution_count": 28,
|
| 885 |
+
"metadata": {},
|
| 886 |
+
"outputs": [],
|
| 887 |
+
"source": [
|
| 888 |
+
"from graph.visuals import plot_2d"
|
| 889 |
+
]
|
| 890 |
+
},
|
| 891 |
+
{
|
| 892 |
+
"cell_type": "code",
|
| 893 |
+
"execution_count": 29,
|
| 894 |
+
"metadata": {},
|
| 895 |
+
"outputs": [
|
| 896 |
+
{
|
| 897 |
+
"data": {
|
| 898 |
+
"image/png": "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",
|
| 899 |
+
"text/plain": [
|
| 900 |
+
"<Figure size 640x480 with 1 Axes>"
|
| 901 |
+
]
|
| 902 |
+
},
|
| 903 |
+
"metadata": {},
|
| 904 |
+
"output_type": "display_data"
|
| 905 |
+
}
|
| 906 |
+
],
|
| 907 |
+
"source": [
|
| 908 |
+
"plot = plot_2d(london_map)\n",
|
| 909 |
+
"plot.show()"
|
| 910 |
+
]
|
| 911 |
+
},
|
| 912 |
+
{
|
| 913 |
+
"cell_type": "markdown",
|
| 914 |
+
"metadata": {},
|
| 915 |
+
"source": [
|
| 916 |
+
"Not exactly what you expected? Well there's a reason why the London overground map is famous for its readability in comparison to raw latitudes and longitudes."
|
| 917 |
+
]
|
| 918 |
+
}
|
| 919 |
+
],
|
| 920 |
+
"metadata": {
|
| 921 |
+
"kernelspec": {
|
| 922 |
+
"display_name": "Python 3 (ipykernel)",
|
| 923 |
+
"language": "python",
|
| 924 |
+
"name": "python3"
|
| 925 |
+
},
|
| 926 |
+
"language_info": {
|
| 927 |
+
"codemirror_mode": {
|
| 928 |
+
"name": "ipython",
|
| 929 |
+
"version": 3
|
| 930 |
+
},
|
| 931 |
+
"file_extension": ".py",
|
| 932 |
+
"mimetype": "text/x-python",
|
| 933 |
+
"name": "python",
|
| 934 |
+
"nbconvert_exporter": "python",
|
| 935 |
+
"pygments_lexer": "ipython3",
|
| 936 |
+
"version": "3.10.11"
|
| 937 |
+
}
|
| 938 |
+
},
|
| 939 |
+
"nbformat": 4,
|
| 940 |
+
"nbformat_minor": 4
|
| 941 |
+
}
|
graph-theory/source/examples/comparing graphs.ipynb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [],
|
| 8 |
+
"source": []
|
| 9 |
+
}
|
| 10 |
+
],
|
| 11 |
+
"metadata": {
|
| 12 |
+
"kernelspec": {
|
| 13 |
+
"display_name": "Python 3",
|
| 14 |
+
"language": "python",
|
| 15 |
+
"name": "python3"
|
| 16 |
+
},
|
| 17 |
+
"language_info": {
|
| 18 |
+
"codemirror_mode": {
|
| 19 |
+
"name": "ipython",
|
| 20 |
+
"version": 3
|
| 21 |
+
},
|
| 22 |
+
"file_extension": ".py",
|
| 23 |
+
"mimetype": "text/x-python",
|
| 24 |
+
"name": "python",
|
| 25 |
+
"nbconvert_exporter": "python",
|
| 26 |
+
"pygments_lexer": "ipython3",
|
| 27 |
+
"version": "3.8.5"
|
| 28 |
+
}
|
| 29 |
+
},
|
| 30 |
+
"nbformat": 4,
|
| 31 |
+
"nbformat_minor": 4
|
| 32 |
+
}
|
graph-theory/source/examples/generating and visualising graphs.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
graph-theory/source/examples/graphs as finite state machines.ipynb
ADDED
|
@@ -0,0 +1,848 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {
|
| 6 |
+
"collapsed": true
|
| 7 |
+
},
|
| 8 |
+
"source": [
|
| 9 |
+
"# Finite State Machines \n",
|
| 10 |
+
"\n",
|
| 11 |
+
"Overview\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"1. Fundamentals\n",
|
| 14 |
+
"2. Turnstile\n",
|
| 15 |
+
"3. Traffic Jam\n",
|
| 16 |
+
"4. Sudoku\n",
|
| 17 |
+
"\n",
|
| 18 |
+
"But first we must be ready:"
|
| 19 |
+
]
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"cell_type": "code",
|
| 23 |
+
"execution_count": 2,
|
| 24 |
+
"metadata": {},
|
| 25 |
+
"outputs": [
|
| 26 |
+
{
|
| 27 |
+
"name": "stdout",
|
| 28 |
+
"output_type": "stream",
|
| 29 |
+
"text": [
|
| 30 |
+
"Note: you may need to restart the kernel to use updated packages.\n"
|
| 31 |
+
]
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"name": "stderr",
|
| 35 |
+
"output_type": "stream",
|
| 36 |
+
"text": [
|
| 37 |
+
"\n",
|
| 38 |
+
"[notice] A new release of pip available: 22.2.2 -> 23.1.2\n",
|
| 39 |
+
"[notice] To update, run: python.exe -m pip install --upgrade pip\n"
|
| 40 |
+
]
|
| 41 |
+
}
|
| 42 |
+
],
|
| 43 |
+
"source": [
|
| 44 |
+
"import sys\n",
|
| 45 |
+
"assert (sys.version_info.major, sys.version_info.minor) >= (3,7)\n",
|
| 46 |
+
"%pip install graph-theory --upgrade --no-cache -q\n",
|
| 47 |
+
"from graph import Graph"
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"cell_type": "markdown",
|
| 52 |
+
"metadata": {},
|
| 53 |
+
"source": [
|
| 54 |
+
"## Fundamentals\n",
|
| 55 |
+
"\n",
|
| 56 |
+
"Nomenclature\n",
|
| 57 |
+
"\n",
|
| 58 |
+
"- A finite state machine is a mathematical model of computation. \n",
|
| 59 |
+
"- The model is in exactly one state at any time.\n",
|
| 60 |
+
"- The model can change from one state to another using predetermined paths.\n",
|
| 61 |
+
"- All possible states is called the \"solution landscape\"\n",
|
| 62 |
+
"\n"
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"cell_type": "markdown",
|
| 67 |
+
"metadata": {},
|
| 68 |
+
"source": [
|
| 69 |
+
"# Turnstile\n",
|
| 70 |
+
"\n",
|
| 71 |
+
"  "
|
| 72 |
+
]
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"cell_type": "code",
|
| 76 |
+
"execution_count": 3,
|
| 77 |
+
"metadata": {},
|
| 78 |
+
"outputs": [],
|
| 79 |
+
"source": [
|
| 80 |
+
"from graph.finite_state_machine import FiniteStateMachine\n",
|
| 81 |
+
"\n",
|
| 82 |
+
"locked, unlocked = 'locked', 'unlocked' # states\n",
|
| 83 |
+
"push, coin = 'push', 'coin' # actions\n",
|
| 84 |
+
"\n",
|
| 85 |
+
"fsm = FiniteStateMachine()\n",
|
| 86 |
+
"fsm.add_transition(locked, coin, unlocked) # turnstile is locked. Put in coin to unlock.\n",
|
| 87 |
+
"fsm.add_transition(unlocked, push, locked) # turnstile is unlocked. Push the rotor to lock.\n",
|
| 88 |
+
"fsm.add_transition(locked, push, locked) # turnstile is locked. Pushing does not unlock.\n",
|
| 89 |
+
"fsm.add_transition(unlocked, coin, unlocked) # turnstile is unlocked. Adding more coins does not change state.\n",
|
| 90 |
+
"\n",
|
| 91 |
+
"fsm.set_initial_state(locked) # set initial state."
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"cell_type": "code",
|
| 96 |
+
"execution_count": 4,
|
| 97 |
+
"metadata": {},
|
| 98 |
+
"outputs": [
|
| 99 |
+
{
|
| 100 |
+
"data": {
|
| 101 |
+
"text/plain": [
|
| 102 |
+
"{'coin', 'push'}"
|
| 103 |
+
]
|
| 104 |
+
},
|
| 105 |
+
"execution_count": 4,
|
| 106 |
+
"metadata": {},
|
| 107 |
+
"output_type": "execute_result"
|
| 108 |
+
}
|
| 109 |
+
],
|
| 110 |
+
"source": [
|
| 111 |
+
"# check options: pay and go\n",
|
| 112 |
+
"set(fsm.options())"
|
| 113 |
+
]
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"cell_type": "code",
|
| 117 |
+
"execution_count": 5,
|
| 118 |
+
"metadata": {},
|
| 119 |
+
"outputs": [
|
| 120 |
+
{
|
| 121 |
+
"data": {
|
| 122 |
+
"text/plain": [
|
| 123 |
+
"True"
|
| 124 |
+
]
|
| 125 |
+
},
|
| 126 |
+
"execution_count": 5,
|
| 127 |
+
"metadata": {},
|
| 128 |
+
"output_type": "execute_result"
|
| 129 |
+
}
|
| 130 |
+
],
|
| 131 |
+
"source": [
|
| 132 |
+
"fsm.current_state == locked"
|
| 133 |
+
]
|
| 134 |
+
},
|
| 135 |
+
{
|
| 136 |
+
"cell_type": "code",
|
| 137 |
+
"execution_count": 6,
|
| 138 |
+
"metadata": {},
|
| 139 |
+
"outputs": [],
|
| 140 |
+
"source": [
|
| 141 |
+
"# now insert coin:\n",
|
| 142 |
+
"fsm.next(action=coin)"
|
| 143 |
+
]
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"cell_type": "code",
|
| 147 |
+
"execution_count": 7,
|
| 148 |
+
"metadata": {},
|
| 149 |
+
"outputs": [
|
| 150 |
+
{
|
| 151 |
+
"data": {
|
| 152 |
+
"text/plain": [
|
| 153 |
+
"{'coin', 'push'}"
|
| 154 |
+
]
|
| 155 |
+
},
|
| 156 |
+
"execution_count": 7,
|
| 157 |
+
"metadata": {},
|
| 158 |
+
"output_type": "execute_result"
|
| 159 |
+
}
|
| 160 |
+
],
|
| 161 |
+
"source": [
|
| 162 |
+
"# check options:\n",
|
| 163 |
+
"set(fsm.options())"
|
| 164 |
+
]
|
| 165 |
+
},
|
| 166 |
+
{
|
| 167 |
+
"cell_type": "code",
|
| 168 |
+
"execution_count": 8,
|
| 169 |
+
"metadata": {},
|
| 170 |
+
"outputs": [
|
| 171 |
+
{
|
| 172 |
+
"data": {
|
| 173 |
+
"text/plain": [
|
| 174 |
+
"True"
|
| 175 |
+
]
|
| 176 |
+
},
|
| 177 |
+
"execution_count": 8,
|
| 178 |
+
"metadata": {},
|
| 179 |
+
"output_type": "execute_result"
|
| 180 |
+
}
|
| 181 |
+
],
|
| 182 |
+
"source": [
|
| 183 |
+
"fsm.current_state == unlocked"
|
| 184 |
+
]
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"cell_type": "code",
|
| 188 |
+
"execution_count": 9,
|
| 189 |
+
"metadata": {},
|
| 190 |
+
"outputs": [
|
| 191 |
+
{
|
| 192 |
+
"data": {
|
| 193 |
+
"text/plain": [
|
| 194 |
+
"True"
|
| 195 |
+
]
|
| 196 |
+
},
|
| 197 |
+
"execution_count": 9,
|
| 198 |
+
"metadata": {},
|
| 199 |
+
"output_type": "execute_result"
|
| 200 |
+
}
|
| 201 |
+
],
|
| 202 |
+
"source": [
|
| 203 |
+
"# ok it's unlocked - so we push:\n",
|
| 204 |
+
"fsm.next(action=push)\n",
|
| 205 |
+
"# and check if it locks behind us:\n",
|
| 206 |
+
"fsm.current_state == locked"
|
| 207 |
+
]
|
| 208 |
+
},
|
| 209 |
+
{
|
| 210 |
+
"cell_type": "markdown",
|
| 211 |
+
"metadata": {},
|
| 212 |
+
"source": [
|
| 213 |
+
"# Traffic Jam\n",
|
| 214 |
+
"\n",
|
| 215 |
+
"As finite state machines can be view as a model of *a valid state*, we can use to verify solutions to optimization problems.\n",
|
| 216 |
+
"\n",
|
| 217 |
+
"In the example below we have a small intersection like, where all the `red`s need to pass all the `blue`s. \n",
|
| 218 |
+
"\n",
|
| 219 |
+
"The order could matter, but we ignore that for now.\n",
|
| 220 |
+
"\n",
|
| 221 |
+
"\n",
|
| 222 |
+
"\n",
|
| 223 |
+
"(initial state)\n",
|
| 224 |
+
"\n",
|
| 225 |
+
"The solution we would like to see looks like this:\n",
|
| 226 |
+
"\n",
|
| 227 |
+
"\n",
|
| 228 |
+
"\n",
|
| 229 |
+
"(final state)\n",
|
| 230 |
+
"\n",
|
| 231 |
+
"And here's the solution that swaps all the blue and red tiles in fewest moves:\n",
|
| 232 |
+
"\n",
|
| 233 |
+
"\n",
|
| 234 |
+
"\n",
|
| 235 |
+
"Q: How did I get to that?\n",
|
| 236 |
+
"\n",
|
| 237 |
+
"A: 3 steps:\n",
|
| 238 |
+
"\n",
|
| 239 |
+
"1. Define the system as a FSM using a \"map\" of options.\n",
|
| 240 |
+
"2. Set the `initial state`\n",
|
| 241 |
+
"3. Search through the solution landscape until the `active state` resembles the `final state`.\n",
|
| 242 |
+
"\n",
|
| 243 |
+
"\n",
|
| 244 |
+
"We will now solve the same problem using a just a tiny subset.\n",
|
| 245 |
+
"\n",
|
| 246 |
+
"Let's start with the map:\n",
|
| 247 |
+
"\n",
|
| 248 |
+
""
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"cell_type": "code",
|
| 253 |
+
"execution_count": 10,
|
| 254 |
+
"metadata": {},
|
| 255 |
+
"outputs": [],
|
| 256 |
+
"source": [
|
| 257 |
+
"g = Graph()\n",
|
| 258 |
+
"edges = [(1,2),(2,3),(2,4)]\n",
|
| 259 |
+
"for edge in edges:\n",
|
| 260 |
+
" g.add_edge(*edge, bidirectional=True)"
|
| 261 |
+
]
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"cell_type": "markdown",
|
| 265 |
+
"metadata": {},
|
| 266 |
+
"source": [
|
| 267 |
+
"Next I add the tiles to represent the initial state and the final state:\n",
|
| 268 |
+
"\n",
|
| 269 |
+
""
|
| 270 |
+
]
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"cell_type": "code",
|
| 274 |
+
"execution_count": 11,
|
| 275 |
+
"metadata": {},
|
| 276 |
+
"outputs": [],
|
| 277 |
+
"source": [
|
| 278 |
+
"loads = [\n",
|
| 279 |
+
" {'id':'red', 'start': 1, 'ends': 3}, \n",
|
| 280 |
+
" {'id': 'blue', 'start': 3, 'ends': 1}\n",
|
| 281 |
+
"]"
|
| 282 |
+
]
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
"cell_type": "code",
|
| 286 |
+
"execution_count": 12,
|
| 287 |
+
"metadata": {},
|
| 288 |
+
"outputs": [],
|
| 289 |
+
"source": [
|
| 290 |
+
"from graph.traffic_scheduling_problem import jam_solver"
|
| 291 |
+
]
|
| 292 |
+
},
|
| 293 |
+
{
|
| 294 |
+
"cell_type": "code",
|
| 295 |
+
"execution_count": 13,
|
| 296 |
+
"metadata": {},
|
| 297 |
+
"outputs": [
|
| 298 |
+
{
|
| 299 |
+
"name": "stdout",
|
| 300 |
+
"output_type": "stream",
|
| 301 |
+
"text": [
|
| 302 |
+
"queue exhausted\n"
|
| 303 |
+
]
|
| 304 |
+
}
|
| 305 |
+
],
|
| 306 |
+
"source": [
|
| 307 |
+
"solution = jam_solver(g,loads)"
|
| 308 |
+
]
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
"cell_type": "code",
|
| 312 |
+
"execution_count": 14,
|
| 313 |
+
"metadata": {},
|
| 314 |
+
"outputs": [
|
| 315 |
+
{
|
| 316 |
+
"data": {
|
| 317 |
+
"text/plain": [
|
| 318 |
+
"[{'red': (1, 2)},\n",
|
| 319 |
+
" {'red': (2, 4), 'blue': (3, 2)},\n",
|
| 320 |
+
" {'blue': (2, 1), 'red': (4, 2)},\n",
|
| 321 |
+
" {'red': (2, 3)}]"
|
| 322 |
+
]
|
| 323 |
+
},
|
| 324 |
+
"execution_count": 14,
|
| 325 |
+
"metadata": {},
|
| 326 |
+
"output_type": "execute_result"
|
| 327 |
+
}
|
| 328 |
+
],
|
| 329 |
+
"source": [
|
| 330 |
+
"solution"
|
| 331 |
+
]
|
| 332 |
+
},
|
| 333 |
+
{
|
| 334 |
+
"cell_type": "code",
|
| 335 |
+
"execution_count": 15,
|
| 336 |
+
"metadata": {},
|
| 337 |
+
"outputs": [
|
| 338 |
+
{
|
| 339 |
+
"name": "stdout",
|
| 340 |
+
"output_type": "stream",
|
| 341 |
+
"text": [
|
| 342 |
+
"red moves from 1 to 2\n",
|
| 343 |
+
"red moves from 2 to 4\n",
|
| 344 |
+
"blue moves from 3 to 2\n",
|
| 345 |
+
"blue moves from 2 to 1\n",
|
| 346 |
+
"red moves from 4 to 2\n",
|
| 347 |
+
"red moves from 2 to 3\n"
|
| 348 |
+
]
|
| 349 |
+
}
|
| 350 |
+
],
|
| 351 |
+
"source": [
|
| 352 |
+
"# in plain english:\n",
|
| 353 |
+
"for move in solution:\n",
|
| 354 |
+
" for color,(start,end) in move.items():\n",
|
| 355 |
+
" print(f\"{color} moves from {start} to {end}\")"
|
| 356 |
+
]
|
| 357 |
+
},
|
| 358 |
+
{
|
| 359 |
+
"cell_type": "markdown",
|
| 360 |
+
"metadata": {},
|
| 361 |
+
"source": [
|
| 362 |
+
"So what kind of `finite state machine` is the traffic jam solver using?\n",
|
| 363 |
+
"\n",
|
| 364 |
+
"Each state is captured on a graph which represents a `tree of options`. As there are a finite number of options to for changing the finite state machine from it's current state to the next state, we can capture the change as branches our `tree of options`. Every time our \"search\" generates a novel branch, we add that to the tree. The unexplored options then become our \"frontier\" from which we search further.\n",
|
| 365 |
+
"\n",
|
| 366 |
+
"\n",
|
| 367 |
+
"\n",
|
| 368 |
+
"We can make a deep-copy of our state machine, as the tree above illustrates, but will quickly consume a terrible amount of memory, \n",
|
| 369 |
+
"\n",
|
| 370 |
+
"To capture each `state` of the `finite state machine` we only need to look at the position of loads. Here is an example:"
|
| 371 |
+
]
|
| 372 |
+
},
|
| 373 |
+
{
|
| 374 |
+
"cell_type": "code",
|
| 375 |
+
"execution_count": 16,
|
| 376 |
+
"metadata": {},
|
| 377 |
+
"outputs": [],
|
| 378 |
+
"source": [
|
| 379 |
+
"state_0 = ((1, 'red'), (2, None), (3, 'blue'), (4, None))\n",
|
| 380 |
+
"state_1 = ((1, None), (2, 'red'), (3, 'blue'), (4, None))\n",
|
| 381 |
+
"state_2 = ((1, 'red'), (2, 'blue'), (3, None), (4, None))\n"
|
| 382 |
+
]
|
| 383 |
+
},
|
| 384 |
+
{
|
| 385 |
+
"cell_type": "markdown",
|
| 386 |
+
"metadata": {},
|
| 387 |
+
"source": [
|
| 388 |
+
"We can now put these states into our `tree of options` as a graph:"
|
| 389 |
+
]
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"cell_type": "code",
|
| 393 |
+
"execution_count": 17,
|
| 394 |
+
"metadata": {},
|
| 395 |
+
"outputs": [],
|
| 396 |
+
"source": [
|
| 397 |
+
"\n",
|
| 398 |
+
"fsm_graph = Graph()\n",
|
| 399 |
+
"fsm_graph.add_edge(state_0, state_1) # there is a path from state_0 to state_1\n",
|
| 400 |
+
"fsm_graph.add_edge(state_0, state_2) # there is a path from state_0 to state_2"
|
| 401 |
+
]
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"cell_type": "markdown",
|
| 405 |
+
"metadata": {},
|
| 406 |
+
"source": [
|
| 407 |
+
"The search now becomes:\n",
|
| 408 |
+
"\n",
|
| 409 |
+
"1. \"make a change\", \n",
|
| 410 |
+
"2. check if it exists in the graph (and add it to the graph if it is novel). \n",
|
| 411 |
+
"\n",
|
| 412 |
+
"However the state definition contains a lot of `None`s that don't really add much.\n",
|
| 413 |
+
"\n",
|
| 414 |
+
"Suggestion: Let's just drop all the `None`s that information.\n",
|
| 415 |
+
"\n",
|
| 416 |
+
"New graph:"
|
| 417 |
+
]
|
| 418 |
+
},
|
| 419 |
+
{
|
| 420 |
+
"cell_type": "code",
|
| 421 |
+
"execution_count": 18,
|
| 422 |
+
"metadata": {},
|
| 423 |
+
"outputs": [],
|
| 424 |
+
"source": [
|
| 425 |
+
"state_0 = ((1, 'red'), (3, 'blue'))\n",
|
| 426 |
+
"state_1 = ((2, 'red'), (3, 'blue'))\n",
|
| 427 |
+
"state_2 = ((1, 'red'), (2, 'blue'))\n",
|
| 428 |
+
"\n",
|
| 429 |
+
"fsm_graph = Graph()\n",
|
| 430 |
+
"fsm_graph.add_edge(state_0, state_1)\n",
|
| 431 |
+
"fsm_graph.add_edge(state_0, state_2)"
|
| 432 |
+
]
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"cell_type": "markdown",
|
| 436 |
+
"metadata": {},
|
| 437 |
+
"source": [
|
| 438 |
+
"That's nicer to read.\n",
|
| 439 |
+
"\n",
|
| 440 |
+
"We can now make a solver that identifies a path from the `initial state` to the `final state`, simply by searching along the frontier from the `initial state`:"
|
| 441 |
+
]
|
| 442 |
+
},
|
| 443 |
+
{
|
| 444 |
+
"cell_type": "code",
|
| 445 |
+
"execution_count": 19,
|
| 446 |
+
"metadata": {},
|
| 447 |
+
"outputs": [],
|
| 448 |
+
"source": [
|
| 449 |
+
"# we need the \"map\" from earlier:\n",
|
| 450 |
+
"the_map = Graph()\n",
|
| 451 |
+
"edges = [(1,2),(2,3),(2,4)]\n",
|
| 452 |
+
"for edge in edges:\n",
|
| 453 |
+
" the_map.add_edge(*edge, bidirectional=True)\n",
|
| 454 |
+
"\n",
|
| 455 |
+
"# we need the \"frontier\"\n",
|
| 456 |
+
"job_queue = [state_0, state_1, state_2] # (you really only need state_0)\n",
|
| 457 |
+
"\n",
|
| 458 |
+
"# we need a \"final state\"\n",
|
| 459 |
+
"final_state = tuple(sorted([(3,'red'), (1,'blue')]))\n",
|
| 460 |
+
"\n",
|
| 461 |
+
"# we need a search\n",
|
| 462 |
+
"while job_queue:\n",
|
| 463 |
+
" state = job_queue.pop()\n",
|
| 464 |
+
" occupied = {position for position,item in state}\n",
|
| 465 |
+
" for position, item in state: # 'red' on position 2.\n",
|
| 466 |
+
" for option in the_map.nodes(from_node=position): # positions {1,4}\n",
|
| 467 |
+
" if option in occupied:\n",
|
| 468 |
+
" continue # skip it.\n",
|
| 469 |
+
" \n",
|
| 470 |
+
" new_state = tuple(sorted([(option, item)] + [(o,i) for o,i in state if i != item]))\n",
|
| 471 |
+
" \n",
|
| 472 |
+
" if new_state in fsm_graph:\n",
|
| 473 |
+
" continue # we've already seen it.\n",
|
| 474 |
+
" \n",
|
| 475 |
+
" # we add the new state to be explored.\n",
|
| 476 |
+
" fsm_graph.add_edge(state, new_state)\n",
|
| 477 |
+
" job_queue.append(new_state)\n",
|
| 478 |
+
"\n",
|
| 479 |
+
" if new_state == final_state:\n",
|
| 480 |
+
" break"
|
| 481 |
+
]
|
| 482 |
+
},
|
| 483 |
+
{
|
| 484 |
+
"cell_type": "markdown",
|
| 485 |
+
"metadata": {},
|
| 486 |
+
"source": [
|
| 487 |
+
"As the solver has made the tree of options of the finite state diagram we can have a quick look:"
|
| 488 |
+
]
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"cell_type": "code",
|
| 492 |
+
"execution_count": 20,
|
| 493 |
+
"metadata": {},
|
| 494 |
+
"outputs": [
|
| 495 |
+
{
|
| 496 |
+
"data": {
|
| 497 |
+
"text/plain": [
|
| 498 |
+
"[((1, 'red'), (3, 'blue')),\n",
|
| 499 |
+
" ((2, 'red'), (3, 'blue')),\n",
|
| 500 |
+
" ((1, 'red'), (2, 'blue')),\n",
|
| 501 |
+
" ((1, 'red'), (4, 'blue')),\n",
|
| 502 |
+
" ((2, 'red'), (4, 'blue')),\n",
|
| 503 |
+
" ((3, 'red'), (4, 'blue')),\n",
|
| 504 |
+
" ((2, 'blue'), (3, 'red')),\n",
|
| 505 |
+
" ((1, 'blue'), (3, 'red')),\n",
|
| 506 |
+
" ((1, 'blue'), (2, 'red')),\n",
|
| 507 |
+
" ((1, 'blue'), (4, 'red')),\n",
|
| 508 |
+
" ((2, 'blue'), (4, 'red')),\n",
|
| 509 |
+
" ((3, 'blue'), (4, 'red'))]"
|
| 510 |
+
]
|
| 511 |
+
},
|
| 512 |
+
"execution_count": 20,
|
| 513 |
+
"metadata": {},
|
| 514 |
+
"output_type": "execute_result"
|
| 515 |
+
}
|
| 516 |
+
],
|
| 517 |
+
"source": [
|
| 518 |
+
"fsm_graph.nodes()"
|
| 519 |
+
]
|
| 520 |
+
},
|
| 521 |
+
{
|
| 522 |
+
"cell_type": "markdown",
|
| 523 |
+
"metadata": {},
|
| 524 |
+
"source": [
|
| 525 |
+
"To find the fewest moves to resolve the traffic jam, we can just ask the finite state diagram for the shortest path from the `initial state` to the `final state`:"
|
| 526 |
+
]
|
| 527 |
+
},
|
| 528 |
+
{
|
| 529 |
+
"cell_type": "code",
|
| 530 |
+
"execution_count": 21,
|
| 531 |
+
"metadata": {},
|
| 532 |
+
"outputs": [
|
| 533 |
+
{
|
| 534 |
+
"data": {
|
| 535 |
+
"text/plain": [
|
| 536 |
+
"(6,\n",
|
| 537 |
+
" [((1, 'red'), (3, 'blue')),\n",
|
| 538 |
+
" ((1, 'red'), (2, 'blue')),\n",
|
| 539 |
+
" ((1, 'red'), (4, 'blue')),\n",
|
| 540 |
+
" ((2, 'red'), (4, 'blue')),\n",
|
| 541 |
+
" ((3, 'red'), (4, 'blue')),\n",
|
| 542 |
+
" ((2, 'blue'), (3, 'red')),\n",
|
| 543 |
+
" ((1, 'blue'), (3, 'red'))])"
|
| 544 |
+
]
|
| 545 |
+
},
|
| 546 |
+
"execution_count": 21,
|
| 547 |
+
"metadata": {},
|
| 548 |
+
"output_type": "execute_result"
|
| 549 |
+
}
|
| 550 |
+
],
|
| 551 |
+
"source": [
|
| 552 |
+
"fsm_graph.shortest_path(state_0, final_state)"
|
| 553 |
+
]
|
| 554 |
+
},
|
| 555 |
+
{
|
| 556 |
+
"cell_type": "markdown",
|
| 557 |
+
"metadata": {},
|
| 558 |
+
"source": [
|
| 559 |
+
"And there you have it! The solution from earlier:\n",
|
| 560 |
+
"\n",
|
| 561 |
+
"```\n",
|
| 562 |
+
"red moves from 1 to 2\n",
|
| 563 |
+
"red moves from 2 to 4\n",
|
| 564 |
+
"blue moves from 3 to 2\n",
|
| 565 |
+
"blue moves from 2 to 1\n",
|
| 566 |
+
"red moves from 4 to 2\n",
|
| 567 |
+
"red moves from 2 to 3\n",
|
| 568 |
+
"```\n",
|
| 569 |
+
"\n",
|
| 570 |
+
"and the solution above shows, state by state what has changed. \n",
|
| 571 |
+
"\n",
|
| 572 |
+
"Note that the two valid solutions interchangeable. "
|
| 573 |
+
]
|
| 574 |
+
},
|
| 575 |
+
{
|
| 576 |
+
"cell_type": "markdown",
|
| 577 |
+
"metadata": {},
|
| 578 |
+
"source": [
|
| 579 |
+
"# Sudoku\n",
|
| 580 |
+
"\n",
|
| 581 |
+
"Solving a sudoku is no different can be done in a similar way, but our search can become a lot more efficient if we introduce the ideas of a wave collapse function.\n",
|
| 582 |
+
"\n",
|
| 583 |
+
"Q: A what-collapse?\n",
|
| 584 |
+
"\n",
|
| 585 |
+
"A: A wave collapse function. \n",
|
| 586 |
+
"\n",
|
| 587 |
+
"Q: ???\n",
|
| 588 |
+
"\n",
|
| 589 |
+
"A: Imagine you're surfing on a wave from an `initial state` over undefined state (US) space. As the wave rolls over the undefined space (US), it imposes the most probably outcome onto the undefined space. Each insertion causes the options of the neighbouring spaces to collapse from all possible options to very few plausible options. [More about wave collapse functions](https://github.com/mxgmn/WaveFunctionCollapse)\n",
|
| 590 |
+
"\n",
|
| 591 |
+
"\n",
|
| 592 |
+
"\n",
|
| 593 |
+
"Inserting for example `2` in the red box would immediately make `2` unavailable in all other boxes in top right corner. That is an example of a collapse of options.\n",
|
| 594 |
+
"\n",
|
| 595 |
+
"We can describe the \"options\" for each cell as: numbers - (box + row + column), e.g. the `red` cell as:"
|
| 596 |
+
]
|
| 597 |
+
},
|
| 598 |
+
{
|
| 599 |
+
"cell_type": "code",
|
| 600 |
+
"execution_count": 22,
|
| 601 |
+
"metadata": {},
|
| 602 |
+
"outputs": [
|
| 603 |
+
{
|
| 604 |
+
"data": {
|
| 605 |
+
"text/plain": [
|
| 606 |
+
"{2, 7, 9}"
|
| 607 |
+
]
|
| 608 |
+
},
|
| 609 |
+
"execution_count": 22,
|
| 610 |
+
"metadata": {},
|
| 611 |
+
"output_type": "execute_result"
|
| 612 |
+
}
|
| 613 |
+
],
|
| 614 |
+
"source": [
|
| 615 |
+
"{1,2,3,4,5,6,7,8,9} - ({1,3,4,5,6,8} | {3,5} | {3,4,6}) "
|
| 616 |
+
]
|
| 617 |
+
},
|
| 618 |
+
{
|
| 619 |
+
"cell_type": "markdown",
|
| 620 |
+
"metadata": {},
|
| 621 |
+
"source": [
|
| 622 |
+
"We can also describe a whole sudoku's state as a list numbers, with zero as the uncollapsed space:.\n",
|
| 623 |
+
"\n",
|
| 624 |
+
"The box above hence becomes: \n",
|
| 625 |
+
"```\n",
|
| 626 |
+
"[8,4,1,\n",
|
| 627 |
+
" 0,0,6,\n",
|
| 628 |
+
" 5,0,3]\n",
|
| 629 |
+
"```\n",
|
| 630 |
+
"\n",
|
| 631 |
+
"Doing the same for a whole sudoku like this: \n",
|
| 632 |
+
"\n",
|
| 633 |
+
"\n",
|
| 634 |
+
"\n",
|
| 635 |
+
"can then be:\n",
|
| 636 |
+
"\n"
|
| 637 |
+
]
|
| 638 |
+
},
|
| 639 |
+
{
|
| 640 |
+
"cell_type": "code",
|
| 641 |
+
"execution_count": 23,
|
| 642 |
+
"metadata": {},
|
| 643 |
+
"outputs": [],
|
| 644 |
+
"source": [
|
| 645 |
+
"sudoku = [\n",
|
| 646 |
+
" # cell value # cell number\n",
|
| 647 |
+
" 0,0,4,0,5,0,0,0,0, # 0, 1, 2, 3, 4, 5, 6, 7, 8,\n",
|
| 648 |
+
" 9,0,0,7,3,4,6,0,0, # 9,10,11,12,13,14,15,16,17,\n",
|
| 649 |
+
" 0,0,3,0,2,1,0,4,9, # 18,19,20,21,22,23,24,25,26, \n",
|
| 650 |
+
" 0,3,5,0,9,0,4,8,0, # 27,28,29,30,31,32,33,34,35,\n",
|
| 651 |
+
" 0,9,0,0,0,0,0,3,0, # 36,37,38,39,40,41,42,43,44,\n",
|
| 652 |
+
" 0,7,6,0,1,0,9,2,0, # 45,46,47,48,49,50,51,52,53,\n",
|
| 653 |
+
" 3,1,0,9,7,0,2,0,0, # 54,55,56,57,58,59,60,61,62,\n",
|
| 654 |
+
" 0,0,9,1,8,2,0,0,3, # 63,64,65,66,67,68,69,70,71,\n",
|
| 655 |
+
" 0,0,0,0,6,0,1,0,0, # 72,73,74,75,76,77,78,79,80\n",
|
| 656 |
+
" # zero = blank.\n",
|
| 657 |
+
"]"
|
| 658 |
+
]
|
| 659 |
+
},
|
| 660 |
+
{
|
| 661 |
+
"cell_type": "markdown",
|
| 662 |
+
"metadata": {},
|
| 663 |
+
"source": [
|
| 664 |
+
"To describe the options for a particular cell, we now need some basic math:\n",
|
| 665 |
+
"\n",
|
| 666 |
+
"We find the set of number that have not been used - just like a minute ago - but we use the \"cell\" based address system."
|
| 667 |
+
]
|
| 668 |
+
},
|
| 669 |
+
{
|
| 670 |
+
"cell_type": "markdown",
|
| 671 |
+
"metadata": {},
|
| 672 |
+
"source": [
|
| 673 |
+
""
|
| 674 |
+
]
|
| 675 |
+
},
|
| 676 |
+
{
|
| 677 |
+
"cell_type": "code",
|
| 678 |
+
"execution_count": 24,
|
| 679 |
+
"metadata": {},
|
| 680 |
+
"outputs": [],
|
| 681 |
+
"source": [
|
| 682 |
+
"from math import floor\n",
|
| 683 |
+
"numbers = {1,2,3,4,5,6,7,8,9}\n",
|
| 684 |
+
"\n",
|
| 685 |
+
"def options(cell,sudoku): \n",
|
| 686 |
+
" column = {v for ix, v in enumerate(sudoku) if ix % 9 == cell % 9}\n",
|
| 687 |
+
" row = {v for ix, v in enumerate(sudoku) if ix // 9 == cell // 9}\n",
|
| 688 |
+
" box = {v for ix, v in enumerate(sudoku) if (ix // (9 * 3) == cell // (9 * 3)) and ((ix % 9) // 3 == (cell % 9) // 3)}\n",
|
| 689 |
+
" return numbers - (box | row | column)"
|
| 690 |
+
]
|
| 691 |
+
},
|
| 692 |
+
{
|
| 693 |
+
"cell_type": "code",
|
| 694 |
+
"execution_count": 25,
|
| 695 |
+
"metadata": {},
|
| 696 |
+
"outputs": [
|
| 697 |
+
{
|
| 698 |
+
"data": {
|
| 699 |
+
"text/plain": [
|
| 700 |
+
"{5}"
|
| 701 |
+
]
|
| 702 |
+
},
|
| 703 |
+
"execution_count": 25,
|
| 704 |
+
"metadata": {},
|
| 705 |
+
"output_type": "execute_result"
|
| 706 |
+
}
|
| 707 |
+
],
|
| 708 |
+
"source": [
|
| 709 |
+
"options(53, sudoku)"
|
| 710 |
+
]
|
| 711 |
+
},
|
| 712 |
+
{
|
| 713 |
+
"cell_type": "markdown",
|
| 714 |
+
"metadata": {},
|
| 715 |
+
"source": [
|
| 716 |
+
"So there's one option for cell number 53, meaning that the solution landscape *must* eventually collapse around this single option. We can now guide our \"search\" by picking the cell's that have least options.\n",
|
| 717 |
+
"\n",
|
| 718 |
+
"Let's find them:"
|
| 719 |
+
]
|
| 720 |
+
},
|
| 721 |
+
{
|
| 722 |
+
"cell_type": "code",
|
| 723 |
+
"execution_count": 26,
|
| 724 |
+
"metadata": {},
|
| 725 |
+
"outputs": [
|
| 726 |
+
{
|
| 727 |
+
"name": "stdout",
|
| 728 |
+
"output_type": "stream",
|
| 729 |
+
"text": [
|
| 730 |
+
"[5, 3, 0, 2, 0, 3, 3, 2, 4]\n",
|
| 731 |
+
"[0, 3, 3, 0, 0, 0, 0, 2, 4]\n",
|
| 732 |
+
"[4, 3, 0, 2, 0, 0, 3, 0, 0]\n",
|
| 733 |
+
"[2, 0, 0, 2, 0, 2, 0, 0, 3]\n",
|
| 734 |
+
"[4, 0, 3, 5, 1, 4, 2, 0, 4]\n",
|
| 735 |
+
"[2, 0, 0, 4, 0, 3, 0, 0, 1]\n",
|
| 736 |
+
"[0, 0, 1, 0, 0, 1, 0, 2, 4]\n",
|
| 737 |
+
"[4, 3, 0, 0, 0, 0, 2, 3, 0]\n",
|
| 738 |
+
"[5, 4, 3, 3, 0, 2, 0, 3, 4]\n"
|
| 739 |
+
]
|
| 740 |
+
}
|
| 741 |
+
],
|
| 742 |
+
"source": [
|
| 743 |
+
"degrees_of_freedom = [0 if v!=0 else len(options(ix,sudoku)) for ix,v in enumerate(sudoku)]\n",
|
| 744 |
+
"\n",
|
| 745 |
+
"for i in range(9):\n",
|
| 746 |
+
" print(degrees_of_freedom[i*9:i*9+9])"
|
| 747 |
+
]
|
| 748 |
+
},
|
| 749 |
+
{
|
| 750 |
+
"cell_type": "markdown",
|
| 751 |
+
"metadata": {},
|
| 752 |
+
"source": [
|
| 753 |
+
"Each of the numbers are the number of options available for each cell.\n",
|
| 754 |
+
"\n",
|
| 755 |
+
"We can then design our solver to inspect the sudoku as a finite state machine, where the \"current state\" is our partial solution, e.g. a list with 81 integers.\n",
|
| 756 |
+
"\n",
|
| 757 |
+
""
|
| 758 |
+
]
|
| 759 |
+
},
|
| 760 |
+
{
|
| 761 |
+
"cell_type": "code",
|
| 762 |
+
"execution_count": 27,
|
| 763 |
+
"metadata": {},
|
| 764 |
+
"outputs": [
|
| 765 |
+
{
|
| 766 |
+
"name": "stdout",
|
| 767 |
+
"output_type": "stream",
|
| 768 |
+
"text": [
|
| 769 |
+
"[2, 6, 4, 8, 5, 9, 3, 1, 7]\n",
|
| 770 |
+
"[9, 8, 1, 7, 3, 4, 6, 5, 2]\n",
|
| 771 |
+
"[7, 5, 3, 6, 2, 1, 8, 4, 9]\n",
|
| 772 |
+
"[1, 3, 5, 2, 9, 7, 4, 8, 6]\n",
|
| 773 |
+
"[8, 9, 2, 5, 4, 6, 7, 3, 1]\n",
|
| 774 |
+
"[4, 7, 6, 3, 1, 8, 9, 2, 5]\n",
|
| 775 |
+
"[3, 1, 8, 9, 7, 5, 2, 6, 4]\n",
|
| 776 |
+
"[6, 4, 9, 1, 8, 2, 5, 7, 3]\n",
|
| 777 |
+
"[5, 2, 7, 4, 6, 3, 1, 9, 8]\n"
|
| 778 |
+
]
|
| 779 |
+
}
|
| 780 |
+
],
|
| 781 |
+
"source": [
|
| 782 |
+
"# the sudoku is our initial state.\n",
|
| 783 |
+
"initial_state = sudoku[:]\n",
|
| 784 |
+
"\n",
|
| 785 |
+
"job_queue = [initial_state] # we need the jobqueue in case of ambiguity of choice.\n",
|
| 786 |
+
"\n",
|
| 787 |
+
"while job_queue:\n",
|
| 788 |
+
" state = job_queue.pop(0)\n",
|
| 789 |
+
" if not any(i==0 for i in state): # no missing values means that the sudoku is solved.\n",
|
| 790 |
+
" break\n",
|
| 791 |
+
"\n",
|
| 792 |
+
" degrees_of_freedom = [0 if v!=0 else len(options(ix,state)) for ix,v in enumerate(state)]\n",
|
| 793 |
+
" least_freedom = min(v for v in degrees_of_freedom if v > 0)\n",
|
| 794 |
+
" cell = degrees_of_freedom.index(least_freedom)\n",
|
| 795 |
+
"\n",
|
| 796 |
+
" for option in options(cell, state): # for each option we add the new state to the queue.\n",
|
| 797 |
+
" new_state = state[:]\n",
|
| 798 |
+
" new_state[cell] = option\n",
|
| 799 |
+
" job_queue.append(new_state)\n",
|
| 800 |
+
"\n",
|
| 801 |
+
"# we print out the solution\n",
|
| 802 |
+
"for i in range(9):\n",
|
| 803 |
+
" print(state[i*9:i*9+9])"
|
| 804 |
+
]
|
| 805 |
+
},
|
| 806 |
+
{
|
| 807 |
+
"cell_type": "markdown",
|
| 808 |
+
"metadata": {},
|
| 809 |
+
"source": [
|
| 810 |
+
"# Conclusions\n",
|
| 811 |
+
"\n",
|
| 812 |
+
"What did we learn?\n",
|
| 813 |
+
"\n",
|
| 814 |
+
"Finite State Machines ...\n",
|
| 815 |
+
"\n",
|
| 816 |
+
"- are mathematical models of states and transitions.\n",
|
| 817 |
+
"- can be used as generators for mapping of a solution landscape.\n",
|
| 818 |
+
"- can be used to solve any discrete problem."
|
| 819 |
+
]
|
| 820 |
+
}
|
| 821 |
+
],
|
| 822 |
+
"metadata": {
|
| 823 |
+
"kernelspec": {
|
| 824 |
+
"display_name": "Python 3.10.5 ('graph310')",
|
| 825 |
+
"language": "python",
|
| 826 |
+
"name": "python3"
|
| 827 |
+
},
|
| 828 |
+
"language_info": {
|
| 829 |
+
"codemirror_mode": {
|
| 830 |
+
"name": "ipython",
|
| 831 |
+
"version": 3
|
| 832 |
+
},
|
| 833 |
+
"file_extension": ".py",
|
| 834 |
+
"mimetype": "text/x-python",
|
| 835 |
+
"name": "python",
|
| 836 |
+
"nbconvert_exporter": "python",
|
| 837 |
+
"pygments_lexer": "ipython3",
|
| 838 |
+
"version": "3.10.11"
|
| 839 |
+
},
|
| 840 |
+
"vscode": {
|
| 841 |
+
"interpreter": {
|
| 842 |
+
"hash": "26e418a870e8218a0038cc410eecceadac9265fed4dd23580d1f865f336a3180"
|
| 843 |
+
}
|
| 844 |
+
}
|
| 845 |
+
},
|
| 846 |
+
"nbformat": 4,
|
| 847 |
+
"nbformat_minor": 0
|
| 848 |
+
}
|
graph-theory/source/examples/graphs.py
ADDED
|
@@ -0,0 +1,766 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from graph import Graph
|
| 2 |
+
from itertools import product
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def grid(x, y, bidirectional=False):
|
| 6 |
+
"""
|
| 7 |
+
:param x: number of columns
|
| 8 |
+
:param y: number of rows
|
| 9 |
+
:param bidirectional: boolean
|
| 10 |
+
:return: Graph with grid.
|
| 11 |
+
"""
|
| 12 |
+
g = Graph()
|
| 13 |
+
for i, j in product(range(x), range(y)):
|
| 14 |
+
if i % x > 0: # connect west
|
| 15 |
+
n1 = i - 1, j
|
| 16 |
+
n2 = i, j
|
| 17 |
+
g.add_edge(n1, n2, bidirectional=bidirectional)
|
| 18 |
+
|
| 19 |
+
if j % y > 0: # connect north
|
| 20 |
+
n1 = i, j - 1
|
| 21 |
+
n2 = i, j
|
| 22 |
+
g.add_edge(n1, n2, bidirectional=bidirectional)
|
| 23 |
+
return g
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def london_underground():
|
| 27 |
+
"""the london underground network"""
|
| 28 |
+
london_underground_stations = { # id:(latitude,longitude,station name),
|
| 29 |
+
1: (51.5028, -0.2801, "Acton Town"),
|
| 30 |
+
2: (51.5143, -0.0755, "Aldgate"),
|
| 31 |
+
3: (51.5154, -0.0726, "Aldgate East"),
|
| 32 |
+
4: (51.5107, -0.013, "All Saints"),
|
| 33 |
+
5: (51.5407, -0.2997, "Alperton"),
|
| 34 |
+
7: (51.5322, -0.1058, "Angel"),
|
| 35 |
+
8: (51.5653, -0.1353, "Archway"),
|
| 36 |
+
9: (51.6164, -0.1331, "Arnos Grove"),
|
| 37 |
+
10: (51.5586, -0.1059, "Arsenal"),
|
| 38 |
+
11: (51.5226, -0.1571, "Baker Street"),
|
| 39 |
+
12: (51.4431, -0.1525, "Balham"),
|
| 40 |
+
13: (51.5133, -0.0886, "Bank"),
|
| 41 |
+
14: (51.5204, -0.0979, "Barbican"),
|
| 42 |
+
15: (51.5396, 0.081, "Barking"),
|
| 43 |
+
16: (51.5856, 0.0887, "Barkingside"),
|
| 44 |
+
17: (51.4905, -0.2139, "Barons Court"),
|
| 45 |
+
18: (51.5121, -0.1879, "Bayswater"),
|
| 46 |
+
19: (51.5148, 0.0613, "Beckton"),
|
| 47 |
+
20: (51.5087, 0.055, "Beckton Park"),
|
| 48 |
+
21: (51.5403, 0.127, "Becontree"),
|
| 49 |
+
22: (51.5504, -0.1642, "Belsize Park"),
|
| 50 |
+
24: (51.527, -0.0549, "Bethnal Green"),
|
| 51 |
+
25: (51.512, -0.1031, "Blackfriars"),
|
| 52 |
+
26: (51.5867, -0.0417, "Blackhorse Road"),
|
| 53 |
+
27: (51.5079, -0.0066, "Blackwall"),
|
| 54 |
+
28: (51.5142, -0.1494, "Bond Street"),
|
| 55 |
+
29: (51.5011, -0.0943, "Borough"),
|
| 56 |
+
30: (51.4956, -0.325, "Boston Manor"),
|
| 57 |
+
31: (51.6071, -0.1243, "Bounds Green"),
|
| 58 |
+
32: (51.5273, -0.0208, "Bow Church"),
|
| 59 |
+
33: (51.5269, -0.0247, "Bow Road"),
|
| 60 |
+
34: (51.5766, -0.2136, "Brent Cross"),
|
| 61 |
+
36: (51.5248, -0.0119, "Bromley-By-Bow"),
|
| 62 |
+
38: (51.6028, -0.2641, "Burnt Oak"),
|
| 63 |
+
39: (51.5481, -0.1188, "Caledonian Road"),
|
| 64 |
+
40: (51.5392, -0.1426, "Camden Town"),
|
| 65 |
+
42: (51.5051, -0.0209, "Canary Wharf"),
|
| 66 |
+
44: (51.5113, -0.0904, "Cannon Street"),
|
| 67 |
+
45: (51.6078, -0.2947, "Canons Park"),
|
| 68 |
+
47: (51.5441, -0.1538, "Chalk Farm"),
|
| 69 |
+
48: (51.5185, -0.1111, "Chancery Lane"),
|
| 70 |
+
49: (51.508, -0.1247, "Charing Cross"),
|
| 71 |
+
51: (51.6177, 0.0755, "Chigwell"),
|
| 72 |
+
52: (51.4946, -0.2678, "Chiswick Park"),
|
| 73 |
+
54: (51.4618, -0.1384, "Clapham Common"),
|
| 74 |
+
55: (51.4649, -0.1299, "Clapham North"),
|
| 75 |
+
56: (51.4527, -0.148, "Clapham South"),
|
| 76 |
+
58: (51.5955, -0.2502, "Colindale"),
|
| 77 |
+
59: (51.418, -0.1778, "Colliers Wood"),
|
| 78 |
+
60: (51.5129, -0.1243, "Covent Garden"),
|
| 79 |
+
61: (51.4957, -0.0144, "Crossharbour & London Arena"),
|
| 80 |
+
63: (51.5095, 0.0276, "Custom House"),
|
| 81 |
+
65: (51.5085, 0.064, "Cyprus"),
|
| 82 |
+
66: (51.5443, 0.1655, "Dagenham East"),
|
| 83 |
+
67: (51.5417, 0.1469, "Dagenham Heathway"),
|
| 84 |
+
70: (51.5223, -0.0173, "Devons Road"),
|
| 85 |
+
71: (51.552, -0.2387, "Dollis Hill"),
|
| 86 |
+
72: (51.5152, -0.3017, "Ealing Broadway"),
|
| 87 |
+
73: (51.5101, -0.2882, "Ealing Common"),
|
| 88 |
+
74: (51.492, -0.1973, "Earl's Court"),
|
| 89 |
+
75: (51.5765, -0.397, "Eastcote"),
|
| 90 |
+
76: (51.5168, -0.2474, "East Acton"),
|
| 91 |
+
77: (51.5874, -0.165, "East Finchley"),
|
| 92 |
+
78: (51.5394, 0.0518, "East Ham"),
|
| 93 |
+
79: (51.5093, -0.0021, "East India"),
|
| 94 |
+
80: (51.4586, -0.2112, "East Putney"),
|
| 95 |
+
81: (51.6137, -0.275, "Edgware"),
|
| 96 |
+
82: (51.5199, -0.1679, "Edgware Road (B)"),
|
| 97 |
+
83: (51.5203, -0.17, "Edgware Road (C)"),
|
| 98 |
+
84: (51.4943, -0.1001, "Elephant & Castle"),
|
| 99 |
+
85: (51.5496, 0.1977, "Elm Park"),
|
| 100 |
+
87: (51.5074, -0.1223, "Embankment"),
|
| 101 |
+
89: (51.5282, -0.1337, "Euston"),
|
| 102 |
+
90: (51.526, -0.1359, "Euston Square"),
|
| 103 |
+
91: (51.596, 0.0912, "Fairlop"),
|
| 104 |
+
92: (51.5203, -0.1053, "Farringdon"),
|
| 105 |
+
93: (51.6012, -0.1932, "Finchley Central"),
|
| 106 |
+
94: (51.5472, -0.1803, "Finchley Road"),
|
| 107 |
+
95: (51.5642, -0.1065, "Finsbury Park"),
|
| 108 |
+
96: (51.4804, -0.195, "Fulham Broadway"),
|
| 109 |
+
97: (51.5096, 0.0716, "Gallions Reach"),
|
| 110 |
+
98: (51.5765, 0.0663, "Gants Hill"),
|
| 111 |
+
99: (51.4945, -0.1829, "Gloucester Road"),
|
| 112 |
+
100: (51.5724, -0.1941, "Golders Green"),
|
| 113 |
+
101: (51.5018, -0.2267, "Goldhawk Road"),
|
| 114 |
+
102: (51.5205, -0.1347, "Goodge Street"),
|
| 115 |
+
103: (51.6132, 0.0923, "Grange Hill"),
|
| 116 |
+
104: (51.5238, -0.1439, "Great Portland Street"),
|
| 117 |
+
105: (51.5423, -0.3456, "Greenford"),
|
| 118 |
+
107: (51.5067, -0.1428, "Green Park"),
|
| 119 |
+
108: (51.4915, -0.2754, "Gunnersbury"),
|
| 120 |
+
109: (51.603, 0.0933, "Hainault"),
|
| 121 |
+
110: (51.4936, -0.2251, "Hammersmith"),
|
| 122 |
+
111: (51.5568, -0.178, "Hampstead"),
|
| 123 |
+
112: (51.5302, -0.2933, "Hanger Lane"),
|
| 124 |
+
113: (51.5362, -0.2575, "Harlesden"),
|
| 125 |
+
114: (51.5925, -0.3351, "Harrow & Wealdston"),
|
| 126 |
+
115: (51.5793, -0.3366, "Harrow-on-the-Hill"),
|
| 127 |
+
116: (51.4669, -0.4227, "Hatton Cross"),
|
| 128 |
+
117: (51.4713, -0.4524, "Heathrow Terminals 1, 2 & 3"),
|
| 129 |
+
118: (51.4598, -0.4476, "Heathrow Terminal 4"),
|
| 130 |
+
119: (51.5829, -0.2259, "Hendon Central"),
|
| 131 |
+
120: (51.5033, -0.0215, "Heron Quays"),
|
| 132 |
+
122: (51.5009, -0.1925, "High Street Kensington"),
|
| 133 |
+
123: (51.546, -0.104, "Highbury & Islington"),
|
| 134 |
+
124: (51.5777, -0.1458, "Highgate"),
|
| 135 |
+
125: (51.5538, -0.4499, "Hillingdon"),
|
| 136 |
+
126: (51.5174, -0.12, "Holborn"),
|
| 137 |
+
127: (51.5075, -0.206, "Holland Park"),
|
| 138 |
+
128: (51.5526, -0.1132, "Holloway Road"),
|
| 139 |
+
129: (51.5539, 0.2184, "Hornchurch"),
|
| 140 |
+
130: (51.4713, -0.3665, "Hounslow Central"),
|
| 141 |
+
131: (51.4733, -0.3564, "Hounslow East"),
|
| 142 |
+
132: (51.4734, -0.3855, "Hounslow West"),
|
| 143 |
+
133: (51.5027, -0.1527, "Hyde Park Corner"),
|
| 144 |
+
134: (51.5619, -0.4421, "Ickenham"),
|
| 145 |
+
135: (51.4871, -0.0101, "Island Gardens"),
|
| 146 |
+
136: (51.4884, -0.1053, "Kennington"),
|
| 147 |
+
137: (51.5304, -0.225, "Kensal Green"),
|
| 148 |
+
138: (51.4983, -0.2106, "Kensington (Olympia)"),
|
| 149 |
+
139: (51.5507, -0.1402, "Kentish Town"),
|
| 150 |
+
140: (51.5816, -0.3162, "Kenton"),
|
| 151 |
+
141: (51.477, -0.285, "Kew Gardens"),
|
| 152 |
+
142: (51.5471, -0.2047, "Kilburn"),
|
| 153 |
+
143: (51.5351, -0.1939, "Kilburn Park"),
|
| 154 |
+
144: (51.5846, -0.2786, "Kingsbury"),
|
| 155 |
+
145: (51.5308, -0.1238, "King's Cross St. Pancras"),
|
| 156 |
+
146: (51.5015, -0.1607, "Knightsbridge"),
|
| 157 |
+
147: (51.5172, -0.2107, "Ladbroke Grove"),
|
| 158 |
+
148: (51.4991, -0.1115, "Lambeth North"),
|
| 159 |
+
149: (51.5119, -0.1756, "Lancaster Gate"),
|
| 160 |
+
150: (51.5139, -0.2172, "Latimer Road"),
|
| 161 |
+
151: (51.5113, -0.1281, "Leicester Square"),
|
| 162 |
+
153: (51.5566, -0.0053, "Leyton"),
|
| 163 |
+
154: (51.5683, 0.0083, "Leytonstone"),
|
| 164 |
+
155: (51.5123, -0.0396, "Limehouse"),
|
| 165 |
+
156: (51.5178, -0.0823, "Liverpool Street"),
|
| 166 |
+
157: (51.5052, -0.0864, "London Bridge"),
|
| 167 |
+
159: (51.53, -0.1854, "Maida Vale"),
|
| 168 |
+
160: (51.5712, -0.0958, "Manor House"),
|
| 169 |
+
161: (51.5122, -0.094, "Mansion House"),
|
| 170 |
+
162: (51.5136, -0.1586, "Marble Arch"),
|
| 171 |
+
163: (51.5225, -0.1631, "Marylebone"),
|
| 172 |
+
164: (51.5249, -0.0332, "Mile End"),
|
| 173 |
+
165: (51.6082, -0.2103, "Mill Hill East"),
|
| 174 |
+
166: (51.5108, -0.0863, "Monument"),
|
| 175 |
+
167: (51.5186, -0.0886, "Moorgate"),
|
| 176 |
+
168: (51.6294, -0.432, "Moor Park"),
|
| 177 |
+
169: (51.4022, -0.1948, "Morden"),
|
| 178 |
+
170: (51.5342, -0.1387, "Mornington Crescent"),
|
| 179 |
+
171: (51.4902, -0.0145, "Mudchute"),
|
| 180 |
+
172: (51.5542, -0.2503, "Neasden"),
|
| 181 |
+
173: (51.5756, 0.0899, "Newbury Park"),
|
| 182 |
+
176: (51.4995, -0.3142, "Northfields"),
|
| 183 |
+
177: (51.5483, -0.3687, "Northolt"),
|
| 184 |
+
178: (51.5784, -0.3184, "Northwick Park"),
|
| 185 |
+
179: (51.6111, -0.424, "Northwood"),
|
| 186 |
+
180: (51.6004, -0.4092, "Northwood Hills"),
|
| 187 |
+
181: (51.5237, -0.2597, "North Acton"),
|
| 188 |
+
182: (51.5175, -0.2887, "North Ealing"),
|
| 189 |
+
184: (51.5846, -0.3626, "North Harrow"),
|
| 190 |
+
185: (51.5621, -0.3034, "North Wembley"),
|
| 191 |
+
186: (51.5094, -0.1967, "Notting Hill Gate"),
|
| 192 |
+
188: (51.5263, -0.0873, "Old Street"),
|
| 193 |
+
190: (51.4813, -0.3522, "Osterley"),
|
| 194 |
+
191: (51.4819, -0.113, "Oval"),
|
| 195 |
+
192: (51.515, -0.1415, "Oxford Circus"),
|
| 196 |
+
193: (51.5154, -0.1755, "Paddington"),
|
| 197 |
+
194: (51.527, -0.2841, "Park Royal"),
|
| 198 |
+
195: (51.4753, -0.2011, "Parsons Green"),
|
| 199 |
+
196: (51.5366, -0.3232, "Perivale"),
|
| 200 |
+
197: (51.5098, -0.1342, "Picadilly Circus"),
|
| 201 |
+
198: (51.4893, -0.1334, "Pimlico"),
|
| 202 |
+
199: (51.5926, -0.3805, "Pinner"),
|
| 203 |
+
200: (51.5313, 0.0172, "Plaistow"),
|
| 204 |
+
201: (51.5077, -0.0173, "Poplar"),
|
| 205 |
+
202: (51.572, -0.2954, "Preston Road"),
|
| 206 |
+
203: (51.5093, 0.0336, "Prince Regent"),
|
| 207 |
+
205: (51.4682, -0.2089, "Putney Bridge"),
|
| 208 |
+
206: (51.5341, -0.2047, "Queen's Park"),
|
| 209 |
+
207: (51.5942, -0.2861, "Queensbury"),
|
| 210 |
+
208: (51.5107, -0.1877, "Queensway"),
|
| 211 |
+
209: (51.4942, -0.2359, "Ravenscourt Park"),
|
| 212 |
+
210: (51.5753, -0.3714, "Rayners Lane"),
|
| 213 |
+
211: (51.5763, 0.0454, "Redbridge"),
|
| 214 |
+
212: (51.5234, -0.1466, "Regent's Park"),
|
| 215 |
+
213: (51.4633, -0.3013, "Richmond"),
|
| 216 |
+
215: (51.6171, 0.0439, "Roding Valley"),
|
| 217 |
+
216: (51.501, -0.0525, "Rotherhithe"),
|
| 218 |
+
217: (51.5084, 0.0465, "Royal Albert"),
|
| 219 |
+
218: (51.519, -0.188, "Royal Oak"),
|
| 220 |
+
219: (51.5091, 0.0181, "Royal Victoria"),
|
| 221 |
+
220: (51.5715, -0.4213, "Ruislip"),
|
| 222 |
+
222: (51.5732, -0.4125, "Ruislip Manor"),
|
| 223 |
+
223: (51.523, -0.1244, "Russell Square"),
|
| 224 |
+
224: (51.5822, -0.0749, "Seven Sisters"),
|
| 225 |
+
225: (51.5117, -0.056, "Shadwell"),
|
| 226 |
+
226: (51.5046, -0.2187, "Shepherd's Bush (C)"),
|
| 227 |
+
227: (51.5058, -0.2265, "Shepherd's Bush (H)"),
|
| 228 |
+
228: (51.5227, -0.0708, "Shoreditch"),
|
| 229 |
+
229: (51.4924, -0.1565, "Sloane Square"),
|
| 230 |
+
230: (51.5808, 0.0216, "Snaresbrook"),
|
| 231 |
+
231: (51.4454, -0.2066, "Southfields"),
|
| 232 |
+
234: (51.5011, -0.3072, "South Ealing"),
|
| 233 |
+
235: (51.5646, -0.3521, "South Harrow"),
|
| 234 |
+
236: (51.4941, -0.1738, "South Kensington"),
|
| 235 |
+
237: (51.5701, -0.3081, "South Kenton"),
|
| 236 |
+
238: (51.5007, -0.0191, "South Quay"),
|
| 237 |
+
239: (51.5569, -0.3988, "South Ruislip"),
|
| 238 |
+
240: (51.4154, -0.1919, "South Wimbledon"),
|
| 239 |
+
241: (51.5917, 0.0275, "South Woodford"),
|
| 240 |
+
242: (51.495, -0.2459, "Stamford Brook"),
|
| 241 |
+
243: (51.6194, -0.3028, "Stanmore"),
|
| 242 |
+
244: (51.5221, -0.047, "Stepney Green"),
|
| 243 |
+
245: (51.4723, -0.123, "Stockwell"),
|
| 244 |
+
246: (51.5439, -0.2759, "Stonebridge Park"),
|
| 245 |
+
247: (51.5416, -0.0042, "Stratford"),
|
| 246 |
+
248: (51.4994, -0.1335, "St. James's Park"),
|
| 247 |
+
249: (51.5347, -0.174, "St. John's Wood"),
|
| 248 |
+
250: (51.5146, -0.0973, "St. Paul's"),
|
| 249 |
+
251: (51.5569, -0.3366, "Sudbury Hill"),
|
| 250 |
+
252: (51.5507, -0.3156, "Sudbury Town"),
|
| 251 |
+
253: (51.4933, -0.0478, "Surrey Quays"),
|
| 252 |
+
254: (51.5432, -0.1738, "Swiss Cottage"),
|
| 253 |
+
255: (51.5111, -0.1141, "Temple"),
|
| 254 |
+
257: (51.4361, -0.1598, "Tooting Bec"),
|
| 255 |
+
258: (51.4275, -0.168, "Tooting Broadway"),
|
| 256 |
+
259: (51.5165, -0.131, "Tottenham Court Road"),
|
| 257 |
+
260: (51.5882, -0.0594, "Tottenham Hale"),
|
| 258 |
+
262: (51.5106, -0.0743, "Tower Gateway"),
|
| 259 |
+
263: (51.5098, -0.0766, "Tower Hill"),
|
| 260 |
+
264: (51.5567, -0.1374, "Tufnell Park"),
|
| 261 |
+
265: (51.4951, -0.2547, "Turnham Green"),
|
| 262 |
+
266: (51.5904, -0.1028, "Turnpike Lane"),
|
| 263 |
+
267: (51.559, 0.251, "Upminster"),
|
| 264 |
+
268: (51.5582, 0.2343, "Upminster Bridge"),
|
| 265 |
+
269: (51.5385, 0.1014, "Upney"),
|
| 266 |
+
270: (51.5352, 0.0343, "Upton Park"),
|
| 267 |
+
271: (51.5463, -0.4786, "Uxbridge"),
|
| 268 |
+
272: (51.4861, -0.1253, "Vauxhall"),
|
| 269 |
+
273: (51.4965, -0.1447, "Victoria"),
|
| 270 |
+
274: (51.583, -0.0195, "Walthamstow Central"),
|
| 271 |
+
275: (51.5775, 0.0288, "Wanstead"),
|
| 272 |
+
276: (51.5043, -0.0558, "Wapping"),
|
| 273 |
+
277: (51.5247, -0.1384, "Warren Street"),
|
| 274 |
+
278: (51.5235, -0.1835, "Warwick Avenue"),
|
| 275 |
+
279: (51.5036, -0.1143, "Waterloo"),
|
| 276 |
+
281: (51.5519, -0.2963, "Wembley Central"),
|
| 277 |
+
282: (51.5635, -0.2795, "Wembley Park"),
|
| 278 |
+
283: (51.521, -0.2011, "Westbourne Park"),
|
| 279 |
+
284: (51.5097, -0.0265, "Westferry"),
|
| 280 |
+
285: (51.501, -0.1254, "Westminster"),
|
| 281 |
+
286: (51.518, -0.2809, "West Acton"),
|
| 282 |
+
287: (51.4872, -0.1953, "West Brompton"),
|
| 283 |
+
288: (51.6095, -0.1883, "West Finchley"),
|
| 284 |
+
289: (51.5287, 0.0056, "West Ham"),
|
| 285 |
+
290: (51.5469, -0.1906, "West Hampstead"),
|
| 286 |
+
291: (51.5795, -0.3533, "West Harrow"),
|
| 287 |
+
292: (51.507, -0.0203, "West India Quay"),
|
| 288 |
+
293: (51.4907, -0.2065, "West Kensington"),
|
| 289 |
+
294: (51.5696, -0.4376, "West Ruislip"),
|
| 290 |
+
295: (51.5194, -0.0612, "Whitechapel"),
|
| 291 |
+
296: (51.512, -0.2239, "White City"),
|
| 292 |
+
297: (51.5492, -0.2215, "Willesden Green"),
|
| 293 |
+
298: (51.5326, -0.2478, "Willesden Junction"),
|
| 294 |
+
299: (51.4214, -0.2064, "Wimbledon"),
|
| 295 |
+
300: (51.4343, -0.1992, "Wimbledon Park"),
|
| 296 |
+
301: (51.607, 0.0341, "Woodford"),
|
| 297 |
+
302: (51.6179, -0.1856, "Woodside Park"),
|
| 298 |
+
303: (51.5975, -0.1097, "Wood Green"),
|
| 299 |
+
35: (51.4627, -0.1145, "Brixton"),
|
| 300 |
+
6: (51.6736, -0.607, "Amersham"),
|
| 301 |
+
23: (51.4979, -0.0637, "Bermondsey"),
|
| 302 |
+
50: (51.7052, -0.611, "Chesham"),
|
| 303 |
+
46: (51.6679, -0.561, "Chalfont & Latimer"),
|
| 304 |
+
53: (51.6543, -0.5183, "Chorleywood"),
|
| 305 |
+
214: (51.6404, -0.4733, "Rickmansworth"),
|
| 306 |
+
62: (51.647, -0.4412, "Croxley"),
|
| 307 |
+
280: (51.6573, -0.4177, "Watford"),
|
| 308 |
+
221: (51.5606, -0.4103, "Ruislip Gardens"),
|
| 309 |
+
121: (51.6503, -0.1943, "High Barnet"),
|
| 310 |
+
261: (51.6302, -0.1791, "Totteridge & Whetstone"),
|
| 311 |
+
57: (51.6517, -0.1496, "Cockfosters"),
|
| 312 |
+
187: (51.6476, -0.1318, "Oakwood"),
|
| 313 |
+
232: (51.6322, -0.128, "Southgate"),
|
| 314 |
+
88: (51.6937, 0.1139, "Epping"),
|
| 315 |
+
256: (51.6717, 0.1033, "Theydon Bois"),
|
| 316 |
+
68: (51.6455, 0.0838, "Debden"),
|
| 317 |
+
158: (51.6412, 0.0558, "Loughton"),
|
| 318 |
+
37: (51.6266, 0.0471, "Buckhurst Hill"),
|
| 319 |
+
204: (51.5343, -0.0139, "Pudding Mill Lane"),
|
| 320 |
+
233: (51.501, -0.1052, "Southwark"),
|
| 321 |
+
41: (51.4982, -0.0502, "Canada Water"),
|
| 322 |
+
43: (51.5147, 0.0082, "Canning Town"),
|
| 323 |
+
183: (51.5005, 0.0039, "North Greenwich"),
|
| 324 |
+
64: (51.4827, -0.0096, "Cutty Sark"),
|
| 325 |
+
106: (51.4781, -0.0149, "Greenwich"),
|
| 326 |
+
69: (51.474, -0.0216, "Deptford Bridge"),
|
| 327 |
+
86: (51.4693, -0.0174, "Elverson Road"),
|
| 328 |
+
152: (51.4657, -0.0142, "Lewisham"),
|
| 329 |
+
174: (51.4767, -0.0327, "New Cross"),
|
| 330 |
+
175: (51.4757, -0.0402, "New Cross Gate"),
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
london_underground_lines = { # line: "name"
|
| 334 |
+
1: "Bakerloo Line",
|
| 335 |
+
3: "Circle Line",
|
| 336 |
+
6: "Hammersmith & City Line",
|
| 337 |
+
7: "Jubilee Line",
|
| 338 |
+
11: "Victoria Line",
|
| 339 |
+
2: "Central Line",
|
| 340 |
+
4: "District Line",
|
| 341 |
+
5: "East London Line",
|
| 342 |
+
8: "Metropolitan Line",
|
| 343 |
+
9: "Northern Line",
|
| 344 |
+
10: "Piccadilly Line",
|
| 345 |
+
12: "Waterloo & City Line",
|
| 346 |
+
13: "Docklands Light Railway",
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
london_underground_connections = [ # (station1,station2), (line, time)
|
| 350 |
+
(11, 163), (1, 1),
|
| 351 |
+
(11, 212), (1, 2),
|
| 352 |
+
(49, 87), (1, 1),
|
| 353 |
+
(49, 197), (1, 2),
|
| 354 |
+
(82, 163), (1, 2),
|
| 355 |
+
(82, 193), (1, 3),
|
| 356 |
+
(84, 148), (1, 3),
|
| 357 |
+
(87, 279), (1, 2),
|
| 358 |
+
(113, 246), (1, 2),
|
| 359 |
+
(113, 298), (1, 2),
|
| 360 |
+
(114, 140), (1, 2),
|
| 361 |
+
(137, 206), (1, 3),
|
| 362 |
+
(137, 298), (1, 3),
|
| 363 |
+
(140, 237), (1, 2),
|
| 364 |
+
(143, 159), (1, 2),
|
| 365 |
+
(143, 206), (1, 2),
|
| 366 |
+
(148, 279), (1, 1),
|
| 367 |
+
(159, 278), (1, 1),
|
| 368 |
+
(185, 237), (1, 2),
|
| 369 |
+
(185, 281), (1, 2),
|
| 370 |
+
(192, 197), (1, 2),
|
| 371 |
+
(192, 212), (1, 2),
|
| 372 |
+
(193, 278), (1, 2),
|
| 373 |
+
(246, 281), (1, 3),
|
| 374 |
+
(13, 156), (2, 2),
|
| 375 |
+
(13, 250), (2, 2),
|
| 376 |
+
(16, 91), (2, 2),
|
| 377 |
+
(16, 173), (2, 2),
|
| 378 |
+
(24, 156), (2, 3),
|
| 379 |
+
(24, 164), (2, 2),
|
| 380 |
+
(28, 162), (2, 1),
|
| 381 |
+
(28, 192), (2, 1),
|
| 382 |
+
(37, 158), (2, 3),
|
| 383 |
+
(37, 301), (2, 3),
|
| 384 |
+
(48, 126), (2, 1),
|
| 385 |
+
(48, 250), (2, 2),
|
| 386 |
+
(51, 103), (2, 2),
|
| 387 |
+
(51, 215), (2, 2),
|
| 388 |
+
(68, 158), (2, 2),
|
| 389 |
+
(68, 256), (2, 3),
|
| 390 |
+
(72, 286), (2, 3),
|
| 391 |
+
(76, 181), (2, 2),
|
| 392 |
+
(76, 296), (2, 3),
|
| 393 |
+
(88, 256), (2, 2),
|
| 394 |
+
(91, 109), (2, 2),
|
| 395 |
+
(98, 173), (2, 3),
|
| 396 |
+
(98, 211), (2, 2),
|
| 397 |
+
(103, 109), (2, 3),
|
| 398 |
+
(105, 177), (2, 2),
|
| 399 |
+
(105, 196), (2, 2),
|
| 400 |
+
(112, 181), (2, 3),
|
| 401 |
+
(112, 196), (2, 2),
|
| 402 |
+
(126, 259), (2, 2),
|
| 403 |
+
(127, 186), (2, 2),
|
| 404 |
+
(127, 226), (2, 1),
|
| 405 |
+
(149, 162), (2, 3),
|
| 406 |
+
(149, 208), (2, 1),
|
| 407 |
+
(153, 154), (2, 3),
|
| 408 |
+
(153, 247), (2, 2),
|
| 409 |
+
(154, 230), (2, 2),
|
| 410 |
+
(154, 275), (2, 2),
|
| 411 |
+
(164, 247), (2, 4),
|
| 412 |
+
(177, 239), (2, 3),
|
| 413 |
+
(181, 286), (2, 2),
|
| 414 |
+
(186, 208), (2, 2),
|
| 415 |
+
(192, 259), (2, 2),
|
| 416 |
+
(211, 275), (2, 2),
|
| 417 |
+
(215, 301), (2, 3),
|
| 418 |
+
(221, 239), (2, 1),
|
| 419 |
+
(221, 294), (2, 3),
|
| 420 |
+
(226, 296), (2, 3),
|
| 421 |
+
(230, 241), (2, 2),
|
| 422 |
+
(241, 301), (2, 2),
|
| 423 |
+
(2, 156), (3, 2),
|
| 424 |
+
(2, 263), (3, 4),
|
| 425 |
+
(11, 83), (3, 3),
|
| 426 |
+
(11, 104), (3, 3),
|
| 427 |
+
(14, 92), (3, 1),
|
| 428 |
+
(14, 167), (3, 2),
|
| 429 |
+
(18, 186), (3, 2),
|
| 430 |
+
(18, 193), (3, 2),
|
| 431 |
+
(25, 161), (3, 1),
|
| 432 |
+
(25, 255), (3, 2),
|
| 433 |
+
(44, 161), (3, 1),
|
| 434 |
+
(44, 166), (3, 2),
|
| 435 |
+
(83, 193), (3, 3),
|
| 436 |
+
(87, 255), (3, 2),
|
| 437 |
+
(87, 285), (3, 2),
|
| 438 |
+
(90, 104), (3, 2),
|
| 439 |
+
(90, 145), (3, 2),
|
| 440 |
+
(92, 145), (3, 4),
|
| 441 |
+
(99, 122), (3, 4),
|
| 442 |
+
(99, 236), (3, 1),
|
| 443 |
+
(122, 186), (3, 3),
|
| 444 |
+
(156, 167), (3, 2),
|
| 445 |
+
(166, 263), (3, 2),
|
| 446 |
+
(229, 236), (3, 2),
|
| 447 |
+
(229, 273), (3, 2),
|
| 448 |
+
(248, 273), (3, 2),
|
| 449 |
+
(248, 285), (3, 2),
|
| 450 |
+
(3, 263), (4, 2),
|
| 451 |
+
(3, 295), (4, 2),
|
| 452 |
+
(15, 78), (4, 4),
|
| 453 |
+
(15, 269), (4, 2),
|
| 454 |
+
(17, 110), (4, 1),
|
| 455 |
+
(17, 293), (4, 2),
|
| 456 |
+
(18, 186), (4, 2),
|
| 457 |
+
(18, 193), (4, 2),
|
| 458 |
+
(21, 67), (4, 3),
|
| 459 |
+
(21, 269), (4, 2),
|
| 460 |
+
(25, 161), (4, 1),
|
| 461 |
+
(25, 255), (4, 2),
|
| 462 |
+
(33, 36), (4, 2),
|
| 463 |
+
(33, 164), (4, 1),
|
| 464 |
+
(36, 289), (4, 2),
|
| 465 |
+
(44, 161), (4, 1),
|
| 466 |
+
(44, 166), (4, 2),
|
| 467 |
+
(52, 1), (4, 2),
|
| 468 |
+
(52, 265), (4, 2),
|
| 469 |
+
(66, 67), (4, 4),
|
| 470 |
+
(66, 85), (4, 3),
|
| 471 |
+
(72, 73), (4, 4),
|
| 472 |
+
(73, 1), (4, 2),
|
| 473 |
+
(74, 99), (4, 3),
|
| 474 |
+
(74, 122), (4, 3),
|
| 475 |
+
(74, 138), (4, 2),
|
| 476 |
+
(74, 287), (4, 2),
|
| 477 |
+
(74, 293), (4, 1),
|
| 478 |
+
(78, 270), (4, 2),
|
| 479 |
+
(80, 205), (4, 2),
|
| 480 |
+
(80, 231), (4, 2),
|
| 481 |
+
(83, 193), (4, 3),
|
| 482 |
+
(85, 129), (4, 2),
|
| 483 |
+
(87, 255), (4, 2),
|
| 484 |
+
(87, 285), (4, 2),
|
| 485 |
+
(96, 195), (4, 2),
|
| 486 |
+
(96, 287), (4, 1),
|
| 487 |
+
(99, 236), (4, 1),
|
| 488 |
+
(108, 141), (4, 3),
|
| 489 |
+
(108, 265), (4, 3),
|
| 490 |
+
(110, 209), (4, 2),
|
| 491 |
+
(122, 186), (4, 3),
|
| 492 |
+
(129, 268), (4, 2),
|
| 493 |
+
(141, 213), (4, 3),
|
| 494 |
+
(164, 244), (4, 2),
|
| 495 |
+
(166, 263), (4, 2),
|
| 496 |
+
(195, 205), (4, 3),
|
| 497 |
+
(200, 270), (4, 2),
|
| 498 |
+
(200, 289), (4, 2),
|
| 499 |
+
(209, 242), (4, 2),
|
| 500 |
+
(229, 236), (4, 2),
|
| 501 |
+
(229, 273), (4, 2),
|
| 502 |
+
(231, 300), (4, 3),
|
| 503 |
+
(242, 265), (4, 1),
|
| 504 |
+
(244, 295), (4, 3),
|
| 505 |
+
(248, 273), (4, 2),
|
| 506 |
+
(248, 285), (4, 2),
|
| 507 |
+
(267, 268), (4, 3),
|
| 508 |
+
(299, 300), (4, 3),
|
| 509 |
+
(4, 70), (13, 2),
|
| 510 |
+
(4, 201), (13, 2),
|
| 511 |
+
(13, 225), (13, 2),
|
| 512 |
+
(19, 97), (13, 2),
|
| 513 |
+
(20, 65), (13, 2),
|
| 514 |
+
(20, 217), (13, 2),
|
| 515 |
+
(27, 79), (13, 2),
|
| 516 |
+
(27, 201), (13, 2),
|
| 517 |
+
(32, 70), (13, 2),
|
| 518 |
+
(32, 204), (13, 2),
|
| 519 |
+
(42, 120), (13, 2),
|
| 520 |
+
(42, 292), (13, 2),
|
| 521 |
+
(43, 79), (13, 2),
|
| 522 |
+
(43, 219), (13, 2),
|
| 523 |
+
(61, 171), (13, 2),
|
| 524 |
+
(61, 238), (13, 2),
|
| 525 |
+
(63, 203), (13, 2),
|
| 526 |
+
(63, 219), (13, 2),
|
| 527 |
+
(64, 106), (13, 2),
|
| 528 |
+
(64, 135), (13, 2),
|
| 529 |
+
(65, 97), (13, 2),
|
| 530 |
+
(69, 86), (13, 2),
|
| 531 |
+
(69, 106), (13, 2),
|
| 532 |
+
(86, 152), (13, 2),
|
| 533 |
+
(120, 238), (13, 2),
|
| 534 |
+
(135, 171), (13, 2),
|
| 535 |
+
(155, 225), (13, 2),
|
| 536 |
+
(155, 284), (13, 2),
|
| 537 |
+
(201, 284), (13, 2),
|
| 538 |
+
(201, 292), (13, 2),
|
| 539 |
+
(203, 217), (13, 2),
|
| 540 |
+
(204, 247), (13, 2),
|
| 541 |
+
(225, 262), (13, 2),
|
| 542 |
+
(284, 292), (13, 2),
|
| 543 |
+
(41, 216), (5, 1),
|
| 544 |
+
(41, 253), (5, 2),
|
| 545 |
+
(174, 253), (5, 4),
|
| 546 |
+
(175, 253), (5, 4),
|
| 547 |
+
(216, 276), (5, 1),
|
| 548 |
+
(225, 276), (5, 1),
|
| 549 |
+
(225, 295), (5, 2),
|
| 550 |
+
(228, 295), (5, 2),
|
| 551 |
+
(3, 156), (6, 4),
|
| 552 |
+
(3, 295), (6, 2),
|
| 553 |
+
(11, 83), (6, 3),
|
| 554 |
+
(11, 104), (6, 3),
|
| 555 |
+
(14, 92), (6, 1),
|
| 556 |
+
(14, 167), (6, 2),
|
| 557 |
+
(15, 78), (6, 4),
|
| 558 |
+
(33, 36), (6, 2),
|
| 559 |
+
(33, 164), (6, 1),
|
| 560 |
+
(36, 289), (6, 2),
|
| 561 |
+
(78, 270), (6, 2),
|
| 562 |
+
(83, 193), (6, 4),
|
| 563 |
+
(90, 104), (6, 2),
|
| 564 |
+
(90, 145), (6, 2),
|
| 565 |
+
(92, 145), (6, 4),
|
| 566 |
+
(101, 110), (6, 2),
|
| 567 |
+
(101, 227), (6, 1),
|
| 568 |
+
(147, 150), (6, 1),
|
| 569 |
+
(147, 283), (6, 2),
|
| 570 |
+
(150, 227), (6, 2),
|
| 571 |
+
(156, 167), (6, 2),
|
| 572 |
+
(164, 244), (6, 2),
|
| 573 |
+
(193, 218), (6, 1),
|
| 574 |
+
(200, 270), (6, 2),
|
| 575 |
+
(200, 289), (6, 2),
|
| 576 |
+
(218, 283), (6, 2),
|
| 577 |
+
(244, 295), (6, 3),
|
| 578 |
+
(11, 28), (7, 2),
|
| 579 |
+
(11, 249), (7, 4),
|
| 580 |
+
(23, 41), (7, 2),
|
| 581 |
+
(23, 157), (7, 3),
|
| 582 |
+
(28, 107), (7, 2),
|
| 583 |
+
(41, 42), (7, 3),
|
| 584 |
+
(42, 183), (7, 3),
|
| 585 |
+
(43, 183), (7, 3),
|
| 586 |
+
(43, 289), (7, 3),
|
| 587 |
+
(45, 207), (7, 2),
|
| 588 |
+
(45, 243), (7, 2),
|
| 589 |
+
(71, 172), (7, 2),
|
| 590 |
+
(71, 297), (7, 2),
|
| 591 |
+
(94, 254), (7, 2),
|
| 592 |
+
(94, 290), (7, 1),
|
| 593 |
+
(107, 285), (7, 3),
|
| 594 |
+
(142, 290), (7, 2),
|
| 595 |
+
(142, 297), (7, 2),
|
| 596 |
+
(144, 207), (7, 2),
|
| 597 |
+
(144, 282), (7, 4),
|
| 598 |
+
(157, 233), (7, 2),
|
| 599 |
+
(172, 282), (7, 4),
|
| 600 |
+
(233, 279), (7, 1),
|
| 601 |
+
(247, 289), (7, 3),
|
| 602 |
+
(249, 254), (7, 1),
|
| 603 |
+
(279, 285), (7, 2),
|
| 604 |
+
(2, 156), (8, 2),
|
| 605 |
+
(6, 46), (8, 4),
|
| 606 |
+
(11, 94), (8, 6),
|
| 607 |
+
(11, 104), (8, 3),
|
| 608 |
+
(14, 92), (8, 1),
|
| 609 |
+
(14, 167), (8, 2),
|
| 610 |
+
(46, 50), (8, 8),
|
| 611 |
+
(46, 53), (8, 4),
|
| 612 |
+
(53, 214), (8, 4),
|
| 613 |
+
(62, 168), (8, 4),
|
| 614 |
+
(62, 280), (8, 3),
|
| 615 |
+
(75, 210), (8, 2),
|
| 616 |
+
(75, 222), (8, 2),
|
| 617 |
+
(90, 104), (8, 2),
|
| 618 |
+
(90, 145), (8, 2),
|
| 619 |
+
(92, 145), (8, 4),
|
| 620 |
+
(94, 282), (8, 7),
|
| 621 |
+
(115, 178), (8, 2),
|
| 622 |
+
(115, 184), (8, 3),
|
| 623 |
+
(115, 291), (8, 2),
|
| 624 |
+
(125, 134), (8, 2),
|
| 625 |
+
(125, 271), (8, 3),
|
| 626 |
+
(134, 220), (8, 3),
|
| 627 |
+
(156, 167), (8, 2),
|
| 628 |
+
(168, 179), (8, 3),
|
| 629 |
+
(168, 214), (8, 4),
|
| 630 |
+
(178, 202), (8, 2),
|
| 631 |
+
(179, 180), (8, 2),
|
| 632 |
+
(180, 199), (8, 2),
|
| 633 |
+
(184, 199), (8, 3),
|
| 634 |
+
(202, 282), (8, 3),
|
| 635 |
+
(210, 291), (8, 3),
|
| 636 |
+
(220, 222), (8, 2),
|
| 637 |
+
(7, 145), (9, 2),
|
| 638 |
+
(7, 188), (9, 3),
|
| 639 |
+
(8, 124), (9, 3),
|
| 640 |
+
(8, 264), (9, 2),
|
| 641 |
+
(12, 56), (9, 2),
|
| 642 |
+
(12, 257), (9, 1),
|
| 643 |
+
(13, 157), (9, 2),
|
| 644 |
+
(13, 167), (9, 3),
|
| 645 |
+
(22, 47), (9, 2),
|
| 646 |
+
(22, 111), (9, 2),
|
| 647 |
+
(29, 84), (9, 1),
|
| 648 |
+
(29, 157), (9, 2),
|
| 649 |
+
(34, 100), (9, 3),
|
| 650 |
+
(34, 119), (9, 2),
|
| 651 |
+
(38, 58), (9, 2),
|
| 652 |
+
(38, 81), (9, 3),
|
| 653 |
+
(40, 47), (9, 2),
|
| 654 |
+
(40, 89), (9, 3),
|
| 655 |
+
(40, 139), (9, 2),
|
| 656 |
+
(40, 170), (9, 1),
|
| 657 |
+
(49, 87), (9, 1),
|
| 658 |
+
(49, 151), (9, 2),
|
| 659 |
+
(54, 55), (9, 2),
|
| 660 |
+
(54, 56), (9, 2),
|
| 661 |
+
(55, 245), (9, 1),
|
| 662 |
+
(58, 119), (9, 3),
|
| 663 |
+
(59, 240), (9, 2),
|
| 664 |
+
(59, 258), (9, 2),
|
| 665 |
+
(77, 93), (9, 4),
|
| 666 |
+
(77, 124), (9, 2),
|
| 667 |
+
(84, 136), (9, 2),
|
| 668 |
+
(87, 279), (9, 2),
|
| 669 |
+
(89, 145), (9, 2),
|
| 670 |
+
(89, 170), (9, 2),
|
| 671 |
+
(89, 277), (9, 1),
|
| 672 |
+
(93, 165), (9, 3),
|
| 673 |
+
(93, 288), (9, 2),
|
| 674 |
+
(100, 111), (9, 4),
|
| 675 |
+
(102, 259), (9, 1),
|
| 676 |
+
(102, 277), (9, 2),
|
| 677 |
+
(121, 261), (9, 3),
|
| 678 |
+
(136, 191), (9, 2),
|
| 679 |
+
(136, 279), (9, 3),
|
| 680 |
+
(139, 264), (9, 2),
|
| 681 |
+
(151, 259), (9, 1),
|
| 682 |
+
(167, 188), (9, 1),
|
| 683 |
+
(169, 240), (9, 4),
|
| 684 |
+
(191, 245), (9, 3),
|
| 685 |
+
(257, 258), (9, 2),
|
| 686 |
+
(261, 302), (9, 3),
|
| 687 |
+
(288, 302), (9, 1),
|
| 688 |
+
(1, 73), (10, 2),
|
| 689 |
+
(1, 234), (10, 4),
|
| 690 |
+
(1, 265), (10, 3),
|
| 691 |
+
(5, 194), (10, 3),
|
| 692 |
+
(5, 252), (10, 2),
|
| 693 |
+
(9, 31), (10, 3),
|
| 694 |
+
(9, 232), (10, 3),
|
| 695 |
+
(10, 95), (10, 2),
|
| 696 |
+
(10, 128), (10, 1),
|
| 697 |
+
(17, 74), (10, 3),
|
| 698 |
+
(17, 110), (10, 2),
|
| 699 |
+
(30, 176), (10, 2),
|
| 700 |
+
(30, 190), (10, 3),
|
| 701 |
+
(31, 303), (10, 2),
|
| 702 |
+
(39, 128), (10, 2),
|
| 703 |
+
(39, 145), (10, 5),
|
| 704 |
+
(57, 187), (10, 4),
|
| 705 |
+
(60, 126), (10, 1),
|
| 706 |
+
(60, 151), (10, 1),
|
| 707 |
+
(73, 182), (10, 3),
|
| 708 |
+
(74, 99), (10, 2),
|
| 709 |
+
(75, 210), (10, 2),
|
| 710 |
+
(75, 222), (10, 2),
|
| 711 |
+
(95, 160), (10, 2),
|
| 712 |
+
(99, 236), (10, 1),
|
| 713 |
+
(107, 133), (10, 2),
|
| 714 |
+
(107, 197), (10, 1),
|
| 715 |
+
(110, 265), (10, 2),
|
| 716 |
+
(116, 117), (10, 3),
|
| 717 |
+
(116, 118), (10, 3),
|
| 718 |
+
(116, 132), (10, 4),
|
| 719 |
+
(117, 118), (10, 5),
|
| 720 |
+
(125, 134), (10, 2),
|
| 721 |
+
(125, 271), (10, 3),
|
| 722 |
+
(126, 223), (10, 2),
|
| 723 |
+
(130, 131), (10, 2),
|
| 724 |
+
(130, 132), (10, 2),
|
| 725 |
+
(131, 190), (10, 2),
|
| 726 |
+
(133, 146), (10, 2),
|
| 727 |
+
(134, 220), (10, 3),
|
| 728 |
+
(145, 223), (10, 2),
|
| 729 |
+
(146, 236), (10, 3),
|
| 730 |
+
(151, 197), (10, 2),
|
| 731 |
+
(160, 266), (10, 3),
|
| 732 |
+
(176, 234), (10, 1),
|
| 733 |
+
(182, 194), (10, 2),
|
| 734 |
+
(187, 232), (10, 3),
|
| 735 |
+
(210, 235), (10, 3),
|
| 736 |
+
(220, 222), (10, 2),
|
| 737 |
+
(235, 251), (10, 2),
|
| 738 |
+
(251, 252), (10, 3),
|
| 739 |
+
(266, 303), (10, 2),
|
| 740 |
+
(26, 260), (11, 2),
|
| 741 |
+
(26, 274), (11, 2),
|
| 742 |
+
(35, 245), (11, 2),
|
| 743 |
+
(89, 145), (11, 2),
|
| 744 |
+
(89, 277), (11, 1),
|
| 745 |
+
(95, 123), (11, 2),
|
| 746 |
+
(95, 224), (11, 4),
|
| 747 |
+
(107, 192), (11, 2),
|
| 748 |
+
(107, 273), (11, 2),
|
| 749 |
+
(123, 145), (11, 4),
|
| 750 |
+
(192, 277), (11, 2),
|
| 751 |
+
(198, 272), (11, 1),
|
| 752 |
+
(198, 273), (11, 3),
|
| 753 |
+
(224, 260), (11, 3),
|
| 754 |
+
(245, 272), (11, 3),
|
| 755 |
+
(13, 279), (12, 4),
|
| 756 |
+
]
|
| 757 |
+
|
| 758 |
+
g = Graph()
|
| 759 |
+
|
| 760 |
+
for sid, station in london_underground_stations.items():
|
| 761 |
+
g.add_node(sid, obj=station)
|
| 762 |
+
for i in range(0,len(london_underground_connections),2):
|
| 763 |
+
n1,n2 = london_underground_connections[i]
|
| 764 |
+
_,time = london_underground_connections[i+1]
|
| 765 |
+
g.add_edge(n1, n2, time, bidirectional=True)
|
| 766 |
+
return g
|
graph-theory/source/examples/images/2500pxLondon_Underground_Overground.png
ADDED
|
Git LFS Details
|
graph-theory/source/examples/images/3-3-cart-traffic-jam.png
ADDED
|
graph-theory/source/examples/images/3-3-cart-traffic-jam_initial.png
ADDED
|
graph-theory/source/examples/images/3-3-traffic-jam-road-map.png
ADDED
|
graph-theory/source/examples/images/3-3-tree-of-states.png
ADDED
|
graph-theory/source/examples/images/6nodes.png
ADDED
|
graph-theory/source/examples/images/Torniqueterevolution.jpg
ADDED
|
graph-theory/source/examples/images/cpm_w_artificial_dependency.png
ADDED
|
graph-theory/source/examples/images/cpm_wo_artificial_dependency.png
ADDED
|
graph-theory/source/examples/images/easy_sudoku.png
ADDED
|
graph-theory/source/examples/images/easy_sudoku3.png
ADDED
|
graph-theory/source/examples/images/movement_graph.png
ADDED
|
graph-theory/source/examples/images/search_tree.png
ADDED
|
graph-theory/source/examples/images/sudoku-wave-collapse-1.png
ADDED
|
graph-theory/source/examples/images/sudoku_solver1.png
ADDED
|
graph-theory/source/examples/images/tjs-map-loads.png
ADDED
|
graph-theory/source/examples/images/tjs-map.png
ADDED
|
graph-theory/source/examples/images/tjs_problem_w_distance_restrictions.png
ADDED
|
graph-theory/source/examples/images/traffic_bi_directional.gif
ADDED
|
Git LFS Details
|
graph-theory/source/examples/images/turnstile.png
ADDED
|
graph-theory/source/examples/readme.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Graph-theory examples
|
| 2 |
+
|
| 3 |
+
This folder contains jupyter notebooks, with examples from
|
| 4 |
+
graph-theory.
|
| 5 |
+
|
| 6 |
+
If you're new to the field I would recommend to study (loosely)
|
| 7 |
+
in the order below:
|
| 8 |
+
|
| 9 |
+
***[basic graph theory](basic%20graph%20theory.ipynb)***: An introduction to the fundamental
|
| 10 |
+
terminology of graph-theory. Topics are:
|
| 11 |
+
|
| 12 |
+
nodes
|
| 13 |
+
edges
|
| 14 |
+
indegree
|
| 15 |
+
outdegree
|
| 16 |
+
has_path
|
| 17 |
+
distance path
|
| 18 |
+
is a subgraph
|
| 19 |
+
examples of existing graphs available for testing.
|
| 20 |
+
|
| 21 |
+
***[generating and visualising graphs](generating%20and%20visualising%20graphs.ipynb)***: An introduction
|
| 22 |
+
to making random xy graphs, grids and visualise them.
|
| 23 |
+
|
| 24 |
+
***[comparing graphs](comparing%20graphs.ipynb)***: An overview of methods for comparing
|
| 25 |
+
graphs, such as:
|
| 26 |
+
|
| 27 |
+
topological sort
|
| 28 |
+
phase lines
|
| 29 |
+
graph-hash
|
| 30 |
+
flow_graph_hash
|
| 31 |
+
merkle-tree
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
***[solving search problems](solving%20search%20problems.ipynb)***: An introduction to different
|
| 35 |
+
methods for findings paths, including:
|
| 36 |
+
|
| 37 |
+
adjacency matrix
|
| 38 |
+
BFS
|
| 39 |
+
DFS
|
| 40 |
+
DFScan
|
| 41 |
+
bidi-BFS
|
| 42 |
+
TSP
|
| 43 |
+
[critical path method]
|
| 44 |
+
find loops
|
| 45 |
+
|
| 46 |
+
***[calculating statistics about graphs](statistics%20on%20graphs.ipynb)*** provides an overview
|
| 47 |
+
of common analysis of graphs, such as:
|
| 48 |
+
|
| 49 |
+
components
|
| 50 |
+
has cycles
|
| 51 |
+
network size
|
| 52 |
+
is partite
|
| 53 |
+
degree of separation
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
***[solving transport problems](solving%20search%20problems.ipynb)*** provides tools for a wide range
|
| 57 |
+
of problems where discrete transport is essential.
|
| 58 |
+
|
| 59 |
+
minmax
|
| 60 |
+
minsum
|
| 61 |
+
shortest_tree all pairs.
|
| 62 |
+
scheduling problem
|
| 63 |
+
traffic scheduling problem
|
| 64 |
+
jam solver
|
| 65 |
+
trans shipment problem (needs rewrite)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
***[solving flow problems](solving%20flow%20problems.ipynb)*** provides tools for solving a
|
| 69 |
+
wide range of problems where continuous flow are central.
|
| 70 |
+
|
| 71 |
+
max flow
|
| 72 |
+
max flow min cut
|
| 73 |
+
min cost flow
|
| 74 |
+
all_simple_paths
|
| 75 |
+
all_paths
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
***[solving assignment problems](solving%20assignment%20problems.ipynb)*** provides tools for solving
|
| 79 |
+
any kind of assignment problem.
|
| 80 |
+
|
| 81 |
+
assignment problem
|
| 82 |
+
wtap
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
***[representing systems as graphs](graphs%20as%20finite%20state%20machines.ipynb)*** provides a use case
|
| 86 |
+
for using `graph as finite state machine`.
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
graph-theory/source/examples/solving assignment problems.ipynb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {
|
| 7 |
+
"collapsed": true
|
| 8 |
+
},
|
| 9 |
+
"outputs": [],
|
| 10 |
+
"source": [
|
| 11 |
+
""
|
| 12 |
+
]
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"metadata": {
|
| 16 |
+
"kernelspec": {
|
| 17 |
+
"display_name": "Python 3",
|
| 18 |
+
"language": "python",
|
| 19 |
+
"name": "python3"
|
| 20 |
+
},
|
| 21 |
+
"language_info": {
|
| 22 |
+
"codemirror_mode": {
|
| 23 |
+
"name": "ipython",
|
| 24 |
+
"version": 2
|
| 25 |
+
},
|
| 26 |
+
"file_extension": ".py",
|
| 27 |
+
"mimetype": "text/x-python",
|
| 28 |
+
"name": "python",
|
| 29 |
+
"nbconvert_exporter": "python",
|
| 30 |
+
"pygments_lexer": "ipython2",
|
| 31 |
+
"version": "2.7.6"
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"nbformat": 4,
|
| 35 |
+
"nbformat_minor": 0
|
| 36 |
+
}
|
graph-theory/source/examples/solving flow problems.ipynb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {
|
| 7 |
+
"collapsed": true
|
| 8 |
+
},
|
| 9 |
+
"outputs": [],
|
| 10 |
+
"source": [
|
| 11 |
+
""
|
| 12 |
+
]
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"metadata": {
|
| 16 |
+
"kernelspec": {
|
| 17 |
+
"display_name": "Python 3",
|
| 18 |
+
"language": "python",
|
| 19 |
+
"name": "python3"
|
| 20 |
+
},
|
| 21 |
+
"language_info": {
|
| 22 |
+
"codemirror_mode": {
|
| 23 |
+
"name": "ipython",
|
| 24 |
+
"version": 2
|
| 25 |
+
},
|
| 26 |
+
"file_extension": ".py",
|
| 27 |
+
"mimetype": "text/x-python",
|
| 28 |
+
"name": "python",
|
| 29 |
+
"nbconvert_exporter": "python",
|
| 30 |
+
"pygments_lexer": "ipython2",
|
| 31 |
+
"version": "2.7.6"
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"nbformat": 4,
|
| 35 |
+
"nbformat_minor": 0
|
| 36 |
+
}
|
graph-theory/source/examples/solving search problems.ipynb
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# Solving search problem.\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"An introduction to different methods for findings paths, including:\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"\tadjacency matrix\n",
|
| 12 |
+
"\tBFS\n",
|
| 13 |
+
" find loops\n",
|
| 14 |
+
"\tDFS\n",
|
| 15 |
+
" DFScan\n",
|
| 16 |
+
"\tbidi-BFS\n",
|
| 17 |
+
"\tTSP\n",
|
| 18 |
+
"\t[critical path method]\n",
|
| 19 |
+
"\t\n"
|
| 20 |
+
]
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"cell_type": "markdown",
|
| 24 |
+
"metadata": {},
|
| 25 |
+
"source": [
|
| 26 |
+
"First things first. Let's load the imports for this chapter"
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"cell_type": "code",
|
| 31 |
+
"execution_count": 1,
|
| 32 |
+
"metadata": {},
|
| 33 |
+
"outputs": [],
|
| 34 |
+
"source": [
|
| 35 |
+
"from graph import Graph"
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"cell_type": "markdown",
|
| 40 |
+
"metadata": {},
|
| 41 |
+
"source": [
|
| 42 |
+
"## The adjacency matrix\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"an adjacency matrix is a square matrix used to represent a finite graph. The elements of the matrix indicate whether pairs of vertices are adjacent or not in the graph.\n",
|
| 45 |
+
"\n",
|
| 46 |
+
" The distance from a node to itself is 0 and distance from a node to\n",
|
| 47 |
+
" an unconnected node is defined to be infinite. This does not mean that there\n",
|
| 48 |
+
" is no path from a node to another via other nodes.\n",
|
| 49 |
+
"\n",
|
| 50 |
+
"```\n",
|
| 51 |
+
"Example:\n",
|
| 52 |
+
" g = Graph(from_dict=\n",
|
| 53 |
+
" {1: {2: 3, 3: 8, 5: -4},\n",
|
| 54 |
+
" 2: {4: 1, 5: 7},\n",
|
| 55 |
+
" 3: {2: 4},\n",
|
| 56 |
+
" 4: {1: 2, 3: -5},\n",
|
| 57 |
+
" 5: {4: 6}})\n",
|
| 58 |
+
"\n",
|
| 59 |
+
" adjacency_matrix(g)\n",
|
| 60 |
+
" {1: {1: 0, 2: 3, 3: 8, 4: inf, 5: -4},\n",
|
| 61 |
+
" 2: {1: inf, 2: 0, 3: inf, 4: 1, 5: 7},\n",
|
| 62 |
+
" 3: {1: inf, 2: 4, 3: 0, 4: inf, 5: inf},\n",
|
| 63 |
+
" 4: {1: 2, 2: inf, 3: -5, 4: 0, 5: inf},\n",
|
| 64 |
+
" 5: {1: inf, 2: inf, 3: inf, 4: 6, 5: 0}}\n",
|
| 65 |
+
"```"
|
| 66 |
+
]
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"cell_type": "markdown",
|
| 70 |
+
"metadata": {},
|
| 71 |
+
"source": [
|
| 72 |
+
"The adjacency matrix is very helpful when we want to compute the all pairs shortest path.\n",
|
| 73 |
+
"\n",
|
| 74 |
+
"Find the cost of the shortest path between every pair of vertices in a\n",
|
| 75 |
+
" weighted graph. Uses the Floyd-Warshall algorithm.\n",
|
| 76 |
+
"\n",
|
| 77 |
+
" Example:\n",
|
| 78 |
+
" inf = float('inf')\n",
|
| 79 |
+
" g = Graph(from_dict=(\n",
|
| 80 |
+
" {0: {0: 0, 1: 1, 2: 4},\n",
|
| 81 |
+
" 1: {0: inf, 1: 0, 2: 2},\n",
|
| 82 |
+
" 2: {0: inf, 1: inf, 2: 0}})\n",
|
| 83 |
+
"\n",
|
| 84 |
+
" fw(g)\n",
|
| 85 |
+
" {0: {0: 0, 1: 1, 2: 3},\n",
|
| 86 |
+
" 1: {0: inf, 1: 0, 2: 2},\n",
|
| 87 |
+
" 2: {0: inf, 1: inf, 2: 0}}\n",
|
| 88 |
+
"\n",
|
| 89 |
+
" h = {1: {2: 3, 3: 8, 5: -4},\n",
|
| 90 |
+
" 2: {4: 1, 5: 7},\n",
|
| 91 |
+
" 3: {2: 4},\n",
|
| 92 |
+
" 4: {1: 2, 3: -5},\n",
|
| 93 |
+
" 5: {4: 6}}\n",
|
| 94 |
+
"\n",
|
| 95 |
+
" fw(adj(h)) #\n",
|
| 96 |
+
" {1: {1: 0, 2: 1, 3: -3, 4: 2, 5: -4},\n",
|
| 97 |
+
" 2: {1: 3, 2: 0, 3: -4, 4: 1, 5: -1},\n",
|
| 98 |
+
" 3: {1: 7, 2: 4, 3: 0, 4: 5, 5: 3},\n",
|
| 99 |
+
" 4: {1: 2, 2: -1, 3: -5, 4: 0, 5: -2},\n",
|
| 100 |
+
" 5: {1: 8, 2: 5, 3: 1, 4: 6, 5: 0}}\n"
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"cell_type": "markdown",
|
| 105 |
+
"metadata": {},
|
| 106 |
+
"source": [
|
| 107 |
+
"### Distance maps\n",
|
| 108 |
+
"\n",
|
| 109 |
+
"As you can see the adjacency matrix computes the whole graph, but often this isn't necessary. So I invented the distance map as \"light weight\" version of the adjacency matrix. Here is how it works:\n",
|
| 110 |
+
"\n",
|
| 111 |
+
"We have a 4x4 graph like this:\n",
|
| 112 |
+
"\n",
|
| 113 |
+
" 1 -> 2 -> 3 -> 4\n",
|
| 114 |
+
" | | | |\n",
|
| 115 |
+
" v v v v\n",
|
| 116 |
+
" 5 -> 6 -> 7 -> 8\n",
|
| 117 |
+
" | | | |\n",
|
| 118 |
+
" v v v v\n",
|
| 119 |
+
" 9 -> 10-> 11-> 12\n",
|
| 120 |
+
" | | | |\n",
|
| 121 |
+
" v v v v\n",
|
| 122 |
+
" 13-> 14-> 15-> 16\n",
|
| 123 |
+
"\n",
|
| 124 |
+
"And we would like to go the shortest distance between _some_ points.\n",
|
| 125 |
+
"\n",
|
| 126 |
+
"Arbitrarily we pick 3 or 5 as starts and decide to go to either 12 or 14.\n",
|
| 127 |
+
"\n",
|
| 128 |
+
"The objective is to choose ANY shortest path."
|
| 129 |
+
]
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"cell_type": "code",
|
| 133 |
+
"execution_count": 2,
|
| 134 |
+
"metadata": {},
|
| 135 |
+
"outputs": [
|
| 136 |
+
{
|
| 137 |
+
"data": {
|
| 138 |
+
"text/plain": [
|
| 139 |
+
"{3: 0,\n",
|
| 140 |
+
" 5: 0,\n",
|
| 141 |
+
" 2: 1,\n",
|
| 142 |
+
" 4: 1,\n",
|
| 143 |
+
" 7: 1,\n",
|
| 144 |
+
" 1: 1,\n",
|
| 145 |
+
" 6: 1,\n",
|
| 146 |
+
" 9: 1,\n",
|
| 147 |
+
" 8: 2,\n",
|
| 148 |
+
" 11: 2,\n",
|
| 149 |
+
" 10: 2,\n",
|
| 150 |
+
" 13: 2,\n",
|
| 151 |
+
" 12: 3,\n",
|
| 152 |
+
" 15: 3,\n",
|
| 153 |
+
" 14: 3,\n",
|
| 154 |
+
" 16: 4}"
|
| 155 |
+
]
|
| 156 |
+
},
|
| 157 |
+
"execution_count": 2,
|
| 158 |
+
"metadata": {},
|
| 159 |
+
"output_type": "execute_result"
|
| 160 |
+
}
|
| 161 |
+
],
|
| 162 |
+
"source": [
|
| 163 |
+
"edges = [\n",
|
| 164 |
+
" (1, 2, 1), (2, 3, 1), (3, 4, 1), (1, 5, 1),\n",
|
| 165 |
+
" (5, 6, 1), (6, 7, 1), (7, 8, 1), (2, 6, 1),\n",
|
| 166 |
+
" (3, 7, 1), (4, 8, 1), (5, 9, 1), (9, 10, 1),\n",
|
| 167 |
+
" (10, 11, 1), (11, 12, 1), (6, 10, 1), (7, 11, 1),\n",
|
| 168 |
+
" (8, 12, 1), (9, 13, 1), (13, 14, 1), (14, 15, 1),\n",
|
| 169 |
+
" (15, 16, 1), (10, 14, 1), (11, 15, 1), (12, 16, 1)\n",
|
| 170 |
+
"]\n",
|
| 171 |
+
"g = Graph()\n",
|
| 172 |
+
"for s, e, d in edges:\n",
|
| 173 |
+
" g.add_edge(s, e, d, bidirectional=True)\n",
|
| 174 |
+
" \n",
|
| 175 |
+
"g.distance_map(starts=[3, 5], ends=[12, 14])\n"
|
| 176 |
+
]
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"cell_type": "markdown",
|
| 180 |
+
"metadata": {},
|
| 181 |
+
"source": [
|
| 182 |
+
"The distance map returns a dictionary with the distance from 3 and 5 traveling towards 12 and 14. \n",
|
| 183 |
+
"\n",
|
| 184 |
+
"Think of this map as a landscape, where the `starts` are at the bottom and then you want to \"walk\" towards either 12 or 14 following the path of least resistance."
|
| 185 |
+
]
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"cell_type": "markdown",
|
| 189 |
+
"metadata": {},
|
| 190 |
+
"source": [
|
| 191 |
+
"In my experience a lot of people are initially confused about the all-pairs shortest path, and it's usage.\n",
|
| 192 |
+
"\n",
|
| 193 |
+
"Let's compare it with two other methods:\n",
|
| 194 |
+
"\n",
|
| 195 |
+
"minsum: finds the mode(s) that have the smallest sum of distance to all other nodes\n",
|
| 196 |
+
"\n",
|
| 197 |
+
"minmax: finds the node(s) with shortest distance to all other nodes. \n"
|
| 198 |
+
]
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"cell_type": "markdown",
|
| 202 |
+
"metadata": {},
|
| 203 |
+
"source": [
|
| 204 |
+
"## Breadth First Search (BFS)\n",
|
| 205 |
+
"\n",
|
| 206 |
+
"Breadth-first search (BFS) is an algorithm for searching a tree data structure for a node that satisfies a given property. It starts at the tree root and explores all nodes at the present depth prior to moving on to the nodes at the next depth level. Extra memory, usually a queue, is needed to keep track of the child nodes that were encountered but not yet explored.\n",
|
| 207 |
+
"\n",
|
| 208 |
+
"A simple example is where you are searching for the nearest gas station on a map: You start with your current position and follow the roads in all directions until you meet an intersection. You repeat this \"extension\" until you find a gas station.\n",
|
| 209 |
+
"\n",
|
| 210 |
+
"BFS on a map is very easy to understand. But what if you don't have a map? What if you only know where you can go?\n",
|
| 211 |
+
"\n",
|
| 212 |
+
"For example, in a chess endgame a chess engine may build the game tree from the current position by applying all possible moves, and use breadth-first search to find a win position for white. Implicit trees (such as game trees or other problem-solving trees) may be of infinite size; breadth-first search is guaranteed to find a solution node if one exists.\n",
|
| 213 |
+
"\n",
|
| 214 |
+
"A slightly simpler case is the search for a solution of the tile-slide puzzle.\n",
|
| 215 |
+
"\n",
|
| 216 |
+
"We can describe the state of a system by it's options as a graph:\n",
|
| 217 |
+
"\n",
|
| 218 |
+
"```\n",
|
| 219 |
+
" A B\n",
|
| 220 |
+
"[1] --- [2]\n",
|
| 221 |
+
" | |\n",
|
| 222 |
+
" | |\n",
|
| 223 |
+
"[4] --- [3]\n",
|
| 224 |
+
"```\n",
|
| 225 |
+
"With tiles on position `[1]` and `[2]` where A is in 1 and B is in 2.\n",
|
| 226 |
+
"Let's find a way for A to move to 3 and B to move to 4.\n",
|
| 227 |
+
"\n",
|
| 228 |
+
"First we create the graph:"
|
| 229 |
+
]
|
| 230 |
+
},
|
| 231 |
+
{
|
| 232 |
+
"cell_type": "code",
|
| 233 |
+
"execution_count": 3,
|
| 234 |
+
"metadata": {},
|
| 235 |
+
"outputs": [],
|
| 236 |
+
"source": [
|
| 237 |
+
"g = Graph()\n",
|
| 238 |
+
"for s,e in zip([1,2,3,4], [2,3,4,1]):\n",
|
| 239 |
+
" g.add_edge(s,e,1,bidirectional=True)"
|
| 240 |
+
]
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"cell_type": "markdown",
|
| 244 |
+
"metadata": {},
|
| 245 |
+
"source": [
|
| 246 |
+
"Then we define the `initial state` and `end state`:"
|
| 247 |
+
]
|
| 248 |
+
},
|
| 249 |
+
{
|
| 250 |
+
"cell_type": "code",
|
| 251 |
+
"execution_count": 4,
|
| 252 |
+
"metadata": {},
|
| 253 |
+
"outputs": [],
|
| 254 |
+
"source": [
|
| 255 |
+
"s1 = (('A',1), ('B',2))"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"cell_type": "code",
|
| 260 |
+
"execution_count": 5,
|
| 261 |
+
"metadata": {},
|
| 262 |
+
"outputs": [],
|
| 263 |
+
"source": [
|
| 264 |
+
"end = (('A',3),('B',4))"
|
| 265 |
+
]
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"attachments": {},
|
| 269 |
+
"cell_type": "markdown",
|
| 270 |
+
"metadata": {},
|
| 271 |
+
"source": [
|
| 272 |
+
"Now `A` can't move from 1 via 2 to 3, as `B` blocks the way.\n",
|
| 273 |
+
"Similarly `B` can't move from 2 via 1 to 3, as `A` blocks the way.\n",
|
| 274 |
+
"So we need generate a tree using BFS as foundation for our search.\n",
|
| 275 |
+
"\n",
|
| 276 |
+
"the search will look like this:\n",
|
| 277 |
+
"\n",
|
| 278 |
+
"\n",
|
| 279 |
+
"\n",
|
| 280 |
+
"but as you see there is some redunancy in the search tree, it is better to create a graph of movements:\n",
|
| 281 |
+
"\n",
|
| 282 |
+
"\n",
|
| 283 |
+
"\n",
|
| 284 |
+
"We can now search for the shortest path from the `initial state` to the `end state` in the movements graph:\n",
|
| 285 |
+
"\n",
|
| 286 |
+
"This is the code from graph-theory"
|
| 287 |
+
]
|
| 288 |
+
},
|
| 289 |
+
{
|
| 290 |
+
"cell_type": "code",
|
| 291 |
+
"execution_count": 6,
|
| 292 |
+
"metadata": {},
|
| 293 |
+
"outputs": [],
|
| 294 |
+
"source": [
|
| 295 |
+
"from graph.bfs import breadth_first_search\n",
|
| 296 |
+
"import inspect"
|
| 297 |
+
]
|
| 298 |
+
},
|
| 299 |
+
{
|
| 300 |
+
"cell_type": "code",
|
| 301 |
+
"execution_count": 7,
|
| 302 |
+
"metadata": {},
|
| 303 |
+
"outputs": [
|
| 304 |
+
{
|
| 305 |
+
"name": "stdout",
|
| 306 |
+
"output_type": "stream",
|
| 307 |
+
"text": [
|
| 308 |
+
"def breadth_first_search(graph, start, end):\n",
|
| 309 |
+
" \"\"\"Determines the path from start to end with fewest nodes.\n",
|
| 310 |
+
" :param graph: class Graph\n",
|
| 311 |
+
" :param start: start node\n",
|
| 312 |
+
" :param end: end node\n",
|
| 313 |
+
" :return: path\n",
|
| 314 |
+
" \"\"\"\n",
|
| 315 |
+
" if not isinstance(graph, BasicGraph):\n",
|
| 316 |
+
" raise TypeError(f\"Expected BasicGraph, Graph or Graph3D, not {type(graph)}\")\n",
|
| 317 |
+
" if start not in graph:\n",
|
| 318 |
+
" raise ValueError(f\"{start} not in graph\")\n",
|
| 319 |
+
" if end not in graph:\n",
|
| 320 |
+
" raise ValueError(f\"{end} not in graph\")\n",
|
| 321 |
+
"\n",
|
| 322 |
+
" visited = {start: None}\n",
|
| 323 |
+
" q = deque([start])\n",
|
| 324 |
+
" while q:\n",
|
| 325 |
+
" node = q.popleft()\n",
|
| 326 |
+
" if node == end:\n",
|
| 327 |
+
" path = deque()\n",
|
| 328 |
+
" while node is not None:\n",
|
| 329 |
+
" path.appendleft(node)\n",
|
| 330 |
+
" node = visited[node]\n",
|
| 331 |
+
" return list(path)\n",
|
| 332 |
+
" for next_node in graph.nodes(from_node=node):\n",
|
| 333 |
+
" if next_node not in visited:\n",
|
| 334 |
+
" visited[next_node] = node\n",
|
| 335 |
+
" q.append(next_node)\n",
|
| 336 |
+
" return []\n",
|
| 337 |
+
"\n"
|
| 338 |
+
]
|
| 339 |
+
}
|
| 340 |
+
],
|
| 341 |
+
"source": [
|
| 342 |
+
"print(inspect.getsource(breadth_first_search))"
|
| 343 |
+
]
|
| 344 |
+
},
|
| 345 |
+
{
|
| 346 |
+
"cell_type": "markdown",
|
| 347 |
+
"metadata": {},
|
| 348 |
+
"source": [
|
| 349 |
+
"**Explanation of the code**\n",
|
| 350 |
+
"\n",
|
| 351 |
+
"`breadth_first_search` starts with the `start` (our `initial state`) and `end` (our `end state`) and puts `start` into a `deque`.\n",
|
| 352 |
+
"\n",
|
| 353 |
+
"```\n",
|
| 354 |
+
"def breadth_first_search(graph, start, end):\n",
|
| 355 |
+
" visited = {start: None}\n",
|
| 356 |
+
" q = deque([start])\n",
|
| 357 |
+
"```\n",
|
| 358 |
+
"Then it enters the `while` loop and runs as long as there's node in the queue.\n",
|
| 359 |
+
"```\n",
|
| 360 |
+
" while q:\n",
|
| 361 |
+
" node = q.popleft()\n",
|
| 362 |
+
" if node == end:\n",
|
| 363 |
+
"```\n",
|
| 364 |
+
"The algorithm then removes the first item from the queue using `popleft`, and checks whether it's the `end`. If it isn't, the search continues:\n",
|
| 365 |
+
"\n",
|
| 366 |
+
"We take each node that the popped `node` is connected to and check if we have seen it before. If we have seen it before we just continue to the next node. If we haven't seen it before we add it to the queue.\n",
|
| 367 |
+
"```\n",
|
| 368 |
+
" for next_node in graph.nodes(from_node=node):\n",
|
| 369 |
+
" if next_node not in visited:\n",
|
| 370 |
+
" visited[next_node] = node\n",
|
| 371 |
+
" q.append(next_node)\n",
|
| 372 |
+
"```\n",
|
| 373 |
+
"Finally when the `end` is the node we are looking for we can terminate the search."
|
| 374 |
+
]
|
| 375 |
+
},
|
| 376 |
+
{
|
| 377 |
+
"cell_type": "markdown",
|
| 378 |
+
"metadata": {},
|
| 379 |
+
"source": [
|
| 380 |
+
"### finding loops"
|
| 381 |
+
]
|
| 382 |
+
},
|
| 383 |
+
{
|
| 384 |
+
"cell_type": "markdown",
|
| 385 |
+
"metadata": {},
|
| 386 |
+
"source": [
|
| 387 |
+
"## Depth First Search (DFS)\n",
|
| 388 |
+
"\n"
|
| 389 |
+
]
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"cell_type": "markdown",
|
| 393 |
+
"metadata": {},
|
| 394 |
+
"source": [
|
| 395 |
+
"## Bidirectional breadth first search (BidiBFS)"
|
| 396 |
+
]
|
| 397 |
+
},
|
| 398 |
+
{
|
| 399 |
+
"cell_type": "markdown",
|
| 400 |
+
"metadata": {
|
| 401 |
+
"jupyter": {
|
| 402 |
+
"outputs_hidden": true
|
| 403 |
+
}
|
| 404 |
+
},
|
| 405 |
+
"source": [
|
| 406 |
+
"## Critical path method\n",
|
| 407 |
+
"\n",
|
| 408 |
+
"The [critical path method](https://en.wikipedia.org/wiki/Critical_path_method) (CPM), or critical path analysis (CPA), is an algorithm for scheduling a set of project activities.\n",
|
| 409 |
+
"\n",
|
| 410 |
+
"A critical path is determined by identifying the longest stretch of dependent activities and, commonly, measuring the time required to complete them from start to finish.\n",
|
| 411 |
+
"\n",
|
| 412 |
+
"An example is shown below where the critical path constitutes the path ABCDE:\n",
|
| 413 |
+
"\n",
|
| 414 |
+
"\n",
|
| 415 |
+
"\n",
|
| 416 |
+
"We can load these values into a Graph as follows:"
|
| 417 |
+
]
|
| 418 |
+
},
|
| 419 |
+
{
|
| 420 |
+
"cell_type": "code",
|
| 421 |
+
"execution_count": 8,
|
| 422 |
+
"metadata": {},
|
| 423 |
+
"outputs": [],
|
| 424 |
+
"source": [
|
| 425 |
+
"tasks = {'A': 10, 'B': 20, 'C': 5, 'D': 10, 'E': 20, 'F': 15, 'G': 5, 'H': 15}\n",
|
| 426 |
+
"dependencies = [\n",
|
| 427 |
+
" ('A', 'B'),\n",
|
| 428 |
+
" ('B', 'C'),\n",
|
| 429 |
+
" ('C', 'D'),\n",
|
| 430 |
+
" ('D', 'E'),\n",
|
| 431 |
+
" ('A', 'F'),\n",
|
| 432 |
+
" ('F', 'G'),\n",
|
| 433 |
+
" ('G', 'E'),\n",
|
| 434 |
+
" ('A', 'H'),\n",
|
| 435 |
+
" ('H', 'E'),\n",
|
| 436 |
+
"]\n",
|
| 437 |
+
"\n",
|
| 438 |
+
"g = Graph()\n",
|
| 439 |
+
"for n, d in tasks.items():\n",
|
| 440 |
+
" g.add_node(n, obj=d)\n",
|
| 441 |
+
"for n1, n2 in dependencies:\n",
|
| 442 |
+
" g.add_edge(n1, n2, 0)"
|
| 443 |
+
]
|
| 444 |
+
},
|
| 445 |
+
{
|
| 446 |
+
"cell_type": "markdown",
|
| 447 |
+
"metadata": {},
|
| 448 |
+
"source": [
|
| 449 |
+
"And we can calculate the schedule and the length of the critical path as:"
|
| 450 |
+
]
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
"cell_type": "code",
|
| 454 |
+
"execution_count": 9,
|
| 455 |
+
"metadata": {},
|
| 456 |
+
"outputs": [],
|
| 457 |
+
"source": [
|
| 458 |
+
"critical_path_length, schedule = g.critical_path()"
|
| 459 |
+
]
|
| 460 |
+
},
|
| 461 |
+
{
|
| 462 |
+
"cell_type": "code",
|
| 463 |
+
"execution_count": 10,
|
| 464 |
+
"metadata": {},
|
| 465 |
+
"outputs": [
|
| 466 |
+
{
|
| 467 |
+
"name": "stdout",
|
| 468 |
+
"output_type": "stream",
|
| 469 |
+
"text": [
|
| 470 |
+
"The critical path has duration 65\n"
|
| 471 |
+
]
|
| 472 |
+
}
|
| 473 |
+
],
|
| 474 |
+
"source": [
|
| 475 |
+
"print(\"The critical path has duration\", critical_path_length)"
|
| 476 |
+
]
|
| 477 |
+
},
|
| 478 |
+
{
|
| 479 |
+
"cell_type": "code",
|
| 480 |
+
"execution_count": 11,
|
| 481 |
+
"metadata": {},
|
| 482 |
+
"outputs": [
|
| 483 |
+
{
|
| 484 |
+
"name": "stdout",
|
| 485 |
+
"output_type": "stream",
|
| 486 |
+
"text": [
|
| 487 |
+
"The tasks are:\n",
|
| 488 |
+
"A Task('A', 10, 0, 0, 10, 10)\n",
|
| 489 |
+
"B Task('B', 20, 10, 10, 30, 30)\n",
|
| 490 |
+
"C Task('C', 5, 30, 30, 35, 35)\n",
|
| 491 |
+
"D Task('D', 10, 35, 35, 45, 45)\n",
|
| 492 |
+
"E Task('E', 20, 45, 45, 65, 65)\n",
|
| 493 |
+
"F Task('F', 15, 10, 25, 25, 40)\n",
|
| 494 |
+
"G Task('G', 5, 25, 40, 30, 45)\n",
|
| 495 |
+
"H Task('H', 15, 10, 30, 25, 45)\n"
|
| 496 |
+
]
|
| 497 |
+
}
|
| 498 |
+
],
|
| 499 |
+
"source": [
|
| 500 |
+
"print(\"The tasks are:\")\n",
|
| 501 |
+
"from graph.critical_path import Task\n",
|
| 502 |
+
"for task_id, task in sorted(schedule.items()):\n",
|
| 503 |
+
" print(task_id, task)"
|
| 504 |
+
]
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"cell_type": "markdown",
|
| 508 |
+
"metadata": {},
|
| 509 |
+
"source": [
|
| 510 |
+
"The properties of each `Task` are:\n",
|
| 511 |
+
"\n",
|
| 512 |
+
"- task id\n",
|
| 513 |
+
"- duration \n",
|
| 514 |
+
"- earliest start time\n",
|
| 515 |
+
"- latest start time\n",
|
| 516 |
+
"- earliest finish time\n",
|
| 517 |
+
"- latest finish time.\n",
|
| 518 |
+
"\n",
|
| 519 |
+
"and the slack in the schedule can be calculated as:"
|
| 520 |
+
]
|
| 521 |
+
},
|
| 522 |
+
{
|
| 523 |
+
"cell_type": "code",
|
| 524 |
+
"execution_count": 12,
|
| 525 |
+
"metadata": {},
|
| 526 |
+
"outputs": [
|
| 527 |
+
{
|
| 528 |
+
"name": "stdout",
|
| 529 |
+
"output_type": "stream",
|
| 530 |
+
"text": [
|
| 531 |
+
"The total slack in the schedule is 50\n"
|
| 532 |
+
]
|
| 533 |
+
}
|
| 534 |
+
],
|
| 535 |
+
"source": [
|
| 536 |
+
"slack = sum(t.slack for t in schedule.values())\n",
|
| 537 |
+
"\n",
|
| 538 |
+
"print(\"The total slack in the schedule is\", slack)"
|
| 539 |
+
]
|
| 540 |
+
},
|
| 541 |
+
{
|
| 542 |
+
"cell_type": "markdown",
|
| 543 |
+
"metadata": {},
|
| 544 |
+
"source": [
|
| 545 |
+
"### Minimising slack\n",
|
| 546 |
+
"\n",
|
| 547 |
+
"In cases where the tasks are commodities, such as CPU time, it can be convenient to minimise the number of concurrently active resources.\n",
|
| 548 |
+
"\n",
|
| 549 |
+
"As you may have noticed above in the diagram, the dependencies indicate that the graph has 3 paths at it's widest, whereby it would be logical to assign 3 CPUs to compute the tasks. However a little search can illustrate that it is possible to solve the tasks with 2 CPUs without extending the critical path.\n",
|
| 550 |
+
"\n",
|
| 551 |
+
"This can be done by inserting artificial dependencies. Here is an example:\n",
|
| 552 |
+
"\n",
|
| 553 |
+
"\n",
|
| 554 |
+
"\n",
|
| 555 |
+
"The method to minimise the slack, is conveniently called `critical_path_minimize_for_slack` and this is how it is used:"
|
| 556 |
+
]
|
| 557 |
+
},
|
| 558 |
+
{
|
| 559 |
+
"cell_type": "code",
|
| 560 |
+
"execution_count": 13,
|
| 561 |
+
"metadata": {
|
| 562 |
+
"jupyter": {
|
| 563 |
+
"outputs_hidden": false
|
| 564 |
+
},
|
| 565 |
+
"pycharm": {
|
| 566 |
+
"name": "#%%\n"
|
| 567 |
+
}
|
| 568 |
+
},
|
| 569 |
+
"outputs": [],
|
| 570 |
+
"source": [
|
| 571 |
+
"g2 = g.critical_path_minimize_for_slack()"
|
| 572 |
+
]
|
| 573 |
+
},
|
| 574 |
+
{
|
| 575 |
+
"cell_type": "markdown",
|
| 576 |
+
"metadata": {},
|
| 577 |
+
"source": [
|
| 578 |
+
"We can verify that the critical path length is the same, and we can verify that this schedule does indeed have less slack:"
|
| 579 |
+
]
|
| 580 |
+
},
|
| 581 |
+
{
|
| 582 |
+
"cell_type": "code",
|
| 583 |
+
"execution_count": 14,
|
| 584 |
+
"metadata": {},
|
| 585 |
+
"outputs": [
|
| 586 |
+
{
|
| 587 |
+
"name": "stdout",
|
| 588 |
+
"output_type": "stream",
|
| 589 |
+
"text": [
|
| 590 |
+
"The total slack in the schedule was 50 and is now 0\n"
|
| 591 |
+
]
|
| 592 |
+
}
|
| 593 |
+
],
|
| 594 |
+
"source": [
|
| 595 |
+
"critical_path_length2, schedule2 = g2.critical_path()\n",
|
| 596 |
+
"\n",
|
| 597 |
+
"slack2 = sum(t.slack for t in schedule2.values())\n",
|
| 598 |
+
"\n",
|
| 599 |
+
"print(\"The total slack in the schedule was\", slack, \"and is now\", slack2)"
|
| 600 |
+
]
|
| 601 |
+
},
|
| 602 |
+
{
|
| 603 |
+
"cell_type": "code",
|
| 604 |
+
"execution_count": 15,
|
| 605 |
+
"metadata": {},
|
| 606 |
+
"outputs": [
|
| 607 |
+
{
|
| 608 |
+
"name": "stdout",
|
| 609 |
+
"output_type": "stream",
|
| 610 |
+
"text": [
|
| 611 |
+
"The tasks remain the same, though with changed timings::\n",
|
| 612 |
+
"A Task('A', 10, 0, 0, 10, 10)\n",
|
| 613 |
+
"B Task('B', 20, 10, 10, 30, 30)\n",
|
| 614 |
+
"C Task('C', 5, 30, 30, 35, 35)\n",
|
| 615 |
+
"D Task('D', 10, 35, 35, 45, 45)\n",
|
| 616 |
+
"E Task('E', 20, 45, 45, 65, 65)\n",
|
| 617 |
+
"F Task('F', 15, 25, 25, 40, 40)\n",
|
| 618 |
+
"G Task('G', 5, 40, 40, 45, 45)\n",
|
| 619 |
+
"H Task('H', 15, 10, 10, 25, 25)\n"
|
| 620 |
+
]
|
| 621 |
+
}
|
| 622 |
+
],
|
| 623 |
+
"source": [
|
| 624 |
+
"print(\"The tasks remain the same, though with changed timings::\")\n",
|
| 625 |
+
"from graph.critical_path import Task\n",
|
| 626 |
+
"for task_id, task in sorted(schedule2.items()):\n",
|
| 627 |
+
" print(task_id, task)"
|
| 628 |
+
]
|
| 629 |
+
}
|
| 630 |
+
],
|
| 631 |
+
"metadata": {
|
| 632 |
+
"kernelspec": {
|
| 633 |
+
"display_name": "Python 3 (ipykernel)",
|
| 634 |
+
"language": "python",
|
| 635 |
+
"name": "python3"
|
| 636 |
+
},
|
| 637 |
+
"language_info": {
|
| 638 |
+
"codemirror_mode": {
|
| 639 |
+
"name": "ipython",
|
| 640 |
+
"version": 3
|
| 641 |
+
},
|
| 642 |
+
"file_extension": ".py",
|
| 643 |
+
"mimetype": "text/x-python",
|
| 644 |
+
"name": "python",
|
| 645 |
+
"nbconvert_exporter": "python",
|
| 646 |
+
"pygments_lexer": "ipython3",
|
| 647 |
+
"version": "3.10.11"
|
| 648 |
+
}
|
| 649 |
+
},
|
| 650 |
+
"nbformat": 4,
|
| 651 |
+
"nbformat_minor": 4
|
| 652 |
+
}
|
graph-theory/source/examples/solving transport problems.ipynb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {
|
| 7 |
+
"collapsed": true
|
| 8 |
+
},
|
| 9 |
+
"outputs": [],
|
| 10 |
+
"source": [
|
| 11 |
+
""
|
| 12 |
+
]
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"metadata": {
|
| 16 |
+
"kernelspec": {
|
| 17 |
+
"display_name": "Python 3",
|
| 18 |
+
"language": "python",
|
| 19 |
+
"name": "python3"
|
| 20 |
+
},
|
| 21 |
+
"language_info": {
|
| 22 |
+
"codemirror_mode": {
|
| 23 |
+
"name": "ipython",
|
| 24 |
+
"version": 2
|
| 25 |
+
},
|
| 26 |
+
"file_extension": ".py",
|
| 27 |
+
"mimetype": "text/x-python",
|
| 28 |
+
"name": "python",
|
| 29 |
+
"nbconvert_exporter": "python",
|
| 30 |
+
"pygments_lexer": "ipython2",
|
| 31 |
+
"version": "2.7.6"
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"nbformat": 4,
|
| 35 |
+
"nbformat_minor": 0
|
| 36 |
+
}
|