# scope_profiler.report_model

*module*

What a profiling report says, as plain data.

The HTML report and the Astro catalogue pages show the same things: headline
numbers, findings, hotspots, tables, charts. The decisions about what to say
(which findings apply, how a change is classified, which rows lead) live here,
as JSON-serializable models, so the two renderers cannot drift apart: each only
decides how to draw them.

Text that mentions a region is a list of *parts*: a plain string, or a mapping
such as ``{"region": "solve"}`` (a region to link to its row in the table) or
``{"anchor": "balance", "text": "load balance"}`` (a link to a section). The
renderer escapes the strings and decides where the links point.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1-L1)

## scope_profiler.report_model.balance_model

*function*

```python
def balance_model(results, balance: dict) -> dict
```

How evenly each region's time is spread over the ranks.

``rows`` are ordered by excess (the time balancing could save); each has the
mean, min and max per-rank time, the slowest rank, the imbalance in percent,
and ``low``, ``high`` and ``mean_pos`` as percentages of the widest bar for
the min-mean-max spread. ``limit`` rows are shown before "show all".

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L652-L689)

## scope_profiler.report_model.candidate_model

*function*

```python
def candidate_model(runs, entries, run_index: int) -> dict
```

One run against the baseline: headline numbers and what moved.

``improvements`` and ``regressions`` are the largest own-time changes, each
with its size, share of the widest change (``width``) and a label.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L847-L881)

## scope_profiler.report_model.change_model

*function*

```python
def change_model(base, value, metric: str) -> dict
```

How ``value`` compares with ``base``, for a comparison table cell.

``kind`` is ``"none"`` (neither has it), ``"new"``, ``"gone"`` or ``"delta"``;
a delta has a ``tone`` (``faster``, ``slower``, ``same``, or, for call counts,
``neutral``), a ``pct`` change and a ``title`` giving its size. ``size`` is
the magnitude of the change, for sorting by it.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L317-L359)

## scope_profiler.report_model.chart_description_text

*function*

```python
def chart_description_text(title: str, payload: dict, comparison: bool = False) -> str
```

Explain how to read one chart; ``""`` for a chart without an explanation.

A scaling chart's text names its x-axis as ``{axis}``, which the renderer
replaces by the current axis label, since the reader can switch axes.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L927-L1002)

## scope_profiler.report_model.chart_panels

*function*

```python
def chart_panels(charts, comparison: bool = False, scaling: str = 'both') -> list[dict]
```

The chart panels of a report, from ``collect_chart_payloads``' charts.

Each panel has the chart's ``title``, ``payload`` and view ``options``, its
``description`` (with ``{axis}`` placeholders, see
:func:`chart_description_text`), its ``axis`` label, whether it starts
``open``, and, for a scaling chart, its x-axis ``variants`` (each with a
``label`` and its own ``payload``) with the index of the ``default_variant``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1005-L1082)

## scope_profiler.report_model.compare_dataset

*function*

```python
def compare_dataset(runs, per_run_rows, per_rank: bool) -> dict
```

Everything needed to compare any two of the runs, compactly, for a browser.

A comparison of 100 runs has 9,900 ordered pairs, too many to compute in
advance, so a page that lets the reader pick the two runs computes the pair in
the browser from this: ``regions`` (``{key: {name, depth, context}}``, a key
being the region's call path) and, per run, its wall time, the ``order`` of
its rows (the call tree) and ``values``, ``{key: [total, own, avg, calls]}``
with ``None`` where the run did not time the region. ``per_rank`` says that
the runs differ in rank counts, so totals and own times are per rank.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1191-L1244)

## scope_profiler.report_model.compare_table_model

*function*

```python
def compare_table_model(runs, entries, per_rank: bool = False) -> dict
```

Every region's duration in every run, against the baseline.

``rows`` follow the baseline's call tree. Each row has, per run, the text of
every metric in ``metrics`` and, for the runs after the baseline, a
:func:`change_model` against the baseline; ``sizes`` holds the size of the
last run's change per metric, to sort by.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L884-L924)

## scope_profiler.report_model.comparison_page_model

*function*

```python
def comparison_page_model(runs, include=None, exclude=None, ranks=None, sort='start', reference=None) -> dict
```

Everything the report shows about a comparison of runs, except its charts.

``dataset`` (see :func:`compare_dataset`) holds what a page needs to compare any
two runs. ``candidates`` and ``table`` are the report's comparison of the runs
against the first; with ``reference`` they cover only that many runs after the
first, as the reference a page's own computation is checked against.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1247-L1275)

## scope_profiler.report_model.deviation

*function*

```python
def deviation(value: float, mean: float) -> dict | None
```

How a value shades against its mean: red above, blue below, none near it.

Returns ``{"sign": 1 | -1, "alpha": float}``, or None for a value within
the noise of the mean.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L225-L240)

## scope_profiler.report_model.hardware_model

*function*

```python
def hardware_model(runs, include=None, exclude=None, ranks=None) -> list[dict]
```

Counter tables, only for the runs and ranks that recorded hardware metrics.

Each panel has a ``kind`` (``likwid`` or ``perf``), a ``title``, and tables:
a ``columns`` list and ``rows`` of already formatted cell texts.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L692-L744)

