Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a4404c3
Reorganize ECO: Move C++ GED implementation from mcs_ged/ to eco/
alirezazd Feb 9, 2026
3fc6772
ECO: Organize third-party libraries into subdirectories
alirezazd Feb 9, 2026
a2e1ed9
ECO/GED: Add core graph data structures
alirezazd Feb 9, 2026
ff87ed7
ECO/GED: Add linear assignment problem solver
alirezazd Feb 9, 2026
2beb4b4
ECO/GED: Implement graph edit distance algorithm
alirezazd Feb 9, 2026
76c55a4
ECO: Update BUILD with GED/MCS targets
alirezazd Feb 9, 2026
dd9229a
ECO/GED: Add maximum common subgraph preprocessing
alirezazd Feb 9, 2026
047cb2a
ECO/GED: Add command-line tool for graph edit distance
alirezazd Feb 9, 2026
f7d0570
ECO: Integrate GED with IR patching system
alirezazd Feb 9, 2026
a7106ec
ECO: Add build rules and integration tests
alirezazd Feb 9, 2026
6ba8e06
ECO: Fix build issues after rebase onto upstream/main
alirezazd Feb 9, 2026
999d63c
ECO: Merge upstream Python tools with deprecation notices
alirezazd Feb 9, 2026
bb1fe2b
ECO: Modernize diff and patch pipeline
alirezazd Feb 28, 2026
c0e6ea8
ECO: Update deprecation notice for GXL export tool
alirezazd Feb 28, 2026
75cbf3e
ECO: Update flag description for equivalence report path to match the…
alirezazd Feb 28, 2026
50c4e81
ECO: Enable execution stats report by default in xls_dslx_ir_diff_rule
alirezazd Feb 28, 2026
58e61c5
ECO: Update include paths and clean up code formatting across multipl…
alirezazd Mar 3, 2026
0d95a0b
ECO: Fixed edge matching logic and introduce PopulateMatchedEdges fu…
alirezazd Mar 3, 2026
17b40f5
ECO: switch report flags to path outputs and simplify ECO build tooling
alirezazd Mar 6, 2026
af31458
ECO: Bug Fixes
alirezazd Mar 6, 2026
3f07a58
ECO: Refactor MCS solver to use SearchContext struct and improve cand…
alirezazd Mar 15, 2026
cde6b87
ECO: Restore comments to clarify mcs_cutoff parameter in SolveMCS fun…
alirezazd Mar 15, 2026
df47750
[eco] refine MCS matching logic and add coverage
alirezazd Mar 25, 2026
56f3587
ECO: Remove outdated comments from testing hooks in mcs.h
alirezazd Mar 25, 2026
f0c838f
ECO: Update dependencies and refactor sorting to use Abseil algorithms
alirezazd Apr 13, 2026
4be2ec5
ECO: Move xls/eco to xls/contrib/eco
alirezazd Apr 13, 2026
63e80b8
Moved tinyxml to BCR
alirezazd May 5, 2026
885da65
feat: Enhance ECO with new attributes and graph conversion
alirezazd May 5, 2026
9a9c528
Refactor cost attributes in XLS graph representation
alirezazd May 5, 2026
e9ddcf5
refactor: Adjust namespace usage and improve code clarity in codegen_…
alirezazd May 5, 2026
bc800bc
ECO: Remove Python implementation, add README, style cleanup
alirezazd May 28, 2026
9f576d1
ECO: Use common Stopwatch for timing; tidy logging, namespacing, TODOs
alirezazd Jun 10, 2026
02bcab0
ECO: Use Stopwatch::GetElapsedTime() instead of manual time subtraction
alirezazd Jun 10, 2026
e93fa11
ECO: Migrate BUILD to bzlmod repo names
alirezazd Sep 1, 2026
edf4290
ECO: Catch up with upstream PipelineSchedule, InsertStateElement and …
alirezazd Sep 2, 2026
2a63d73
ECO: Look up state read by IR node name in xls_ir_to_graph_test
alirezazd Sep 1, 2026
2a2a053
ECO: Use int64_t instead of long in ged_main
alirezazd Oct 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
ECO: Merge upstream Python tools with deprecation notices
This commit contains two types of changes:
1. Upstream updates to ir2nx.py merged during rebase (large diff)
2. Our deprecation TODOs added to Python GED toolchain:
   - ir_diff.py: NetworkX GED → ged.h/cc (10-100x faster)
   - ir_diff_main.py: Python tool → ged_main.cc CLI
   - ir_patch_gen.py: Python patcher → patch_ir.h/cc
   - ir2nx.py: NetworkX converter → xls_ir_to_cytoscape.cc
   - ir2gxl.py: GXL exporter → gxl_parser.h/cc (from upstream)
  • Loading branch information
alirezazd committed Oct 10, 2026
commit 999d63c1aecde4d15bc15ba17472805a7e5b5103
199 changes: 199 additions & 0 deletions xls/eco/ir2gxl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#!/usr/bin/env python3

# TODO(b/1234567890): DEPRECATED - This GXL export tool will be replaced by
# C++ GXL parser in gxl_parser.h/cc which provides:
# - Bidirectional GXL parsing (not just export)
# - Better XML handling through tinyxml2
# - Integration with C++ GED implementation
# Please migrate to using the C++ GXL tools in //xls/eco.

