Skip to content

Commit 21cc3d6

Browse files
authored
refactor: visualization tool (#309)
Refactored visualization tool, instead of job ID, added config yaml file name. Also, added a readme file to describe how this tool can be used. Removed dependencies from pyproject.toml since the readme specifies the requirements of using this tool and libraries can be installed manually by following instructions.
1 parent 0112e8a commit 21cc3d6

File tree

3 files changed

+91
-13
lines changed

3 files changed

+91
-13
lines changed

pyproject.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ dev = [
3737
"bumpver",
3838
"grpcio-tools",
3939
"isort",
40-
"matplotlib>=3.7",
41-
"networkx>=3.2",
4240
"pip-tools",
4341
"pytest",
4442
"python-lsp-server",

tools/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Job Visualizer
2+
3+
This utility visualizes the **flow graph** of a job configuration defined in a YAML file.
4+
It builds a **directed graph** using [`networkx`](https://networkx.org/) and renders it visually using [`matplotlib`](https://matplotlib.org/).
5+
6+
The tool expects a configuration file that can be parsed into a `JobConfig` object.
7+
Each worker and its connections (peers) are represented as nodes and directed edges in the graph.
8+
9+
---
10+
11+
## Features
12+
13+
- Visualizes job flow graphs defined in YAML files.
14+
- Shows workers as nodes and communication paths as directed edges.
15+
- Adds labels on edges showing world names, addresses, and backends.
16+
- Arranges workers horizontally by their `stage["start"]` values.
17+
- Opens the graph interactively with the possibility of saving it as PNG image.
18+
19+
---
20+
21+
## Requirements
22+
23+
Install the required dependencies:
24+
25+
```bash
26+
pip install matplotlib networkx pyyaml
27+
```
28+
29+
---
30+
31+
## Usage
32+
33+
```bash
34+
python visualize_job.py path/to/job_config.yaml [-s]
35+
```
36+
37+
| Argument | Description |
38+
| -------------- | ----------------------------------------- |
39+
| `config_path` | Path to the job YAML configuration file. |
40+
| `-s`, `--save` | Optional boolean arg to save the PNG file |
41+
42+
---
43+
44+
## Example
45+
46+
#### Display job in a new window
47+
```bash
48+
python tools/visualize_job.py examples/llama3/static/mesh.yaml
49+
```
50+
51+
#### Display job in a new window and save the PNG file
52+
```bash
53+
python tools/visualize_job.py examples/llama3/static/mesh.yaml -s
54+
```

tools/visualise_flow_graph.py renamed to tools/visualize_job.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
# Copyright 2025 Cisco Systems, Inc. and its affiliates
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
17+
"""visualize_tool.py."""
18+
19+
120
import argparse
221
import os
322

@@ -8,11 +27,13 @@
827
from infscale.configs.job import JobConfig
928

1029

11-
def load_job_config(path: str) -> JobConfig:
30+
def get_job_data(path: str) -> tuple[JobConfig, str]:
31+
"""Load job data composed by JobConfig and yaml file name."""
1232
with open(path) as f:
1333
data = yaml.safe_load(f)
34+
file_name = os.path.splitext(os.path.basename(path))[0]
1435

15-
return JobConfig(**data)
36+
return JobConfig(**data), file_name
1637

1738

1839
def build_graph(job: JobConfig) -> tuple[nx.DiGraph, dict[str, int]]:
@@ -42,7 +63,7 @@ def build_graph(job: JobConfig) -> tuple[nx.DiGraph, dict[str, int]]:
4263
def draw_graph(
4364
graph: nx.DiGraph,
4465
worker_stage: dict[str, int],
45-
job_id: str,
66+
file_name: str,
4667
output_path: str = "",
4768
) -> None:
4869
"""Draw graph where worker_stage maps node -> stage (start)."""
@@ -151,32 +172,37 @@ def draw_graph(
151172
ax.set_axis_off()
152173
plt.tight_layout()
153174
if output_path:
154-
os.makedirs(output_path, exist_ok=True)
155-
output_file = os.path.join(output_path, f"{job_id}.png")
175+
# save PNG in the same folder as this script
176+
script_dir = os.path.dirname(os.path.abspath(__file__))
177+
output_file = os.path.join(script_dir, f"{file_name}.png")
156178
plt.savefig(output_file, dpi=300, bbox_inches="tight")
157179
print(f"Graph saved at: {output_file}")
158-
else:
159-
print("Graph opened in a new window.")
160-
plt.show()
180+
181+
print("Graph opened in a new window.")
182+
plt.show()
161183

162184

163185
def main():
164186
parser = argparse.ArgumentParser(description="Visualize JobConfig flow graph")
165187
parser.add_argument("config_path", help="Path to job YAML config")
166188
parser.add_argument(
167-
"-o", "--output", help="Directory to save output image (optional)", default=None
189+
"-s",
190+
"--save",
191+
action="store_true",
192+
help="Save the graph as a PNG file in the same directory as the script instead of displaying it",
168193
)
169194
args = parser.parse_args()
170195

171196
try:
172-
config = load_job_config(args.config_path)
197+
config, file_name = get_job_data(args.config_path)
173198
except FileNotFoundError as e:
174199
print(f"Error while loading file: {e}")
175200
return
176201

177202
graph, worker_stage = build_graph(config)
203+
output_path = os.path.dirname(os.path.abspath(__file__)) if args.save else None
178204
try:
179-
draw_graph(graph, worker_stage, config.job_id, args.output)
205+
draw_graph(graph, worker_stage, file_name, output_path)
180206
except nx.exception.NetworkXError as e:
181207
print(f"Error while drawing graph: {e}")
182208
except KeyboardInterrupt:

0 commit comments

Comments
 (0)