Source code for aigverse.abc._commands

"""Wrappers for the individual ABC optimization commands.

The canonical scripts in :mod:`._wrappers` are sequences of these four commands.
Exposing them individually makes it possible to compose a schedule from Python --
for instance in a reinforcement-learning loop over synthesis actions -- without
assembling ABC command strings by hand.

Every wrapper here is callable on a network, and its ``cmd()`` builds the same
command without running it, so a parameterized command can be handed to
:func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many` like any other.

Every one of these is an ABC builtin, so they work regardless of whether an
``abc.rc`` can be found.

A note on levels: ABC's ``-l`` switch *toggles* a default of "preserve the number
of levels", so passing it turns level preservation off. These wrappers expose the
resulting behaviour directly as ``preserve_levels`` rather than the switch, so
that ``preserve_levels=False`` reads as what it does.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Final

from ._options import check_option
from ._runner import AigT, Command, CommandWrapper, run_script

if TYPE_CHECKING:
    import os

__all__ = [
    "Balance",
    "Orchestrate",
    "Refactor",
    "Resub",
    "Rewrite",
    "balance",
    "orchestrate",
    "refactor",
    "resub",
    "rewrite",
]


[docs] class Balance(CommandWrapper): """Runs ABC's ``balance`` command on a network, or builds it as a command."""
[docs] @staticmethod def cmd( *, minimize_levels: bool = True, exor: bool = False, duplicate: bool = False, duplicate_critical: bool = False, ) -> Command: """Builds ABC's ``balance`` command without running it. Args: minimize_levels: If ``True`` (ABC's default), balance for minimal depth. exor: If ``True``, balance multi-input EXOR structures as well. duplicate: If ``True``, allow logic to be duplicated. duplicate_critical: If ``True``, duplicate logic on the critical paths only, which buys depth for less area than ``duplicate`` does. Returns: The command, ready for :func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many`. """ command = "balance" if not minimize_levels: command += " -l" if duplicate: command += " -d" if duplicate_critical: command += " -s" if exor: command += " -x" return Command(command)
[docs] def __call__( self, ntk: AigT, *, minimize_levels: bool = True, exor: bool = False, duplicate: bool = False, duplicate_critical: bool = False, timeout: float | None = None, verbose: bool = False, binary: str | os.PathLike[str] | None = None, ) -> AigT: """Runs ABC's ``balance`` command on a network. Restructures the AND trees of the network to reduce its depth. Args: ntk: The network to optimize. minimize_levels: If ``True`` (ABC's default), balance for minimal depth. exor: If ``True``, balance multi-input EXOR structures as well. duplicate: If ``True``, allow logic to be duplicated. duplicate_critical: If ``True``, duplicate logic on the critical paths only, which buys depth for less area than ``duplicate`` does. timeout: Seconds to wait for ABC to terminate, or ``None`` for no limit. verbose: If ``True``, print everything ABC wrote. binary: Overrides the resolved ABC executable for this call only. Returns: The optimized network, of the same type as ``ntk``. """ command = self.cmd( minimize_levels=minimize_levels, exor=exor, duplicate=duplicate, duplicate_critical=duplicate_critical, ) return run_script(ntk, command, timeout=timeout, verbose=verbose, binary=binary)
balance: Final[Balance] = Balance() """Runs ABC's ``balance`` command on a network, or builds it as a command; see :class:`Balance`."""
[docs] class Rewrite(CommandWrapper): """Runs ABC's ``rewrite`` command on a network, or builds it as a command."""
[docs] @staticmethod def cmd(*, preserve_levels: bool = True, zero_cost: bool = False) -> Command: """Builds ABC's ``rewrite`` command without running it. Args: preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. Returns: The command, ready for :func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many`. """ command = "rewrite" if not preserve_levels: command += " -l" if zero_cost: command += " -z" return Command(command)
[docs] def __call__( self, ntk: AigT, *, preserve_levels: bool = True, zero_cost: bool = False, timeout: float | None = None, verbose: bool = False, binary: str | os.PathLike[str] | None = None, ) -> AigT: """Runs ABC's ``rewrite`` command on a network. Replaces 4-input subgraphs with smaller pre-computed equivalents. Args: ntk: The network to optimize. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. timeout: Seconds to wait for ABC to terminate, or ``None`` for no limit. verbose: If ``True``, print everything ABC wrote. binary: Overrides the resolved ABC executable for this call only. Returns: The optimized network, of the same type as ``ntk``. """ command = self.cmd(preserve_levels=preserve_levels, zero_cost=zero_cost) return run_script(ntk, command, timeout=timeout, verbose=verbose, binary=binary)
rewrite: Final[Rewrite] = Rewrite() """Runs ABC's ``rewrite`` command on a network, or builds it as a command; see :class:`Rewrite`."""
[docs] class Refactor(CommandWrapper): """Runs ABC's ``refactor`` command on a network, or builds it as a command."""
[docs] @staticmethod def cmd( *, max_support: int | None = None, min_saved: int | None = None, preserve_levels: bool = True, zero_cost: bool = False, ) -> Command: """Builds ABC's ``refactor`` command without running it. Args: max_support: Maximum support of a collapsed node (ABC's ``-N``, 1 to 15), or ``None`` for ABC's default of 10. Larger values are slower. ABC documents no range but rejects anything above 15. min_saved: Minimum number of nodes a single step must save to be applied (ABC's ``-M``, at least 0), or ``None`` for ABC's default of 1. Setting it to 0 accepts steps that save nothing. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. Returns: The command, ready for :func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many`. Raises: ValueError: If ``max_support`` is outside the range ABC accepts. """ command = "refactor" if max_support is not None: check_option("refactor", "N", max_support, name="max_support") command += f" -N {max_support}" if min_saved is not None: check_option("refactor", "M", min_saved, name="min_saved") command += f" -M {min_saved}" if not preserve_levels: command += " -l" if zero_cost: command += " -z" return Command(command)
[docs] def __call__( self, ntk: AigT, *, max_support: int | None = None, min_saved: int | None = None, preserve_levels: bool = True, zero_cost: bool = False, timeout: float | None = None, verbose: bool = False, binary: str | os.PathLike[str] | None = None, ) -> AigT: """Runs ABC's ``refactor`` command on a network. Collapses a cone into a single node and resynthesizes it from its truth table, which reaches larger cuts than :obj:`rewrite` does. Args: ntk: The network to optimize. max_support: Maximum support of a collapsed node (ABC's ``-N``, 1 to 15), or ``None`` for ABC's default of 10. Larger values are slower. ABC documents no range but rejects anything above 15. min_saved: Minimum number of nodes a single step must save to be applied (ABC's ``-M``, at least 0), or ``None`` for ABC's default of 1. Setting it to 0 accepts steps that save nothing. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. timeout: Seconds to wait for ABC to terminate, or ``None`` for no limit. verbose: If ``True``, print everything ABC wrote. binary: Overrides the resolved ABC executable for this call only. Returns: The optimized network, of the same type as ``ntk``. Raises: ValueError: If ``max_support`` is outside the range ABC accepts. """ command = self.cmd( max_support=max_support, min_saved=min_saved, preserve_levels=preserve_levels, zero_cost=zero_cost, ) return run_script(ntk, command, timeout=timeout, verbose=verbose, binary=binary)
refactor: Final[Refactor] = Refactor() """Runs ABC's ``refactor`` command on a network, or builds it as a command; see :class:`Refactor`."""
[docs] class Resub(CommandWrapper): """Runs ABC's ``resub`` command on a network, or builds it as a command."""
[docs] @staticmethod def cmd( *, max_cut_size: int | None = None, max_inserts: int | None = None, min_saved: int | None = None, odc_levels: int | None = None, preserve_levels: bool = True, zero_cost: bool = False, ) -> Command: """Builds ABC's ``resub`` command without running it. Args: max_cut_size: Maximum cut size (ABC's ``-K``, 4 to 16), or ``None`` for ABC's default of 8. The canonical scripts sweep this from 6 to 12. max_inserts: Maximum number of nodes to add (ABC's ``-N``, 0 to 3), or ``None`` for ABC's default of 1. min_saved: Minimum number of nodes a single step must save to be applied (ABC's ``-M``, at least 0), or ``None`` for ABC's default of 1. odc_levels: Fanout levels used for observability-don't-care computation (ABC's ``-F``, at least 0), or ``None`` for ABC's default of 0, which disables it. Don't-cares find substitutions that are only valid in context, at the cost of a more expensive analysis. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. Returns: The command, ready for :func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many`. Raises: ValueError: If ``max_cut_size`` or ``max_inserts`` is outside the range ABC accepts. """ command = "resub" if max_cut_size is not None: check_option("resub", "K", max_cut_size, name="max_cut_size") command += f" -K {max_cut_size}" if max_inserts is not None: check_option("resub", "N", max_inserts, name="max_inserts") command += f" -N {max_inserts}" if min_saved is not None: check_option("resub", "M", min_saved, name="min_saved") command += f" -M {min_saved}" if odc_levels is not None: check_option("resub", "F", odc_levels, name="odc_levels") command += f" -F {odc_levels}" if not preserve_levels: command += " -l" if zero_cost: command += " -z" return Command(command)
[docs] def __call__( self, ntk: AigT, *, max_cut_size: int | None = None, max_inserts: int | None = None, min_saved: int | None = None, odc_levels: int | None = None, preserve_levels: bool = True, zero_cost: bool = False, timeout: float | None = None, verbose: bool = False, binary: str | os.PathLike[str] | None = None, ) -> AigT: """Runs ABC's ``resub`` command on a network. Re-expresses a node in terms of other nodes already present, which removes logic that rewriting and refactoring cannot reach because it is not local. Args: ntk: The network to optimize. max_cut_size: Maximum cut size (ABC's ``-K``, 4 to 16), or ``None`` for ABC's default of 8. The canonical scripts sweep this from 6 to 12. max_inserts: Maximum number of nodes to add (ABC's ``-N``, 0 to 3), or ``None`` for ABC's default of 1. min_saved: Minimum number of nodes a single step must save to be applied (ABC's ``-M``, at least 0), or ``None`` for ABC's default of 1. odc_levels: Fanout levels used for observability-don't-care computation (ABC's ``-F``, at least 0), or ``None`` for ABC's default of 0, which disables it. Don't-cares find substitutions that are only valid in context, at the cost of a more expensive analysis. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost: If ``True``, also apply replacements that do not reduce the size, which perturbs the structure and can unlock later gains. timeout: Seconds to wait for ABC to terminate, or ``None`` for no limit. verbose: If ``True``, print everything ABC wrote. binary: Overrides the resolved ABC executable for this call only. Returns: The optimized network, of the same type as ``ntk``. Raises: ValueError: If ``max_cut_size`` or ``max_inserts`` is outside the range ABC accepts. """ command = self.cmd( max_cut_size=max_cut_size, max_inserts=max_inserts, min_saved=min_saved, odc_levels=odc_levels, preserve_levels=preserve_levels, zero_cost=zero_cost, ) return run_script(ntk, command, timeout=timeout, verbose=verbose, binary=binary)
resub: Final[Resub] = Resub() """Runs ABC's ``resub`` command on a network, or builds it as a command; see :class:`Resub`."""
[docs] class Orchestrate(CommandWrapper): """Runs ABC's ``orchestrate`` command on a network, or builds it as a command."""
[docs] @staticmethod def cmd( *, max_cut_size: int | None = None, max_inserts: int | None = None, odc_levels: int | None = None, preserve_levels: bool = True, zero_cost_rewrite: bool = True, zero_cost_refactor: bool = True, ) -> Command: """Builds ABC's ``orchestrate`` command without running it. Args: max_cut_size: Resubstitution cut size (ABC's ``-K``, 4 to 16), or ``None`` for ABC's default of 8. max_inserts: Nodes resubstitution may add (ABC's ``-N``, 0 to 3), or ``None`` for ABC's default of 1. odc_levels: Fanout levels used for don't-care computation (ABC's ``-F``), or ``None`` for ABC's default of 0. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost_rewrite: If ``True`` (ABC's default here), let the rewriting part apply replacements that do not reduce the size. zero_cost_refactor: If ``True`` (ABC's default here), the same for the refactoring part. Returns: The command, ready for :func:`~aigverse.abc.run_script` or :func:`~aigverse.abc.run_many`. Raises: ValueError: If an option is outside the range ABC accepts. """ command = "orchestrate" if max_cut_size is not None: check_option("orchestrate", "K", max_cut_size, name="max_cut_size") command += f" -K {max_cut_size}" if max_inserts is not None: check_option("orchestrate", "N", max_inserts, name="max_inserts") command += f" -N {max_inserts}" if odc_levels is not None: check_option("orchestrate", "F", odc_levels, name="odc_levels") command += f" -F {odc_levels}" # every one of these switches toggles a default of "on" if not preserve_levels: command += " -l" if not zero_cost_rewrite: command += " -z" if not zero_cost_refactor: command += " -Z" return Command(command)
[docs] def __call__( self, ntk: AigT, *, max_cut_size: int | None = None, max_inserts: int | None = None, odc_levels: int | None = None, preserve_levels: bool = True, zero_cost_rewrite: bool = True, zero_cost_refactor: bool = True, timeout: float | None = None, verbose: bool = False, binary: str | os.PathLike[str] | None = None, ) -> AigT: """Runs ABC's ``orchestrate`` command on a network. Interleaves rewriting, refactoring and resubstitution rather than running them one after another, choosing per node which of the three to apply. It is a single command doing the job of a whole schedule. Note that ABC enables zero-cost replacements here by default, unlike in the standalone :obj:`rewrite` and :obj:`refactor` commands. Args: ntk: The network to optimize. max_cut_size: Resubstitution cut size (ABC's ``-K``, 4 to 16), or ``None`` for ABC's default of 8. max_inserts: Nodes resubstitution may add (ABC's ``-N``, 0 to 3), or ``None`` for ABC's default of 1. odc_levels: Fanout levels used for don't-care computation (ABC's ``-F``), or ``None`` for ABC's default of 0. preserve_levels: If ``True`` (ABC's default), never increase the depth. zero_cost_rewrite: If ``True`` (ABC's default here), let the rewriting part apply replacements that do not reduce the size. zero_cost_refactor: If ``True`` (ABC's default here), the same for the refactoring part. timeout: Seconds to wait for ABC to terminate, or ``None`` for no limit. verbose: If ``True``, print everything ABC wrote. binary: Overrides the resolved ABC executable for this call only. Returns: The optimized network, of the same type as ``ntk``. Raises: ValueError: If an option is outside the range ABC accepts. """ command = self.cmd( max_cut_size=max_cut_size, max_inserts=max_inserts, odc_levels=odc_levels, preserve_levels=preserve_levels, zero_cost_rewrite=zero_cost_rewrite, zero_cost_refactor=zero_cost_refactor, ) return run_script(ntk, command, timeout=timeout, verbose=verbose, binary=binary)
orchestrate: Final[Orchestrate] = Orchestrate() """Runs ABC's ``orchestrate`` command on a network, or builds it as a command; see :class:`Orchestrate`."""