"""IR to GXL conversion library for XLS ECO.

This module provides functionality to convert XLS IR files to GXL format
for use with the Java GED implementation.

TODO(eco): Deprecate this Python-based IR-to-GXL converter in favor of a
C++ implementation based on xls/visualization/ir_viz/xls_ir_to_cytoscape.cc.
The C++ approach would be more robust (using actual IR APIs instead of regex
parsing), faster, and better maintained. Consider extending the C++ cytoscape
tool to support GXL output format, or create a C++ GXL exporter that shares
the same IR graph construction logic.
"""

import sys
import os
from pathlib import Path
from typing import Optional
import networkx as nx

# Add the parent directory to the path so we can import the modules
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from xls.eco import ir2nx


class IrParser:
"""This class parses XLS intermediate representation (IR) into GXL format.

Similar interface to ir2nx.IrParser for consistency.
"""

def __init__(self, path=None, graph=None):
"""Initializes the IrParser object.

Args:
path: The path to the IR file (str or Path object). If None, graph must be provided.
graph: NetworkX graph to convert. If None, path must be provided.
"""
if path is not None:
self._path = Path(path)
self._ir_parser = ir2nx.IrParser(self._path)
self._nx_graph = self._ir_parser.graph
elif graph is not None:
self._path = None
self._nx_graph = graph
else:
raise ValueError("Either path or graph must be provided")

self.gxl = self._convert_to_gxl()

def _convert_to_gxl(self) -> str:
"""Convert the NetworkX graph to GXL format."""
wrapper = _NetworkXGraphWrapper(self._nx_graph)
return wrapper.to_gxl_string()


class _NetworkXGraphWrapper:
"""Internal wrapper for converting NetworkX graphs to GXL format."""

def __init__(self, nx_graph):
self.graph = nx_graph
self._convert_to_internal_format()

def _convert_to_internal_format(self):
"""Convert NetworkX graph to internal format."""
self.nodes = {}
self.edges = []
self.return_node = self.graph.graph.get("ret")

for node_id, node_data in self.graph.nodes(data=True):
self.nodes[str(node_id)] = {
"op": node_data.get("op", "unknown"),
"cost_attributes": node_data.get("cost_attributes", {}),
}

# Preserve multigraph edge keys so operand ordering survives.
for u, v, key, edge_data in self.graph.edges(keys=True, data=True):
self.edges.append(
{
"source": str(u),
"target": str(v),
"key": key,
"cost_attributes": edge_data.get("cost_attributes", {}),
}
)

def _serialize_cost_attributes(self, cost_attrs):
"""Serialize cost attributes to a parseable string format."""
if not cost_attrs:
return ""

parts = []
for key, value in cost_attrs.items():
if isinstance(value, (list, tuple)):
# Convert lists/tuples to comma-separated strings
value_str = ",".join(str(v) for v in value)
parts.append(f"{key}={value_str}")
elif isinstance(value, bool):
# Convert booleans to "true"/"false"
parts.append(f"{key}={str(value).lower()}")
else:
# Simple string/number values
parts.append(f"{key}={value}")

return "|".join(parts) # Use | as separator between attributes

def to_gxl_string(self) -> str:
"""Convert to GXL string format."""
gxl_content = f"""<?xml version="1.0" encoding="UTF-8"?>
<gxl>
<graph id="graph" edgeids="true" edgemode="directed">
"""
if self.return_node is not None:
gxl_content += f""" <attr name="ret">
<string>{self.return_node}</string>
</attr>
"""

for node_id, node_data in self.nodes.items():
cost_attr_str = self._serialize_cost_attributes(
node_data["cost_attributes"]
)
gxl_content += f""" <node id="{node_id}">
<attr name="op">
<string>{node_data['op']}</string>
</attr>
<attr name="cost_attributes">
<string>{cost_attr_str}</string>
</attr>
</node>
"""

# Emit edges and include operand index info when available.
for i, edge in enumerate(self.edges):
cost_attr_str = self._serialize_cost_attributes(edge["cost_attributes"])
gxl_content += f""" <edge id="e{i}" from="{edge['source']}" to="{edge['target']}">
<attr name="cost_attributes">
<string>{cost_attr_str}</string>
</attr>
"""
if edge.get("key") is not None:
gxl_content += f""" <attr name="index">
<string>{edge['key']}</string>
</attr>
"""
gxl_content += " </edge>\n"

gxl_content += """ </graph>
</gxl>"""
return gxl_content


def ir_to_gxl(ir_file_path: str) -> str:
"""Convenience function to convert IR file to GXL string.

Args:
ir_file_path: Path to the IR file

Returns:
GXL string representation
"""
parser = IrParser(ir_file_path)
return parser.gxl


# Removed nx_to_gxl function - use IrParser(graph=nx_graph).gxl instead


def main():
"""Command-line interface for IR to GXL conversion."""
if len(sys.argv) != 2:
print("Usage: python ir2gxl.py <ir_file_path>")
sys.exit(1)

ir_file_path = sys.argv[1]
if not os.path.exists(ir_file_path):
print(f"Error: IR file not found: {ir_file_path}")
sys.exit(1)

try:
# Use the same pattern as ir2nx
parser = IrParser(ir_file_path)
print(parser.gxl)
except Exception as e:
print(f"Error converting IR to GXL: {e}")
sys.exit(1)


if __name__ == "__main__":
main()
Loading