-
Notifications
You must be signed in to change notification settings - Fork 2
Add dependency graph entry and test files. #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sahelib25
wants to merge
18
commits into
krai:master
Choose a base branch
from
sahelib25:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
efb2553
Add entry for graph under varia_collection.
sahelib25 4d8cbcd
Upadate data_axs.json and code_axs.json
sahelib25 8093f91
Add dfs func for target node.
elimkwan f76c610
Update graph with parents of parents and multiple parents of output n…
sahelib25 65e207f
Fix the issue of accessing data_axs.json files based on given target …
sahelib25 b020883
Add visual features to the graph: colouring, grouping and clustering.
sahelib25 6101f26
Merge remote-tracking branch 'upstream/master'
sahelib25 bac5ed4
Add an extra functionality for extracting parents from output_entry_p…
sahelib25 549f744
Add more visual features to dependency graph.
sahelib25 50cce22
Enhance user readability of code_axs.py for graph entry.
sahelib25 032b5c7
Merge remote-tracking branch 'upstream/master'
sahelib25 d7bdfd9
Add README.md for graph
sahelib25 9a0b2ee
Enhance user readability for code_axs.py and README.md.
sahelib25 2ff306a
Merge branch 'krai:master' into master
sahelib25 569f570
Add features to print the hierarchical graph in a tree structure.
sahelib25 5cd9332
Remove redundant printing of entry_name with path
sahelib25 a690619
Merge branch 'krai:master' into master
sahelib25 406ffe8
Update Readme and fix typo.
sahelib25 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
## Generating Hierarchy Graph | ||
`AXS` command: | ||
``` | ||
axs byname graph , draw <entry_name> | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
Eg. | ||
``` | ||
axs byname graph , draw image_classification_using_tf_py | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
 | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The figure shows a dependency graph generated for `image_classification_using_tf_py` entry. | ||
