Source code for entoscope.operation.plan

from __future__ import annotations

import textwrap
from dataclasses import dataclass, field
from enum import Enum

import pandas as pd

from entoscope.operation.config import AbstractConfig

MAX_LISTED = 10


def _listed(items: list[str]) -> str:
    if len(items) <= MAX_LISTED:
        return ", ".join(items)
    return f"{', '.join(items[:MAX_LISTED])}, +{len(items) - MAX_LISTED} more"


def _printable(index: pd.DataFrame) -> pd.DataFrame:
    """Spell an index's locations as ``index.csv`` does, since ``to_string`` prints an enum by its member name.

    Args:
        index: An index frame.

    Returns:
        The frame with every enum cell replaced by its value.
    """
    return index.map(lambda cell: cell.value if isinstance(cell, Enum) else cell)


def _plural(count: int, noun: str) -> str:
    if count == 1:
        return f"{count} {noun}"
    return f"{count} {noun}{'es' if noun.endswith(('s', 'x', 'ch', 'sh')) else 's'}"


[docs] @dataclass class OperationPlan: """What a run would do, as an operator's ``plan`` reports it. ``str(plan)`` gives ``describe()``. Attributes: operation: The operation's name, such as ``"Lens"``. method: The name of the method the config runs. config: The config the run would be made under. steps: How many units of work the run makes. steps_label: What a unit of work is: an index element, a probe, a patched run or a prompt. forward_passes: Every forward pass the run makes, the cached runs of the prompts included. backward_passes: Every backward pass the run makes. batches: How many batches the run makes, set only for a method that runs the model a batch at a time. index: One frame per index, the same frame ``index.csv`` would hold. prompts: One ``(label, prompt, token count)`` per prompt. activations: The transformer activation names the run caches. reads_full_cache: Whether the run caches every activation, in which case ``activations`` is empty. save_dir: The directory the run would write into. outputs: The file names the run would write into ``save_dir``. """ operation: str method: str config: AbstractConfig steps: int steps_label: str forward_passes: int backward_passes: int = 0 batches: int | None = None index: list[pd.DataFrame] = field(default_factory=list) prompts: list[tuple[str, str, int]] = field(default_factory=list) activations: list[str] = field(default_factory=list) reads_full_cache: bool = False save_dir: str | None = None outputs: list[str] = field(default_factory=list)
[docs] def describe(self, max_rows: int = 100) -> str: """Render the plan as text, which is what ``str(plan)`` and the log show. Args: max_rows: The most rows of each index to print. Returns: The plan, one fact per line, followed by each index. """ lines = [f"{self.operation} plan", f" method: {self.method}"] if self.config.config_id: lines.append(f" config: {self.config.config_id}") for label, prompt, n_tokens in self.prompts[:MAX_LISTED]: lines.append(f' {label}: "{prompt}" ({_plural(n_tokens, "token")})') if len(self.prompts) > MAX_LISTED: lines.append(f" +{len(self.prompts) - MAX_LISTED} more prompts") if self.reads_full_cache: lines.append(" reads: full activation cache") elif self.activations: lines.append(f" reads: {_listed(self.activations)}") lines.append(f" work: {self._work()}") lines.append(f" writes: {self._writes()}") for i, index in enumerate(self.index): name = "index" if len(self.index) == 1 else f"index {i}" lines.append(f" {name} ({_plural(len(index), 'row')}):") lines.append(textwrap.indent(_printable(index).to_string(index=False, max_rows=max_rows), " ")) return "\n".join(lines)
def _work(self) -> str: work = _plural(self.steps, self.steps_label) if self.batches is not None: work = f"{work} in {_plural(self.batches, 'batch')}" passes = _plural(self.forward_passes, "forward pass") if self.backward_passes: passes = f"{passes}, {_plural(self.backward_passes, 'backward pass')}" return f"{work}, {passes}" def _writes(self) -> str: if self.save_dir is None: return "nothing (no save_dir)" return f"{self.save_dir}/{{{_listed(self.outputs)}}}" def __str__(self) -> str: return self.describe()