## scope_profiler.report_model.hotspots_model

*function*

```python
def hotspots_model(entries, base) -> dict
```

The largest leaves of the call tree, ranked, with their share of ``base``.

``items`` is empty when there is nothing to rank against (fewer than two
entries). Each item has the entry's ``name``, ``context`` and ``kind``
(``leaf``, ``own`` or ``outside``), its ``share`` in percent, ``details``
(time, calls, time per call) and, for a slow leaf, a ``flag``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L578-L613)

## scope_profiler.report_model.line_profile_list_model

*function*

```python
def line_profile_list_model(results, functions) -> dict
```

Line profiles of regions the table does not show: one entry per function.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1118-L1135)

## scope_profiler.report_model.line_profile_location

*function*

```python
def line_profile_location(results, entry: dict) -> dict
```

Where a profiled function is, and on how many ranks it ran.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L438-L446)

## scope_profiler.report_model.line_profile_model

*function*

```python
def line_profile_model(entry: dict) -> dict
```

One profiled function's source, each line with the time spent on it.

``rows`` has a row per line: ``number``, ``source`` (HTML with syntax
classes) and, for a line that was timed, ``hits``, ``seconds``, ``per_hit``
and ``percent``; the hottest line has ``hot`` set. ``has_source`` is false
when no line's source could be found, and then only timed lines are listed.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L362-L410)

## scope_profiler.report_model.matrix_model

*function*

```python
def matrix_model(balance: dict) -> dict
```

Every rank side by side, region by region, against the region's mean.

``rows`` and ``totals`` hold one cell per rank: its ``value`` in seconds, its
``label`` (the percent against the mean), its ``title`` and its ``shade``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L243-L314)

## scope_profiler.report_model.metadata_model

*function*

```python
def metadata_model(metadata: dict, keys=None) -> list[list[str]]
```

The run's recorded metadata as sorted ``[key, value]`` pairs.

``keys`` limits which fields are included; all of them by default.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L747-L764)

## scope_profiler.report_model.partition_line_profiles

*function*

```python
def partition_line_profiles(results, rows, ranks)
```

Line profiles split into those of tabled regions and the rest.

Returns ``(by_region, unmatched)``: ``by_region`` maps a region in the table
to its profiled functions, which its detail shows; ``unmatched`` lists the
functions of regions the table does not show, largest first.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1100-L1115)

## scope_profiler.report_model.region_detail_model

*function*

```python
def region_detail_model(results, region, ranks, functions=()) -> dict
```

What a region's expandable detail shows: call site, tags, ranks, lines.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L470-L505)

## scope_profiler.report_model.region_table_model

*function*

```python
def region_table_model(results, rows, ranks, columns, line_profiles=None) -> dict
```

Region statistics laid out like the terminal summary table.

Cell text comes from :func:`~scope_profiler.summary.format_region_table`, so
every renderer shows the same tree, call counts and ``(own)`` rows. Each
region is one entry of ``groups``, holding its row, its ``own`` row when it
has children, its sort keys, its call durations for a trend line and its
``detail``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L508-L575)

## scope_profiler.report_model.report_rows

*function*

```python
def report_rows(results, include, exclude, ranks, sort) -> list[dict]
```

The region rows a report is built from.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1085-L1097)

## scope_profiler.report_model.run_meta_model

*function*

```python
def run_meta_model(results, region_count: int, metadata_keys=None) -> dict
```

One line on where, when and for how long a run was profiled.

``metadata_keys`` limits which of the host-describing metadata fields
(``hostname``, ``chip_information``) are included; all of them by default.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L616-L649)

## scope_profiler.report_model.run_page_model

*function*

```python
def run_page_model(results, include=None, exclude=None, ranks=None, sort='start', columns=None, host_keys=None, metadata_keys=None) -> dict
```

Everything the report shows about one run, except its charts.

The sections are ``meta``, ``summary``, ``hotspots``, ``regions`` (the table),
``balance`` and ``matrix`` (None for a single rank), ``line_profiles`` (None
without any), ``metadata`` and ``hardware``. ``region_ids`` maps a region to
the ``id`` of its first row in ``regions["groups"]``, for links to it.

``host_keys`` and ``metadata_keys`` limit which host-describing fields the
header shows and which metadata fields are listed (all by default), for a
report that is published.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L1138-L1188)

## scope_profiler.report_model.runs_table_model

*function*

```python
def runs_table_model(runs) -> dict
```

One line per compared run: its recording, size, wall time and change.

``change`` is a :func:`change_model` of the run's wall time against the
baseline's, or ``{"kind": "base"}`` for the baseline itself.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L767-L807)

## scope_profiler.report_model.summary_model

*function*

```python
def summary_model(results, rows, entries, base, balance) -> dict
```

Headline numbers and a few sentences on what stands out in one run.

Returns ``{"empty": True}`` without timed regions; otherwise ``{"kpis": [...],
"findings": [...]}``, where each finding is ``{"tone": "info" | "warn",
"parts": [...]}``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L79-L222)

## scope_profiler.report_model.wall_model

*function*

```python
def wall_model(base_run, run) -> dict
```

Wall time of ``run`` against ``base_run``: both, the change and its tone.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/report_model.py#L810-L827)
