Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 6 additions & 4 deletions test/unit_test/circle/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from tico.circle.cli.main import _build_parser, _parse_passes
from tico.circle.passes.cleanup import CompactIndicesPass, DeadCodeEliminationPass
from tico.circle.passes.optimization import RemoveRedundantLayoutOpsPass


class CircleCLITest(unittest.TestCase):
Expand All @@ -38,8 +39,9 @@ def test_extract_accepts_tensor_patterns_without_marker_flag(self):
self.assertEqual(args.from_tensor, ["input"])
self.assertEqual(args.to_tensor, ["output"])

def test_cleanup_pass_names_are_resolved(self):
passes = _parse_passes("dce,compact")
def test_optimization_and_cleanup_pass_names_are_resolved(self):
passes = _parse_passes("remove-redundant-layout-ops,dce,compact")

self.assertIsInstance(passes[0], DeadCodeEliminationPass)
self.assertIsInstance(passes[1], CompactIndicesPass)
self.assertIsInstance(passes[0], RemoveRedundantLayoutOpsPass)
self.assertIsInstance(passes[1], DeadCodeEliminationPass)
self.assertIsInstance(passes[2], CompactIndicesPass)
29 changes: 22 additions & 7 deletions tico/circle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ CircleDocument
├── inspect stable summaries and text output
├── operations.extract workflow-level graph extraction
└── passes composable Circle-to-Circle rewrites
├── RemoveRedundantLayoutOpsPass
├── DeadCodeEliminationPass
└── CompactIndicesPass
```
Expand Down Expand Up @@ -124,26 +125,32 @@ result = extract_by_tensor_patterns(
result.document.save("attention.circle")
```

### Run cleanup passes
### Run optimization and cleanup passes

```python
from tico.circle.passes import CirclePassManager
from tico.circle.passes import CirclePassManager, RemoveRedundantLayoutOpsPass
from tico.circle.passes.cleanup import (
CompactIndicesPass,
DeadCodeEliminationPass,
)

pipeline = CirclePassManager(
[
RemoveRedundantLayoutOpsPass(),
DeadCodeEliminationPass(),
CompactIndicesPass(),
]
)
result = pipeline.run(model)
print(result.changes)
model.save("model.cleaned.circle")
model.save("model.optimized.circle")
```

`RemoveRedundantLayoutOpsPass` rewires consecutive Reshape operations and inverse
Transpose pairs so their redundant operators become dead. Run dead-code elimination
after it to remove those operators, then compact the remaining tensor, buffer, and
operator-code indices.

By default, `CirclePassManager` verifies the document after every pass.
Set `CirclePassContext(verify_after_each_pass=False)` only when a multi-step
transformation intentionally has a temporary invalid state and performs
Expand Down Expand Up @@ -265,24 +272,31 @@ Signatures for untouched subgraphs remain intact when `--keep-other-subgraphs` i

```bash
tico-circle optimize model.circle \
--passes dce,compact \
-o model.cleaned.circle
--passes remove-redundant-layout-ops,dce,compact \
-o model.optimized.circle
```

Available first-stage passes:
Available passes:

| Name | Implementation | Behavior |
|---|---|---|
| `remove-redundant-layout-ops` | `RemoveRedundantLayoutOpsPass` | Rewires consecutive Reshape operations and consecutive inverse Transpose pairs so redundant operators can be removed |
| `dce` | `DeadCodeEliminationPass` | Removes operators that cannot contribute to graph outputs and prunes unused graph inputs |
| `compact` | `CompactIndicesPass` | Removes unused tensors, buffers, and operator codes and remaps all supported references |

`--passes` defaults to `dce,compact`. To remove redundant layout patterns, select the
three-pass pipeline shown above. Its order matters: the layout pass rewires dataflow,
`dce` removes the newly dead operators, and `compact` removes and remaps unused objects.

### Standard input and output

Use `-` for a binary stream:

```bash
tico-circle extract model.circle --ops 0-100 -o - \
| tico-circle optimize - --passes dce,compact -o output.circle
| tico-circle optimize - \
--passes remove-redundant-layout-ops,dce,compact \
-o output.circle
```

Do not redirect `inspect` text into a Circle transformation command; `inspect` writes text by design.
Expand Down Expand Up @@ -346,6 +360,7 @@ Important test scenarios include:

- graph producer and consumer indexing
- operator and tensor-boundary selection
- redundant Reshape and inverse Transpose elimination
- dead branch elimination
- signature tensor-map remapping
- shared buffer preservation across two subgraphs
Expand Down
27 changes: 18 additions & 9 deletions tico/circle/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@
extract_by_tensor_patterns,
SignaturePolicy,
)
from tico.circle.passes import CirclePass, CirclePassContext, CirclePassManager
from tico.circle.passes import (
CirclePass,
CirclePassContext,
CirclePassManager,
RemoveRedundantLayoutOpsPass,
)
from tico.circle.passes.cleanup import CompactIndicesPass, DeadCodeEliminationPass
from tico.circle.selector import parse_operator_spec

LOGGER = logging.getLogger("tico.circle.cli")

_PASS_REGISTRY: dict[str, type[CirclePass]] = {
"remove-redundant-layout-ops": RemoveRedundantLayoutOpsPass,
"dce": DeadCodeEliminationPass,
"compact": CompactIndicesPass,
}


def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -137,7 +148,7 @@ def _build_parser() -> argparse.ArgumentParser:
extract_parser.set_defaults(handler=_extract_command)

optimize_parser = subparsers.add_parser(
"optimize", help="Run cleanup passes over a Circle model."
"optimize", help="Run optimization and cleanup passes over a Circle model."
)
optimize_parser.add_argument("input", help="Input .circle path or '-' for stdin.")
optimize_parser.add_argument(
Expand All @@ -146,7 +157,9 @@ def _build_parser() -> argparse.ArgumentParser:
optimize_parser.add_argument(
"--passes",
default="dce,compact",
help="Comma-separated passes. Available values: dce, compact.",
help=(
"Comma-separated passes. Available values: " f"{', '.join(_PASS_REGISTRY)}."
),
)
optimize_parser.add_argument(
"--no-verify",
Expand Down Expand Up @@ -238,21 +251,17 @@ def _extract_command(args: argparse.Namespace) -> int:


def _parse_passes(value: str) -> list[CirclePass]:
registry: dict[str, type[CirclePass]] = {
"dce": DeadCodeEliminationPass,
"compact": CompactIndicesPass,
}
passes: list[CirclePass] = []
for raw_name in value.split(","):
name = raw_name.strip().lower()
if not name:
continue
try:
passes.append(registry[name]())
passes.append(_PASS_REGISTRY[name]())
except KeyError as error:
raise ValueError(
f"Unknown Circle pass {name!r}; available passes are "
f"{sorted(registry)}."
f"{sorted(_PASS_REGISTRY)}."
) from error
if not passes:
raise ValueError("At least one Circle pass must be selected.")
Expand Down
Loading