|
||
- Red : target entry | ||
- Coral: Parents of the target entry | ||
- Blue: output node | ||
- Lightblue: Parents of the output node | ||
|
||
If the run is successful, it should print `Graph is generated!` | ||
|
||
``` | ||
saved to '/home/saheli/work_collection/generated_by_graph_on_draw_a579763d98044530962cc967ac659b28/data_axs.json' | ||
byname_entries: ['base_imagenet_experiment'] | ||
Graph is generated! | ||
['^', 'byname', 'generated_by_graph_on_draw_generated_by_graph_on_draw_a579763d98044530962cc967ac659b28'] | ||
``` | ||
The image of the graph rendered in vector graphics, `image.svg` and `.dot` file are generated under `~/work_collection/generated_by_graph_on_draw_a579763d98044530962cc967ac659b28/` folder. | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The structure of the `.dot` file will look something like this: `image`. It can be interpreted as a normal text file. | ||
``` | ||
digraph { | ||
node [shape=ellipse] | ||
dpi=400 | ||
subgraph cluster_0 { | ||
style=dotted | ||
label="Entry and Its Parent(s)" | ||
image_classification_using_tf_py [color=red style=filled] | ||
python_script -> image_classification_using_tf_py | ||
base_benchmark_program -> image_classification_using_tf_py | ||
base_benchmark_program [color=lightcoral style=filled] | ||
python_script [color=lightcoral style=filled] | ||
python_in_shell -> python_script | ||
python_in_shell [color=lightcoral style=filled] | ||
shell -> python_in_shell | ||
shell [color=lightcoral style=filled] | ||
} | ||
output [color=blue style=filled] | ||
image_classification_using_tf_py -> output | ||
base_imagenet_experiment -> output | ||
subgraph cluster_1 { | ||
style=dotted | ||
label="Parent(s) of the Output Entry" | ||
base_imagenet_experiment [color=lightblue style=filled] | ||
base_experiment -> base_imagenet_experiment | ||
base_experiment [color=lightblue style=filled] | ||
} | ||
} | ||
``` | ||
## Running tests | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The next step is to run the tests. | ||
|
||
### Step 1: | ||
The first step is to run the `create_json.py` to generate two json files containing dictionaries containing all the contained entries obtained as keys from `bert`, `image_classification`, `object_detection` folders and corresponding values as the `_parent_entries` and `output_parent_entries` for each entry. | ||
``` | ||
python create_json.py | ||
``` | ||
<Details><Pre> | ||
output_parent_entries_dict.json parent_entries_dict.json </Details></Pre> | ||
|
||
### Step 2: | ||
Next, to run the `test_parent_and_output_entries.py` file | ||
``` | ||
pytest test_parent_and_output_entries.py | ||
``` | ||
<Details><Pre> | ||
==================================================================================== 2 passed in 0.01s ===================================================================================== | ||
collected 2 items | ||
|
||
test_parent_and_output_entries.py::test_compare_dot_and_json_for_target PASSED | ||
test_parent_and_output_entries.py::test_compare_dot_and_json_for_target_output PASSED | ||
</Details></Pre> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import re | ||
import graphviz | ||
import json | ||
from kernel import default_kernel as ak | ||
|
||
initial_root_visited = False | ||
|
||
def dfs(root, f, __entry__, is_output=False): | ||
|
||
""" Depth First Search(DFS) for a given node. | ||
""" | ||
|
||
global initial_root_visited | ||
stack = [] | ||
visited = set() | ||
|
||
cur_target_entry = __entry__.get_kernel().byname(root) | ||
if not cur_target_entry: | ||
print("ERROR!") | ||
return | ||
|
||
stack.append((cur_target_entry, True)) # Using True to signify that this is the initial root node | ||
|
||
while stack: | ||
cur_target_entry, is_initial_root = stack.pop() | ||
cur_target = cur_target_entry.get_name() | ||
|
||
if cur_target in visited: | ||
continue | ||
|
||
if not initial_root_visited: | ||
color = 'red' | ||
initial_root_visited = True | ||
elif is_output: | ||
color = 'lightblue' | ||
else: | ||
color = 'lightcoral' | ||
|
||
f.node(cur_target, color=color, style='filled') | ||
visited.add(cur_target) | ||
|
||
parents = cur_target_entry.get("_parent_entries") | ||
if parents: | ||
for parent in parents: | ||
if isinstance(parent, str): | ||
p = __entry__.get_kernel().byname(parent) | ||
else: | ||
p = parent | ||
if not p: | ||
continue | ||
stack.append((p, False)) # Using False to signify that this is not the initial root node | ||
f.edge(p.get_name(), cur_target) | ||
|
||
return f | ||
|
||
def draw(target, return_this_entry=None, __entry__=None): | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
""" Generate Dependency Graph for a given entry. | ||
|
||
Usage examples: | ||
axs byname graph , draw bert_using_onnxrt_py | ||
axs byname graph , draw image_classification_using_tf_py | ||
elimkwan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
|
||
global initial_root_visited | ||
initial_root_visited = False | ||
output = False | ||
output_parents_data = "" | ||
dest_dir = return_this_entry.get_path() | ||
|
||
target_entry = __entry__.get_kernel().byname(target) | ||
if target_entry: | ||
get_path = target_entry.get_path() | ||
file_path = f'{get_path}/data_axs.json' | ||
target_data = target_entry.own_data() | ||
output_entries = target_entry.get("output_entry_parents") | ||
|
||
if output_entries: | ||
# Extract all 'byname' entries from "output_entry_parents" as objects to byname as key | ||
byname_entries = extract_byname_entries(output_entries) | ||
print("byname_entries:", byname_entries) | ||
|
||
for key, val in target_data.items(): | ||
if "_parent_entries" in str(val): | ||
output = True | ||
output_parents_data = val | ||
elif "tags" in str(val): | ||
output = True | ||
elif output_entries: | ||
output = True | ||
|
||
f = graphviz.Digraph(format='svg') | ||
f.attr('node', shape='ellipse') | ||
f.attr(dpi='400') | ||
f.engine = 'dot' | ||
|
||
|
||
with f.subgraph(name='cluster_0') as c: | ||
c.attr(style='dotted') | ||
c.attr(label='Entry and Its Parent(s)') | ||
dfs(target, c, __entry__, is_output=False) | ||
|
||
|
||
if output: | ||
f.node("output", style='filled', color='blue') | ||
f.edge(target, "output") | ||
|
||
if output_parents_data: | ||
info = find_parent(output_parents_data) | ||
output_parents = find_byname(file_path,obj=info) | ||
print("output_parents", output_parents) | ||
for output_parent in output_parents: | ||
with f.subgraph(name='cluster_1') as c: | ||
c.attr(style='dotted') | ||
c.attr(label='Parent(s) of the Output Entry') | ||
dfs(output_parent, c, __entry__, is_output=True) | ||
f.edge(output_parent, "output") | ||
target_entry = output_parent | ||
else: | ||
target_entry = None | ||
|
||
elif output_entries and byname_entries: | ||
for byname_entry in byname_entries: | ||
with f.subgraph(name=f'cluster_1') as c: | ||
c.attr(style='dotted') | ||
c.attr(label=f'Parent(s) of the Output Entry') | ||
dfs(byname_entry, c, __entry__, is_output=True) | ||
f.edge(byname_entry, "output") | ||
|
||
|
||
f.render(filename=f"{dest_dir}/image", view=False, cleanup=False) | ||
print("Graph is generated!") | ||
|
||
return return_this_entry | ||
else: | ||
print("ERROR! Provide correct entry name!") | ||
|
||
|
||
def find_parent(obj): | ||
items = find_key(obj, "_parent_entries") | ||
return items | ||
|
||
def find_byname(file_path, obj=None): | ||
obj=process_json(file_path) | ||
items = find_key(obj, "byname") | ||
print("items",items) | ||
return [list(item)[2] for item in items] | ||
|
||
def find_key(obj, key): | ||
matches = [] | ||
if isinstance(obj, dict): | ||
for k, v in obj.items(): | ||
if k == key: | ||
return v | ||
matches.extend(find_key(v, key)) | ||
result = find_key(v, key) | ||
if result is not None: | ||
return result | ||
elif isinstance(obj, list): | ||
if key == "_parent_entries" and re.search(r"^\[(?:'|\")_parent_entries(.*)", str(obj)): | ||
matches.append(obj) | ||
if key == "byname" and re.search(r"^\[(?:'|\")\^(?:'|\")(?:\s*),(?:\s*)(?:'|\")byname(.*)", str(obj)): | ||
matches.append(obj) | ||
for item in obj: | ||
matches.extend(find_key(item, key)) | ||
|
||
return matches | ||
|
||
def process_json(file_path): | ||
with open(file_path) as f: | ||
obj = json.load(f) | ||
required_data = {key: obj[key] for key in ['output_file_path', 'output_entry'] if key in obj} | ||
parents = find_parent(required_data) | ||
return parents | ||
|
||
|
||
def extract_byname_entries(output_entries): | ||
byname_entries = [] | ||
for item in output_entries: | ||
if isinstance(item, list) and 'byname' in item: | ||
index = item.index('byname') + 1 | ||
if index < len(item): | ||
byname_entries.append(item[index]) | ||
return byname_entries | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import json | ||
import os | ||
|
||
base_directory = os.path.expanduser('~/axs/core_collection/workflows_collection') | ||
|
||
def get_contained_entries_keys_from_multiple_locations(): | ||
folders = ['bert', 'image_classification', 'object_detection'] | ||
keys_dict = {} | ||
workflows_directory = os.path.expanduser('~/axs/core_collection/workflows_collection') | ||
|
||
for folder in folders: | ||
for root, dirs, files in os.walk(os.path.join(workflows_directory, folder)): | ||
if 'data_axs.json' in files: | ||
json_file_path = os.path.join(root, 'data_axs.json') | ||
|
||
print(f"Checking existence of {json_file_path}") # Debug line | ||
|
||
with open(json_file_path, 'r') as f: | ||
data = json.load(f) | ||
keys = list(data.get('contained_entries', {}).keys()) | ||
|
||
if folder in keys_dict: | ||
keys_dict[folder].extend(keys) | ||
else: | ||
keys_dict[folder] = keys | ||
else: | ||
print(f"The JSON file at {os.path.join(root, 'data_axs.json')} doesn't exist.") | ||
|
||
return keys_dict | ||
|
||
contained_entries_keys_dict = get_contained_entries_keys_from_multiple_locations() | ||
|
||
key_json_paths = [] | ||
|
||
for folder, keys in contained_entries_keys_dict.items(): | ||
for key in keys: | ||
key_json_path = os.path.join(base_directory,folder, key, 'data_axs.json') | ||
key_json_paths.append(key_json_path) | ||
|
||
def read_entries(key_json_paths, entry_key): | ||
entries_dict = {} | ||
for key_json_path in key_json_paths: | ||
if os.path.exists(key_json_path): | ||
with open(key_json_path, 'r') as f: | ||
data = json.load(f) | ||
key_name = os.path.basename(os.path.dirname(key_json_path)) | ||
entries_dict[key_name] = [entry[-1] for entry in data.get('_parent_entries', [])] | ||
else: | ||
print(f"The JSON file at {key_json_path} doesn't exist.") | ||
return entries_dict | ||
|
||
# Read parent entries | ||
parent_entries_dict = read_entries(key_json_paths, '_parent_entries') | ||
|
||
def read_output_parent_entries(key_json_paths): | ||
output_parent_entries_dict = {} | ||
for key_json_path in key_json_paths: | ||
if os.path.exists(key_json_path): | ||
with open(key_json_path, 'r') as f: | ||
data = json.load(f) | ||
output_key_name = os.path.basename(os.path.dirname(key_json_path)) | ||
|
||
final_entries = [] | ||
for entry in data.get('output_entry_parents', []): | ||
if isinstance(entry, list) and 'byname' in entry: | ||
index = entry.index('byname') + 1 | ||
if index < len(entry): | ||
final_entries.append(entry[index]) | ||
|
||
if final_entries: | ||
output_parent_entries_dict[output_key_name] = final_entries | ||
else: | ||
output_parent_entries_dict[output_key_name] = data.get('output_entry_parents', []) | ||
|
||
else: | ||
print(f"The JSON file at {key_json_path} doesn't exist.") | ||
|
||
return output_parent_entries_dict | ||
|
||
# Read output parent entries | ||
output_parent_entries_dict = read_output_parent_entries(key_json_paths) | ||
|
||
# Save to a JSON file | ||
with open('parent_entries_dict.json', 'w') as f: | ||
json.dump(parent_entries_dict, f, indent=4) | ||
|
||
with open('output_parent_entries_dict.json', 'w') as f: | ||
json.dump(output_parent_entries_dict, f, indent=4) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"_producer_rules": [ | ||
[ [ "graph_output" ], [["draw"]], { }, [ "target"]]], | ||
"target" : "shell", | ||
"return_this_entry": [ "^^", "execute", [[ | ||
[ "get", "__record_entry__" ], | ||
[ "attach", [ "^", "work_collection" ] ], | ||
[ "plant", [ "^^", "substitute", [[ | ||
"tags", [ "graph_output"], | ||
"target", "#{target}#" | ||
]] ] ], | ||
[ "save" ] | ||
]] ] | ||
|
||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.