compression-safeguards
  • Home
  • Try the safeguards

API Reference

  • Overview
  • compression_safeguards
    • api
    • safeguards
      • abc
      • combinators
        • all
        • any
        • assume_safe
        • select
      • eb
      • pointwise
        • abc
        • eb
        • lossless
        • qoi
          • eb
        • same
        • sign
      • qois
      • stencil
        • abc
        • qoi
          • eb
    • utils
      • bindings
      • cast
      • error
      • intervals
      • typing
  • numcodecs_safeguards
    • checksum
    • compute
    • lossless
  • xarray_safeguards

Examples

  • Datasets
  • Special values
    • NaN values
    • Missing values
  • Regions of Interest (RoIs)
    • Latent Heat Flux
  • Quantities of Interest (QoIs)
    • Pointwise: Log-scale Specific Humidity
    • Semi-Pointwise: Kinetic energy
    • Higher-order derivatives
    • Neighbourhood: Relative Vorticity
    • Bounding the dSSIM metric
  • Topology
    • Precipitation extrema
    • Isosurfaces
  • Error bound distributions
    • Absolute error bound
  • Safeguards on chunked data
    • xarray-safeguards
  • Preview
    • Incremental safeguards

Links

  • GitHub
  • PyPI
    • compression-safeguards
    • numcodecs-safeguards
    • xarray-safeguards
compression-safeguards
  • Summary
  • Try     View Source

Copied!
import json
import os
import re
from io import StringIO
from pathlib import Path

import matplotlib as mpl
import numpy as np
import observe
import pandas as pd
from matplotlib import offsetbox as mof
from matplotlib import patheffects as mpe
from matplotlib import pyplot as plt
from matplotlib.gridspec import GridSpec
import json import os import re from io import StringIO from pathlib import Path import matplotlib as mpl import numpy as np import observe import pandas as pd from matplotlib import offsetbox as mof from matplotlib import patheffects as mpe from matplotlib import pyplot as plt from matplotlib.gridspec import GridSpec
Copied!
colors = {
    "zfp.rs": mpl.cm.tab10.colors[0],
    "sz3.rs": mpl.cm.tab10.colors[6],
    "sperr.rs": mpl.cm.tab10.colors[2],
}
colors = { "zfp.rs": mpl.cm.tab10.colors[0], "sz3.rs": mpl.cm.tab10.colors[6], "sperr.rs": mpl.cm.tab10.colors[2], }
Copied!
fig = plt.figure(layout="tight", figsize=(12, 6.5))
gs = GridSpec(3, 4, figure=fig, height_ratios=[1, 1, 0.0])

for i, (name, title) in enumerate(
    {
        "nan.json": "Missing NaN Values",
        "missing.json": "Missing Non-NaN Values",
        "specific-humidity-log10.json": r"Pointwise $\log_{10}$",
        "kinetic-energy.json": "Kinetic Energy",
        "derivative-radial.json": r"Laplacian ($\Delta x$=const)",
        "derivative-log-exp.json": "Logarithm of Laplacian",
        "vorticity.json": "Relative Vorticity",
        "dssim.json": "dSSIM (3x3)",
    }.items()
):
    with Path("observations").joinpath(name).open() as f:
        observations = json.load(f)

    scatter_colors = []
    is_iteratives = []
    is_losslesss = []
    codec_encode_timings = []
    codec_decode_timings = []
    corrections_compute_timings = []
    corrections_encode_timings = []

    raw_codec_encode_timings = {c: [] for c in colors.keys()}

    zstd_encode_timing = None
    optzconfig_encode_timing = {c: None for c in colors.keys()}
    qpet_sperr_encode_timing = None

    for result in observations:
        if result["codec"]["id"] == "zstd.rs":
            (zstd_encode_timing,) = result["encode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] == "pressio.rs":
            codec_id = result["codec"]["early_config"]["numcodecs.rs:id"].removeprefix(
                "e-"
            )
            if codec_id == "sz3.3.rs":  # skip when no corrections are needed
                continue
            if codec_id == "sz3.2.rs":
                codec_id = "sz3.rs"
            assert codec_id in ["zfp.rs", "sz3.rs", "sperr.rs"]
            optzconfig_encode_timing[codec_id] = result["encode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] == "qpet-sperr.rs":
            (qpet_sperr_encode_timing,) = result["encode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] != "safeguards":
            continue

        codec_id = result["codec"]["codec"]["id"]

        if codec_id == "combinators.stack":
            a, b = result["codec"]["codec"]["codecs"]
            assert a["id"] == "replace.filter", a["id"]
            codec_id = b["id"]
        elif codec_id == "mask.meta":
            codec_id = result["codec"]["codec"]["codec"]["id"]
        elif codec_id == "pw_ratio":
            codec_id = result["codec"]["codec"]["log_codec"]["id"]
        if codec_id in ["sz3.2.rs", "sz3.3.rs"]:
            codec_id = "sz3.rs"

        assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id

        if codec_id == "zero":
            continue

        is_iterative = result["codec"]["compute"]["unstable_iterative"]
        is_lossless = result["codec"]["compute"]["unstable_lossless_corrections"]

        assert result["codec"]["lossless"]["for_codec"] is None

        (safeguarded_encode_timing,) = result["encode_timing"][
            observe.json_hash(result["codec"])
        ]
        (codec_encode_encode_timing,) = result["encode_timing"][
            observe.json_hash(result["codec"]["codec"])
        ]
        codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][
            observe.json_hash(result["codec"]["codec"])
        ]
        # corrections lossless codec may be skipped if no corrections are needed
        (lossless_for_corrections_encode_timing,) = result["encode_timing"].get(
            observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None]
        )

        # skip when no corrections were needed
        # - missing with mask meta-codec
        # - log10 with pwe meta-codec
        # - NaN with SZ3[v3.3]
        # - radial Laplacian with the 3rd error bound
        if lossless_for_corrections_encode_timing is None:
            continue

        raw_codec_encode_timings[codec_id].append(codec_encode_encode_timing)

        scatter_colors.append(colors[codec_id])
        is_iteratives.append(is_iterative and not is_lossless)
        is_losslesss.append(is_iterative and is_lossless)
        codec_encode_timings.append(
            codec_encode_encode_timing / codec_encode_encode_timing
        )
        codec_decode_timings.append(
            codec_encode_decode_timing / codec_encode_encode_timing
        )
        corrections_compute_timings.append(
            lossless_for_corrections_encode_timing / codec_encode_encode_timing
        )
        corrections_encode_timings.append(
            (
                safeguarded_encode_timing
                - codec_encode_encode_timing
                - codec_encode_decode_timing
                - lossless_for_corrections_encode_timing
            )
            / codec_encode_encode_timing
        )

    is_losslesss = np.array(is_losslesss)
    is_iteratives = np.array(is_iteratives)

    ax = fig.add_subplot(gs[i % 2, i // 2])

    ax.set_title(title)

    rng = np.random.default_rng(seed=42)

    b1 = ax.boxplot(codec_encode_timings, notch=True, showfliers=False, positions=[0])
    b2 = ax.boxplot(
        np.array(codec_decode_timings) + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.2],
    )
    b3 = ax.boxplot(
        np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.4],
    )
    b4 = ax.boxplot(
        np.array(corrections_compute_timings)
        + np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.6],
    )

    total_timings = (
        np.array(corrections_compute_timings)
        + np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings)
    )

    extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10

    ax.set_xlim(-0.1, 0.7)
    ax.set_xticklabels(["CE", "+CD", "+Sg", "+CC"])
    ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra)
    ax.set_yticks(
        [
            1 if y == 0 else y
            for y in ax.get_yticks()
            if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0
        ]
    )
    ax.get_yticklabels()[-1].set_fontweight("bold")
    ax.text(
        0.68,
        b4["caps"][1].get_data()[1][0] + extra / 2,
        rf"max: $\times${np.round(np.amax(total_timings), 1)}",
        ha="right",
        va="bottom",
    )
    ax.text(
        0.6,
        b4["medians"][0].get_data()[1][0],
        f"{np.round(np.median(total_timings), 1)}",
        ha="center",
        va="bottom",
        color=b4["medians"][0].get_color(),
        path_effects=[mpe.withStroke(linewidth=2, foreground="white", alpha=0.5)],
    )

    cs = np.array(scatter_colors)

    xs = rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_encode_timings))
    ys = np.array(codec_encode_timings)
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.2, scale=0.1 / 5, size=len(codec_decode_timings))
    ys = np.array(codec_decode_timings) + np.array(codec_encode_timings)
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_encode_timings))
    ys = (
        np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings)
    )
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.6, scale=0.1 / 5, size=len(corrections_compute_timings))
    ys = (
        np.array(corrections_compute_timings)
        + np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings)
    )
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    extra_texts = []

    extra_texts.append(
        mof.TextArea(
            "ZSTD(22):",
            textprops=dict(ha="left", va="center", color="gray", fontsize="small"),
        )
    )
    for c in colors.keys():
        zstd_rel_encode_timings = zstd_encode_timing / np.array(
            raw_codec_encode_timings[c]
        )
        extra_texts.append(
            mof.TextArea(
                rf"$\times${np.round(np.mean(zstd_rel_encode_timings), 1)}$\pm${np.round(np.std(zstd_rel_encode_timings), 1)}",
                textprops=dict(
                    ha="left", va="center", color=colors[c], fontsize="small"
                ),
            )
        )

    extra_texts.append(
        mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4))
    )
    extra_texts.append(
        mof.TextArea(
            "OptZConfig:",
            textprops=dict(
                ha="left", va="center", color=mpl.cm.tab20b.colors[10], fontsize="small"
            ),
        )
    )
    for c in colors.keys():
        optzconfig_rel_encode_timings = optzconfig_encode_timing[c] / np.array(
            raw_codec_encode_timings[c]
        )
        extra_texts.append(
            mof.TextArea(
                rf"$\times${np.round(np.mean(optzconfig_rel_encode_timings), 1)}$\pm${np.round(np.std(optzconfig_rel_encode_timings), 1)}",
                textprops=dict(
                    ha="left", va="center", color=colors[c], fontsize="small"
                ),
            )
        )

    if qpet_sperr_encode_timing is not None:
        qpet_sperr_rel_encode_timings = qpet_sperr_encode_timing / np.array(
            raw_codec_encode_timings["sperr.rs"]
        )
        extra_texts.append(
            mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4))
        )
        extra_texts.append(
            mof.TextArea(
                "QPET-SPERR:",
                textprops=dict(
                    ha="left",
                    va="center",
                    color=mpl.cm.tab20b.colors[4],
                    fontsize="small",
                ),
            )
        )
        extra_texts.append(
            mof.TextArea(
                rf"$\times${np.round(np.mean(qpet_sperr_rel_encode_timings), 1)}$\pm${np.round(np.std(qpet_sperr_rel_encode_timings), 1)}",
                textprops=dict(
                    ha="left", va="center", color=colors["sperr.rs"], fontsize="small"
                ),
            )
        )

    extra_text = mof.VPacker(
        children=extra_texts,
        sep=2,
    )
    extra_annot = mof.AnnotationBbox(
        extra_text,
        xy=(
            -0.08,
            b4["caps"][1].get_data()[1][0] + extra * 1.35,
        ),
        box_alignment=(0, 1),
        pad=0,
        frameon=False,
    )
    ax.add_artist(extra_annot)

fig.supylabel("relative compression time")

ax = fig.add_subplot(gs[2, :])
ax.axis("off")
l1 = ax.legend(
    handles=[
        mpl.lines.Line2D(
            [0],
            [0],
            marker="o",
            color="none",
            markerfacecolor=col,
            markeredgecolor=col,
            label={
                "zfp.rs": "ZFP",
                "sz3.rs": "SZ3",
                "sperr.rs": "SPERR",
            }[c],
        )
        for c, col in colors.items()
    ],
    ncol=3,
    loc="center left",
    title="Compressor",
)
ax.legend(
    handles=[
        mpl.lines.Line2D(
            [0],
            [0],
            marker="D",
            color="none",
            markerfacecolor=colors["zfp.rs"],
            markeredgecolor=colors["zfp.rs"],
            label="lossless",
        ),
        mpl.lines.Line2D(
            [0],
            [0],
            marker="o",
            color="none",
            markerfacecolor=colors["zfp.rs"],
            markeredgecolor=colors["zfp.rs"],
            label="one-shot",
        ),
        mpl.lines.Line2D(
            [0],
            [0],
            marker="*",
            color="none",
            markerfacecolor=colors["zfp.rs"],
            markeredgecolor=colors["zfp.rs"],
            label="iterative",
        ),
    ],
    ncol=3,
    loc="center right",
    title="Safeguards Corrections",
)
ax.add_artist(l1)

plt.tight_layout()

plt.savefig(Path("plots") / "summary-overhead-compression.pdf", dpi=300)

plt.show()
fig = plt.figure(layout="tight", figsize=(12, 6.5)) gs = GridSpec(3, 4, figure=fig, height_ratios=[1, 1, 0.0]) for i, (name, title) in enumerate( { "nan.json": "Missing NaN Values", "missing.json": "Missing Non-NaN Values", "specific-humidity-log10.json": r"Pointwise $\log_{10}$", "kinetic-energy.json": "Kinetic Energy", "derivative-radial.json": r"Laplacian ($\Delta x$=const)", "derivative-log-exp.json": "Logarithm of Laplacian", "vorticity.json": "Relative Vorticity", "dssim.json": "dSSIM (3x3)", }.items() ): with Path("observations").joinpath(name).open() as f: observations = json.load(f) scatter_colors = [] is_iteratives = [] is_losslesss = [] codec_encode_timings = [] codec_decode_timings = [] corrections_compute_timings = [] corrections_encode_timings = [] raw_codec_encode_timings = {c: [] for c in colors.keys()} zstd_encode_timing = None optzconfig_encode_timing = {c: None for c in colors.keys()} qpet_sperr_encode_timing = None for result in observations: if result["codec"]["id"] == "zstd.rs": (zstd_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] == "pressio.rs": codec_id = result["codec"]["early_config"]["numcodecs.rs:id"].removeprefix( "e-" ) if codec_id == "sz3.3.rs": # skip when no corrections are needed continue if codec_id == "sz3.2.rs": codec_id = "sz3.rs" assert codec_id in ["zfp.rs", "sz3.rs", "sperr.rs"] optzconfig_encode_timing[codec_id] = result["encode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] == "qpet-sperr.rs": (qpet_sperr_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] != "safeguards": continue codec_id = result["codec"]["codec"]["id"] if codec_id == "combinators.stack": a, b = result["codec"]["codec"]["codecs"] assert a["id"] == "replace.filter", a["id"] codec_id = b["id"] elif codec_id == "mask.meta": codec_id = result["codec"]["codec"]["codec"]["id"] elif codec_id == "pw_ratio": codec_id = result["codec"]["codec"]["log_codec"]["id"] if codec_id in ["sz3.2.rs", "sz3.3.rs"]: codec_id = "sz3.rs" assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id if codec_id == "zero": continue is_iterative = result["codec"]["compute"]["unstable_iterative"] is_lossless = result["codec"]["compute"]["unstable_lossless_corrections"] assert result["codec"]["lossless"]["for_codec"] is None (safeguarded_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]) ] (codec_encode_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]["codec"]) ] codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][ observe.json_hash(result["codec"]["codec"]) ] # corrections lossless codec may be skipped if no corrections are needed (lossless_for_corrections_encode_timing,) = result["encode_timing"].get( observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None] ) # skip when no corrections were needed # - missing with mask meta-codec # - log10 with pwe meta-codec # - NaN with SZ3[v3.3] # - radial Laplacian with the 3rd error bound if lossless_for_corrections_encode_timing is None: continue raw_codec_encode_timings[codec_id].append(codec_encode_encode_timing) scatter_colors.append(colors[codec_id]) is_iteratives.append(is_iterative and not is_lossless) is_losslesss.append(is_iterative and is_lossless) codec_encode_timings.append( codec_encode_encode_timing / codec_encode_encode_timing ) codec_decode_timings.append( codec_encode_decode_timing / codec_encode_encode_timing ) corrections_compute_timings.append( lossless_for_corrections_encode_timing / codec_encode_encode_timing ) corrections_encode_timings.append( ( safeguarded_encode_timing - codec_encode_encode_timing - codec_encode_decode_timing - lossless_for_corrections_encode_timing ) / codec_encode_encode_timing ) is_losslesss = np.array(is_losslesss) is_iteratives = np.array(is_iteratives) ax = fig.add_subplot(gs[i % 2, i // 2]) ax.set_title(title) rng = np.random.default_rng(seed=42) b1 = ax.boxplot(codec_encode_timings, notch=True, showfliers=False, positions=[0]) b2 = ax.boxplot( np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.2], ) b3 = ax.boxplot( np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.4], ) b4 = ax.boxplot( np.array(corrections_compute_timings) + np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.6], ) total_timings = ( np.array(corrections_compute_timings) + np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings) ) extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10 ax.set_xlim(-0.1, 0.7) ax.set_xticklabels(["CE", "+CD", "+Sg", "+CC"]) ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra) ax.set_yticks( [ 1 if y == 0 else y for y in ax.get_yticks() if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0 ] ) ax.get_yticklabels()[-1].set_fontweight("bold") ax.text( 0.68, b4["caps"][1].get_data()[1][0] + extra / 2, rf"max: $\times${np.round(np.amax(total_timings), 1)}", ha="right", va="bottom", ) ax.text( 0.6, b4["medians"][0].get_data()[1][0], f"{np.round(np.median(total_timings), 1)}", ha="center", va="bottom", color=b4["medians"][0].get_color(), path_effects=[mpe.withStroke(linewidth=2, foreground="white", alpha=0.5)], ) cs = np.array(scatter_colors) xs = rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_encode_timings)) ys = np.array(codec_encode_timings) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.2, scale=0.1 / 5, size=len(codec_decode_timings)) ys = np.array(codec_decode_timings) + np.array(codec_encode_timings) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_encode_timings)) ys = ( np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings) ) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.6, scale=0.1 / 5, size=len(corrections_compute_timings)) ys = ( np.array(corrections_compute_timings) + np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings) ) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) extra_texts = [] extra_texts.append( mof.TextArea( "ZSTD(22):", textprops=dict(ha="left", va="center", color="gray", fontsize="small"), ) ) for c in colors.keys(): zstd_rel_encode_timings = zstd_encode_timing / np.array( raw_codec_encode_timings[c] ) extra_texts.append( mof.TextArea( rf"$\times${np.round(np.mean(zstd_rel_encode_timings), 1)}$\pm${np.round(np.std(zstd_rel_encode_timings), 1)}", textprops=dict( ha="left", va="center", color=colors[c], fontsize="small" ), ) ) extra_texts.append( mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)) ) extra_texts.append( mof.TextArea( "OptZConfig:", textprops=dict( ha="left", va="center", color=mpl.cm.tab20b.colors[10], fontsize="small" ), ) ) for c in colors.keys(): optzconfig_rel_encode_timings = optzconfig_encode_timing[c] / np.array( raw_codec_encode_timings[c] ) extra_texts.append( mof.TextArea( rf"$\times${np.round(np.mean(optzconfig_rel_encode_timings), 1)}$\pm${np.round(np.std(optzconfig_rel_encode_timings), 1)}", textprops=dict( ha="left", va="center", color=colors[c], fontsize="small" ), ) ) if qpet_sperr_encode_timing is not None: qpet_sperr_rel_encode_timings = qpet_sperr_encode_timing / np.array( raw_codec_encode_timings["sperr.rs"] ) extra_texts.append( mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)) ) extra_texts.append( mof.TextArea( "QPET-SPERR:", textprops=dict( ha="left", va="center", color=mpl.cm.tab20b.colors[4], fontsize="small", ), ) ) extra_texts.append( mof.TextArea( rf"$\times${np.round(np.mean(qpet_sperr_rel_encode_timings), 1)}$\pm${np.round(np.std(qpet_sperr_rel_encode_timings), 1)}", textprops=dict( ha="left", va="center", color=colors["sperr.rs"], fontsize="small" ), ) ) extra_text = mof.VPacker( children=extra_texts, sep=2, ) extra_annot = mof.AnnotationBbox( extra_text, xy=( -0.08, b4["caps"][1].get_data()[1][0] + extra * 1.35, ), box_alignment=(0, 1), pad=0, frameon=False, ) ax.add_artist(extra_annot) fig.supylabel("relative compression time") ax = fig.add_subplot(gs[2, :]) ax.axis("off") l1 = ax.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=col, markeredgecolor=col, label={ "zfp.rs": "ZFP", "sz3.rs": "SZ3", "sperr.rs": "SPERR", }[c], ) for c, col in colors.items() ], ncol=3, loc="center left", title="Compressor", ) ax.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="D", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="lossless", ), mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="one-shot", ), mpl.lines.Line2D( [0], [0], marker="*", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="iterative", ), ], ncol=3, loc="center right", title="Safeguards Corrections", ) ax.add_artist(l1) plt.tight_layout() plt.savefig(Path("plots") / "summary-overhead-compression.pdf", dpi=300) plt.show()
No description has been provided for this image
Copied!
fig, (ax, ax2, ax3) = plt.subplots(
    1, 3, figsize=(7, 4), width_ratios=[19.5, 1.5, 1.5], gridspec_kw=dict(wspace=0.2)
)

rng = np.random.default_rng(seed=42)

scatter_colors = []
codec_decode_timings = []
corrections_decode_timings = []
corrections_apply_timings = []
zstd_rel_decode_timings = {c: [] for c in colors.keys()}
optzconfig_rel_decode_timings = {c: [] for c in colors.keys()}
qpet_sperr_rel_decode_timings = []

for name in [
    "derivative-log-exp.json",
    "derivative-radial.json",
    "dssim.json",
    "kinetic-energy.json",
    "missing.json",
    "nan.json",
    "specific-humidity-log10.json",
    "vorticity.json",
]:
    with Path("observations").joinpath(name).open() as f:
        observations = json.load(f)

    raw_codec_decode_timings = {c: [] for c in colors.keys()}

    zstd_decode_timing = None
    optzconfig_decode_timing = {c: None for c in colors.keys()}
    qpet_sperr_decode_timing = None

    for result in observations:
        if result["codec"]["id"] == "zstd.rs":
            (zstd_decode_timing,) = result["decode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] == "pressio.rs":
            codec_id = result["codec"]["early_config"]["numcodecs.rs:id"].removeprefix(
                "e-"
            )
            if codec_id == "sz3.3.rs":  # skip when no corrections are needed
                continue
            if codec_id == "sz3.2.rs":
                codec_id = "sz3.rs"
            assert codec_id in ["zfp.rs", "sz3.rs", "sperr.rs"]
            optzconfig_decode_timing[codec_id] = result["decode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] == "qpet-sperr.rs":
            (qpet_sperr_decode_timing,) = result["decode_timing"][
                observe.json_hash(result["codec"])
            ]

        if result["codec"]["id"] != "safeguards":
            continue

        codec_id = result["codec"]["codec"]["id"]

        if codec_id == "combinators.stack":
            a, b = result["codec"]["codec"]["codecs"]
            assert a["id"] == "replace.filter", a["id"]
            codec_id = b["id"]
        elif codec_id == "mask.meta":
            codec_id = result["codec"]["codec"]["codec"]["id"]
        elif codec_id == "pw_ratio":
            codec_id = result["codec"]["codec"]["log_codec"]["id"]
        if codec_id in ["sz3.2.rs", "sz3.3.rs"]:
            codec_id = "sz3.rs"

        assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id

        if codec_id == "zero":
            continue

        assert result["codec"]["lossless"]["for_codec"] is None

        codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][
            observe.json_hash(result["codec"]["codec"])
        ]
        (safeguarded_decode_timing,) = result["decode_timing"][
            observe.json_hash(result["codec"])
        ]
        # corrections lossless codec may be skipped if no corrections are needed
        (lossless_for_corrections_decode_timing,) = result["decode_timing"].get(
            observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None]
        )

        # skip when no corrections were needed
        # - missing with mask meta-codec
        # - log10 with pwe meta-codec
        # - NaN with SZ3[v3.3]
        # - radial Laplacian with the 3rd error bound
        if lossless_for_corrections_decode_timing is None:
            continue

        raw_codec_decode_timings[codec_id].append(codec_decode_timing)

        scatter_colors.append(colors[codec_id])
        codec_decode_timings.append(codec_decode_timing / codec_decode_timing)
        corrections_decode_timings.append(
            lossless_for_corrections_decode_timing / codec_decode_timing
        )
        corrections_apply_timings.append(
            (
                safeguarded_decode_timing
                - codec_decode_timing
                - lossless_for_corrections_decode_timing
            )
            / codec_decode_timing
        )

    for c in colors.keys():
        zstd_rel_decode_timings[c].extend(
            zstd_decode_timing / np.array(raw_codec_decode_timings[c])
        )
        optzconfig_rel_decode_timings[c].extend(
            optzconfig_decode_timing[c] / np.array(raw_codec_decode_timings[c])
        )
    if qpet_sperr_decode_timing is not None:
        qpet_sperr_rel_decode_timings.extend(
            qpet_sperr_decode_timing / np.array(raw_codec_decode_timings["sperr.rs"])
        )

b1 = ax.boxplot(codec_decode_timings, notch=False, showfliers=False, positions=[0])
b2 = ax.boxplot(
    np.array(corrections_decode_timings) + np.array(codec_decode_timings),
    notch=False,
    showfliers=False,
    positions=[0.2],
)
b3 = ax.boxplot(
    np.array(corrections_apply_timings)
    + np.array(corrections_decode_timings)
    + np.array(codec_decode_timings),
    notch=False,
    showfliers=False,
    positions=[0.4],
)

total_timings = (
    np.array(corrections_apply_timings)
    + np.array(corrections_decode_timings)
    + np.array(codec_decode_timings)
)

ax.text(
    0.4,
    b3["caps"][1].get_data()[1][0] + 0.005,
    rf"max: $\times${np.amax(total_timings):0.3}",
    ha="center",
    va="bottom",
    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
)
ax.text(
    0.4,
    b3["medians"][0].get_data()[1][0],
    rf"$\times${np.median(total_timings):0.3}",
    ha="center",
    va="bottom",
    color=b3["medians"][0].get_color(),
    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
)

ax.set_xlim(-0.1, 0.5)
ax.set_xticks([0, 0.2, 0.4])
ax.set_xticklabels(["decompress codec", "+ corrections", "+ apply corrections"])
ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + 0.01)
ax.set_ylabel("relative decompression time")

b4 = ax2.boxplot(
    sum((list(t) for t in zstd_rel_decode_timings.values()), start=[]),
    notch=False,
    showfliers=False,
    positions=[0.0],
)
ax2.set_xlim(-0.15, 0.15)
ax2.set_xticks([0])
ax2.set_xticklabels(["ZSTD"])

b5 = ax3.boxplot(
    sum((list(t) for t in optzconfig_rel_decode_timings.values()), start=[]),
    notch=False,
    showfliers=False,
    positions=[0.0],
)
ax3.set_xlim(-0.15, 0.15)
ax3.set_xticks([0])
ax3.set_xticklabels(["OptZConfig"])
ax3.yaxis.tick_right()

ax.scatter(
    rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_decode_timings)),
    codec_decode_timings,
    marker=".",
    c=scatter_colors,
)
ax.scatter(
    rng.normal(loc=0.2, scale=0.1 / 5, size=len(corrections_decode_timings)),
    np.array(corrections_decode_timings) + np.array(codec_decode_timings),
    marker=".",
    c=scatter_colors,
)
ax.scatter(
    rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_apply_timings)),
    np.array(corrections_apply_timings)
    + np.array(corrections_decode_timings)
    + np.array(codec_decode_timings),
    marker=".",
    c=scatter_colors,
)
ax2.scatter(
    rng.normal(
        loc=0.0,
        scale=0.1 / 5,
        size=sum(len(z) for z in zstd_rel_decode_timings.values()),
    ),
    sum((list(z) for z in zstd_rel_decode_timings.values()), start=[]),
    marker=".",
    c=[colors[c] for c in colors.keys() for _ in zstd_rel_decode_timings[c]],
    s=4,
)
ax3.scatter(
    rng.normal(
        loc=0.0,
        scale=0.1 / 5,
        size=sum(len(o) for o in optzconfig_rel_decode_timings.values()),
    ),
    sum((list(o) for o in optzconfig_rel_decode_timings.values()), start=[]),
    marker=".",
    c=[colors[c] for c in colors.keys() for _ in optzconfig_rel_decode_timings[c]],
    s=4,
)
ax3.set_ylim(*ax2.get_ylim())

zstd_text = mof.HPacker(
    children=[
        mof.TextArea(
            "ZSTD(22):",
            textprops=dict(
                ha="left",
                va="center",
                color="gray",
                fontsize="small",
                path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
            ),
        ),
    ]
    + [
        t
        for c in zstd_rel_decode_timings.keys()
        for t in [
            mof.TextArea(
                rf"$\times${np.round(np.mean(zstd_rel_decode_timings[c]), 1)}$\pm${np.round(np.std(zstd_rel_decode_timings[c]), 1)}",
                textprops=dict(
                    ha="left",
                    va="center",
                    color=colors[c],
                    fontsize="small",
                    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
                ),
            ),
            mof.TextArea(
                "/",
                textprops=dict(
                    ha="left",
                    va="center",
                    color="gray",
                    fontsize="small",
                    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
                ),
            ),
        ]
    ][:-1]
)
optzconfig_text = mof.HPacker(
    children=[
        mof.TextArea(
            "OptZConfig:",
            textprops=dict(
                ha="left",
                va="center",
                color=mpl.cm.tab20b.colors[10],
                fontsize="small",
                path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
            ),
        ),
    ]
    + [
        t
        for c in optzconfig_rel_decode_timings.keys()
        for t in [
            mof.TextArea(
                rf"$\times${np.round(np.mean(optzconfig_rel_decode_timings[c]), 1)}$\pm${np.round(np.std(optzconfig_rel_decode_timings[c]), 1)}",
                textprops=dict(
                    ha="left",
                    va="center",
                    color=colors[c],
                    fontsize="small",
                    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
                ),
            ),
            mof.TextArea(
                "/",
                textprops=dict(
                    ha="left",
                    va="center",
                    color=mpl.cm.tab20b.colors[10],
                    fontsize="small",
                    path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
                ),
            ),
        ]
    ][:-1]
)
qpet_sperr_text = mof.HPacker(
    children=[
        mof.TextArea(
            "QPET-SPERR:",
            textprops=dict(
                ha="left",
                va="center",
                color=mpl.cm.tab20b.colors[4],
                fontsize="small",
                path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
            ),
        ),
        mof.TextArea(
            rf"$\times${np.round(np.mean(qpet_sperr_rel_decode_timings), 1)}$\pm${np.round(np.std(qpet_sperr_rel_decode_timings), 1)}",
            textprops=dict(
                ha="left",
                va="center",
                color=colors["sperr.rs"],
                fontsize="small",
                path_effects=[mpe.withStroke(linewidth=3, foreground="white")],
            ),
        ),
    ]
)

extra_texts = mof.VPacker(
    children=[
        zstd_text,
        mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)),
        optzconfig_text,
        mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)),
        qpet_sperr_text,
    ],
    sep=2,
)

extra_annot = mof.AnnotationBbox(
    extra_texts,
    xy=(
        -0.08,
        b3["caps"][1].get_data()[1][0] + 0.011,
    ),
    box_alignment=(0, 1),
    pad=0,
    frameon=False,
)
ax.add_artist(extra_annot)

ax.legend(
    handles=[
        mpl.lines.Line2D(
            [0],
            [0],
            marker="o",
            color="none",
            markerfacecolor=col,
            markeredgecolor=col,
            label={
                "zfp.rs": "ZFP",
                "sz3.rs": "SZ3",
                "sperr.rs": "SPERR",
            }[c],
        )
        for c, col in colors.items()
    ],
    loc="center left",
    title="Compressor",
)

# plt.tight_layout()

plt.savefig(
    Path("plots") / "summary-overhead-decompression.pdf", dpi=300, bbox_inches="tight"
)

plt.show()
fig, (ax, ax2, ax3) = plt.subplots( 1, 3, figsize=(7, 4), width_ratios=[19.5, 1.5, 1.5], gridspec_kw=dict(wspace=0.2) ) rng = np.random.default_rng(seed=42) scatter_colors = [] codec_decode_timings = [] corrections_decode_timings = [] corrections_apply_timings = [] zstd_rel_decode_timings = {c: [] for c in colors.keys()} optzconfig_rel_decode_timings = {c: [] for c in colors.keys()} qpet_sperr_rel_decode_timings = [] for name in [ "derivative-log-exp.json", "derivative-radial.json", "dssim.json", "kinetic-energy.json", "missing.json", "nan.json", "specific-humidity-log10.json", "vorticity.json", ]: with Path("observations").joinpath(name).open() as f: observations = json.load(f) raw_codec_decode_timings = {c: [] for c in colors.keys()} zstd_decode_timing = None optzconfig_decode_timing = {c: None for c in colors.keys()} qpet_sperr_decode_timing = None for result in observations: if result["codec"]["id"] == "zstd.rs": (zstd_decode_timing,) = result["decode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] == "pressio.rs": codec_id = result["codec"]["early_config"]["numcodecs.rs:id"].removeprefix( "e-" ) if codec_id == "sz3.3.rs": # skip when no corrections are needed continue if codec_id == "sz3.2.rs": codec_id = "sz3.rs" assert codec_id in ["zfp.rs", "sz3.rs", "sperr.rs"] optzconfig_decode_timing[codec_id] = result["decode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] == "qpet-sperr.rs": (qpet_sperr_decode_timing,) = result["decode_timing"][ observe.json_hash(result["codec"]) ] if result["codec"]["id"] != "safeguards": continue codec_id = result["codec"]["codec"]["id"] if codec_id == "combinators.stack": a, b = result["codec"]["codec"]["codecs"] assert a["id"] == "replace.filter", a["id"] codec_id = b["id"] elif codec_id == "mask.meta": codec_id = result["codec"]["codec"]["codec"]["id"] elif codec_id == "pw_ratio": codec_id = result["codec"]["codec"]["log_codec"]["id"] if codec_id in ["sz3.2.rs", "sz3.3.rs"]: codec_id = "sz3.rs" assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id if codec_id == "zero": continue assert result["codec"]["lossless"]["for_codec"] is None codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][ observe.json_hash(result["codec"]["codec"]) ] (safeguarded_decode_timing,) = result["decode_timing"][ observe.json_hash(result["codec"]) ] # corrections lossless codec may be skipped if no corrections are needed (lossless_for_corrections_decode_timing,) = result["decode_timing"].get( observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None] ) # skip when no corrections were needed # - missing with mask meta-codec # - log10 with pwe meta-codec # - NaN with SZ3[v3.3] # - radial Laplacian with the 3rd error bound if lossless_for_corrections_decode_timing is None: continue raw_codec_decode_timings[codec_id].append(codec_decode_timing) scatter_colors.append(colors[codec_id]) codec_decode_timings.append(codec_decode_timing / codec_decode_timing) corrections_decode_timings.append( lossless_for_corrections_decode_timing / codec_decode_timing ) corrections_apply_timings.append( ( safeguarded_decode_timing - codec_decode_timing - lossless_for_corrections_decode_timing ) / codec_decode_timing ) for c in colors.keys(): zstd_rel_decode_timings[c].extend( zstd_decode_timing / np.array(raw_codec_decode_timings[c]) ) optzconfig_rel_decode_timings[c].extend( optzconfig_decode_timing[c] / np.array(raw_codec_decode_timings[c]) ) if qpet_sperr_decode_timing is not None: qpet_sperr_rel_decode_timings.extend( qpet_sperr_decode_timing / np.array(raw_codec_decode_timings["sperr.rs"]) ) b1 = ax.boxplot(codec_decode_timings, notch=False, showfliers=False, positions=[0]) b2 = ax.boxplot( np.array(corrections_decode_timings) + np.array(codec_decode_timings), notch=False, showfliers=False, positions=[0.2], ) b3 = ax.boxplot( np.array(corrections_apply_timings) + np.array(corrections_decode_timings) + np.array(codec_decode_timings), notch=False, showfliers=False, positions=[0.4], ) total_timings = ( np.array(corrections_apply_timings) + np.array(corrections_decode_timings) + np.array(codec_decode_timings) ) ax.text( 0.4, b3["caps"][1].get_data()[1][0] + 0.005, rf"max: $\times${np.amax(total_timings):0.3}", ha="center", va="bottom", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ) ax.text( 0.4, b3["medians"][0].get_data()[1][0], rf"$\times${np.median(total_timings):0.3}", ha="center", va="bottom", color=b3["medians"][0].get_color(), path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ) ax.set_xlim(-0.1, 0.5) ax.set_xticks([0, 0.2, 0.4]) ax.set_xticklabels(["decompress codec", "+ corrections", "+ apply corrections"]) ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + 0.01) ax.set_ylabel("relative decompression time") b4 = ax2.boxplot( sum((list(t) for t in zstd_rel_decode_timings.values()), start=[]), notch=False, showfliers=False, positions=[0.0], ) ax2.set_xlim(-0.15, 0.15) ax2.set_xticks([0]) ax2.set_xticklabels(["ZSTD"]) b5 = ax3.boxplot( sum((list(t) for t in optzconfig_rel_decode_timings.values()), start=[]), notch=False, showfliers=False, positions=[0.0], ) ax3.set_xlim(-0.15, 0.15) ax3.set_xticks([0]) ax3.set_xticklabels(["OptZConfig"]) ax3.yaxis.tick_right() ax.scatter( rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_decode_timings)), codec_decode_timings, marker=".", c=scatter_colors, ) ax.scatter( rng.normal(loc=0.2, scale=0.1 / 5, size=len(corrections_decode_timings)), np.array(corrections_decode_timings) + np.array(codec_decode_timings), marker=".", c=scatter_colors, ) ax.scatter( rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_apply_timings)), np.array(corrections_apply_timings) + np.array(corrections_decode_timings) + np.array(codec_decode_timings), marker=".", c=scatter_colors, ) ax2.scatter( rng.normal( loc=0.0, scale=0.1 / 5, size=sum(len(z) for z in zstd_rel_decode_timings.values()), ), sum((list(z) for z in zstd_rel_decode_timings.values()), start=[]), marker=".", c=[colors[c] for c in colors.keys() for _ in zstd_rel_decode_timings[c]], s=4, ) ax3.scatter( rng.normal( loc=0.0, scale=0.1 / 5, size=sum(len(o) for o in optzconfig_rel_decode_timings.values()), ), sum((list(o) for o in optzconfig_rel_decode_timings.values()), start=[]), marker=".", c=[colors[c] for c in colors.keys() for _ in optzconfig_rel_decode_timings[c]], s=4, ) ax3.set_ylim(*ax2.get_ylim()) zstd_text = mof.HPacker( children=[ mof.TextArea( "ZSTD(22):", textprops=dict( ha="left", va="center", color="gray", fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), ] + [ t for c in zstd_rel_decode_timings.keys() for t in [ mof.TextArea( rf"$\times${np.round(np.mean(zstd_rel_decode_timings[c]), 1)}$\pm${np.round(np.std(zstd_rel_decode_timings[c]), 1)}", textprops=dict( ha="left", va="center", color=colors[c], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), mof.TextArea( "/", textprops=dict( ha="left", va="center", color="gray", fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), ] ][:-1] ) optzconfig_text = mof.HPacker( children=[ mof.TextArea( "OptZConfig:", textprops=dict( ha="left", va="center", color=mpl.cm.tab20b.colors[10], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), ] + [ t for c in optzconfig_rel_decode_timings.keys() for t in [ mof.TextArea( rf"$\times${np.round(np.mean(optzconfig_rel_decode_timings[c]), 1)}$\pm${np.round(np.std(optzconfig_rel_decode_timings[c]), 1)}", textprops=dict( ha="left", va="center", color=colors[c], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), mof.TextArea( "/", textprops=dict( ha="left", va="center", color=mpl.cm.tab20b.colors[10], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), ] ][:-1] ) qpet_sperr_text = mof.HPacker( children=[ mof.TextArea( "QPET-SPERR:", textprops=dict( ha="left", va="center", color=mpl.cm.tab20b.colors[4], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), mof.TextArea( rf"$\times${np.round(np.mean(qpet_sperr_rel_decode_timings), 1)}$\pm${np.round(np.std(qpet_sperr_rel_decode_timings), 1)}", textprops=dict( ha="left", va="center", color=colors["sperr.rs"], fontsize="small", path_effects=[mpe.withStroke(linewidth=3, foreground="white")], ), ), ] ) extra_texts = mof.VPacker( children=[ zstd_text, mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)), optzconfig_text, mof.TextArea("", textprops=dict(ha="left", va="center", fontsize=4)), qpet_sperr_text, ], sep=2, ) extra_annot = mof.AnnotationBbox( extra_texts, xy=( -0.08, b3["caps"][1].get_data()[1][0] + 0.011, ), box_alignment=(0, 1), pad=0, frameon=False, ) ax.add_artist(extra_annot) ax.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=col, markeredgecolor=col, label={ "zfp.rs": "ZFP", "sz3.rs": "SZ3", "sperr.rs": "SPERR", }[c], ) for c, col in colors.items() ], loc="center left", title="Compressor", ) # plt.tight_layout() plt.savefig( Path("plots") / "summary-overhead-decompression.pdf", dpi=300, bbox_inches="tight" ) plt.show()
No description has been provided for this image
Copied!
MULTIROW_PATTERN = re.compile(r"^\\multirow\[[^\]]+\]\{(\d+)\}\{\*\}\{(.*)\}$")


def parse_latex_table(filepath: os.PathLike) -> pd.DataFrame:
    with open(filepath) as f:
        s = "".join(
            line.replace(r"\%", r"%").replace("\\\\\n", "\n")
            for line in f
            if not any(
                x in line
                for x in [
                    r"\begin{tabular}",
                    r"\toprule",
                    r"\midrule",
                    r"\cline",
                    r"\bottomrule",
                    r"\end{tabular}",
                ]
            )
        )
    pre = pd.read_table(StringIO(s), sep=r"\s*&\s*", engine="python", header=None)
    header = pre.loc[0].combine_first(pre.loc[1])
    index = list(pre.loc[1][pre.loc[1].notnull()])
    mid = pre.loc[2:].reset_index(drop=True).rename(columns=header)
    for c in index:
        i = 0
        while i < len(mid[c]):
            m = MULTIROW_PATTERN.match(str(mid[c].iloc[i]))
            if m is None:
                i += 1
            else:
                skip = int(m.group(1))
                mid.loc[i : i + skip - 1, c] = m.group(2)
                i += skip
    return mid.fillna("").set_index(index)
MULTIROW_PATTERN = re.compile(r"^\\multirow\[[^\]]+\]\{(\d+)\}\{\*\}\{(.*)\}$") def parse_latex_table(filepath: os.PathLike) -> pd.DataFrame: with open(filepath) as f: s = "".join( line.replace(r"\%", r"%").replace("\\\\\n", "\n") for line in f if not any( x in line for x in [ r"\begin{tabular}", r"\toprule", r"\midrule", r"\cline", r"\bottomrule", r"\end{tabular}", ] ) ) pre = pd.read_table(StringIO(s), sep=r"\s*&\s*", engine="python", header=None) header = pre.loc[0].combine_first(pre.loc[1]) index = list(pre.loc[1][pre.loc[1].notnull()]) mid = pre.loc[2:].reset_index(drop=True).rename(columns=header) for c in index: i = 0 while i < len(mid[c]): m = MULTIROW_PATTERN.match(str(mid[c].iloc[i])) if m is None: i += 1 else: skip = int(m.group(1)) mid.loc[i : i + skip - 1, c] = m.group(2) i += skip return mid.fillna("").set_index(index)
Copied!
colors_with_zero = {
    "0": mpl.cm.tab10.colors[8],
    r"ZFP($\epsilon_{abs}$)": mpl.cm.tab10.colors[0],
    r"ZFP": mpl.cm.tab10.colors[0],
    r"SZ3($\epsilon_{abs}$)": mpl.cm.tab10.colors[6],
    r"SZ3[v3.2]($\epsilon_{abs}$)": mpl.cm.tab10.colors[6],
    r"SZ3": mpl.cm.tab10.colors[6],
    r"SPERR($\epsilon_{abs}$)": mpl.cm.tab10.colors[2],
    r"SPERR": mpl.cm.tab10.colors[2],
}
colors_with_zero = { "0": mpl.cm.tab10.colors[8], r"ZFP($\epsilon_{abs}$)": mpl.cm.tab10.colors[0], r"ZFP": mpl.cm.tab10.colors[0], r"SZ3($\epsilon_{abs}$)": mpl.cm.tab10.colors[6], r"SZ3[v3.2]($\epsilon_{abs}$)": mpl.cm.tab10.colors[6], r"SZ3": mpl.cm.tab10.colors[6], r"SPERR($\epsilon_{abs}$)": mpl.cm.tab10.colors[2], r"SPERR": mpl.cm.tab10.colors[2], }
Copied!
fig, (ax1, axm, ax2) = plt.subplots(
    1, 3, width_ratios=[4, 0.5, 1], gridspec_kw=dict(wspace=0)
)

rng = np.random.default_rng(seed=42)

xticklabels = []

for i, (name, title) in enumerate(
    {
        "nan.tex": "Missing NaN Values",
        "missing.tex": "Missing Non-NaN Values",
        "specific-humidity-log10.tex": r"Pointwise $\log_{10}$",
        "kinetic-energy.tex": "Kinetic Energy",
        "derivative-radial.tex": r"Laplacian ($\Delta x$=const)",
        "derivative-log-exp.tex": "Logarithm of Laplacian",
        "vorticity.tex": "Relative Vorticity",
        "dssim.tex": "dSSIM (3x3)",
    }.items()
):
    crc = "CR (any NaN)" if name == "nan.tex" else "CR"

    cr = parse_latex_table(Path("tables") / name)[[crc, "C"]]

    # exclude results with no corrections
    cr = cr[cr["C"] != "0"][crc]

    cr = pd.to_numeric(cr.str.removeprefix(r"$\times$ "), errors="coerce")

    lossless = cr[cr.index.get_level_values("Corrections") == "lossless"].droplevel(
        "Corrections"
    )
    oneshot = cr[cr.index.get_level_values("Corrections") == "one-shot"].droplevel(
        "Corrections"
    )
    iterative = cr[cr.index.get_level_values("Corrections") == "iterative"].droplevel(
        "Corrections"
    )

    zstd = (
        cr[cr.index.get_level_values("Compressor") == "ZSTD(22)"].values
        / oneshot.values
    )
    optzconfig = {}
    for c in colors.keys():
        cname = {
            "zfp.rs": "ZFP",
            "sz3.rs": "SZ3",
            "sperr.rs": "SPERR",
        }[c]
        if (c == "sz3.rs") and (
            r"SZ3[v3.2]($\epsilon_{abs}$)" in cr.index.get_level_values("Compressor")
        ):
            cname = "SZ3[v3.2]"
        optzconfig[c] = (
            cr[cr.index.get_level_values("Compressor") == f"OptZConfig({cname})"].values
            / oneshot[
                oneshot.index.get_level_values("Compressor").isin(
                    [cname, rf"{cname}($\epsilon_{{abs}}$)"]
                )
            ].values
        )
    if np.any(cr.index.get_level_values("Compressor") == "QPET-SPERR"):
        qpet_sperr = (
            cr[cr.index.get_level_values("Compressor") == "QPET-SPERR"].values
            / oneshot[
                oneshot.index.get_level_values("Compressor").isin(
                    ["SPERR", r"SPERR($\epsilon_{abs}$)"]
                )
            ].values
        )
    else:
        qpet_sperr = np.zeros(0)

    ax1.boxplot(
        lossless / oneshot,
        notch=False,
        showfliers=False,
        positions=[-i * 0.2],
        orientation="horizontal",
    )

    ax2.boxplot(
        iterative / oneshot,
        notch=False,
        showfliers=False,
        positions=[-i * 0.2],
        orientation="horizontal",
    )

    scatter_colors = [
        colors_with_zero[c] for c in lossless.index.get_level_values("Compressor")
    ]

    ax1.scatter(
        lossless / oneshot,
        rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(oneshot)),
        marker="D",
        s=8,
        color=scatter_colors,
    )
    ax2.scatter(
        iterative / oneshot,
        rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(oneshot)),
        marker="*",
        s=16,
        color=scatter_colors,
    )

    qpet_scatter = rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(qpet_sperr))
    ax1.scatter(
        [x for x in qpet_sperr if x < 1],
        [y for x, y in zip(qpet_sperr, qpet_scatter) if x < 1],
        marker="X",
        s=24,
        color=mpl.cm.tab20b.colors[4],
    )
    ax2.scatter(
        [x for x in qpet_sperr if x >= 1],
        [y for x, y in zip(qpet_sperr, qpet_scatter) if x >= 1],
        marker="X",
        s=24,
        color=mpl.cm.tab20b.colors[4],
    )

    zstd_scatter = -i * 0.2 + 0.125 * (1 - np.linspace(0.0, 1.0, len(zstd)))
    axm.scatter(
        [1 / 3 for x in zstd if x < 1],
        [y for x, y in zip(zstd, zstd_scatter) if x < 1],
        marker="<",
        s=16,
        color=[c for x, c in zip(zstd, scatter_colors) if x < 1],
        edgecolors="gray",
        linewidths=1,
    )
    axm.scatter(
        [1 / 3 for x in zstd if x >= 1],
        [y for x, y in zip(zstd, zstd_scatter) if x >= 1],
        marker=">",
        s=16,
        color=[c for x, c in zip(zstd, scatter_colors) if x >= 1],
        edgecolors="gray",
        linewidths=1,
    )
    optzconfig_scatter = -i * 0.2 + 0.125 * (
        1
        - np.linspace(0.0, 1.0, sum(len(o) for o in optzconfig.values()) * 4 // 3)[
            -sum(len(o) for o in optzconfig.values()) :
        ]
    )
    axm.scatter(
        [2 / 3 for c in colors.keys() for x in optzconfig[c] if x < 1],
        optzconfig_scatter[[x < 1 for c in colors.keys() for x in optzconfig[c]]],
        marker="<",
        s=16,
        color=[colors[c] for c in colors.keys() for x in optzconfig[c] if x < 1],
        edgecolors=mpl.cm.tab20b.colors[10],
        linewidths=1,
    )
    axm.scatter(
        [2 / 3 for c in colors.keys() for x in optzconfig[c] if x >= 1],
        optzconfig_scatter[[x >= 1 for c in colors.keys() for x in optzconfig[c]]],
        marker=">",
        s=16,
        color=[colors[c] for c in colors.keys() for x in optzconfig[c] if x >= 1],
        edgecolors=mpl.cm.tab20b.colors[10],
        linewidths=1,
    )

    xticklabels.append(title)

ax1.set_title("Lossless")
ax1.set_xscale("log")
ax1.set_xlabel("compression ratio, relative to one-shot corrections")
ax1.set_yticks(np.arange(len(xticklabels)) * -0.2)
ax1.set_yticklabels(xticklabels)
ax1.axvline(1, c="k", zorder=-1, ls=":")
ax1.set_ylim(len(xticklabels) * -0.2 + 0.05, 0.15)
ax1.set_xlim(0.015, 1.25)
ax1.set_xticks([0.02, 0.05, 0.1, 0.2, 0.5, 1.0])
ax1.set_xticklabels(
    [r"$\div$50", r"$\div$20", r"$\div$10", r"$\div$5", r"$\div$2", r"$\div$1"]
)

ax2.set_title("Iterative")
ax2.set_xscale("log")
ax2.set_yticks(np.arange(len(xticklabels)) * -0.2)
ax2.set_yticklabels([""] * len(xticklabels))
ax2.axvline(1, c="k", zorder=-1, ls=":")
ax2.set_ylim(len(xticklabels) * -0.2 + 0.05, 0.15)
ax2.set_xlim(
    1
    / np.power(
        10.0,
        np.log10(2.5) / (1 - 4 * np.log10(1.25) / (np.log10(1.25) - np.log10(0.015)))
        - np.log10(2.5),
    ),
    2.5,
)
ax2.set_xticks([1.0, 2.0])
ax2.get_xaxis().set_major_formatter(
    mpl.ticker.FuncFormatter(lambda x, y: rf"$\times${int(x)}")
)
ax2.get_xaxis().set_minor_formatter("")

axm.axis("off")
axm.set_xlim(0, 1)

l1 = ax1.legend(
    handles=[
        mpl.lines.Line2D(
            [0],
            [0],
            marker="o",
            color="none",
            markerfacecolor=col,
            markeredgecolor=col,
            label=c,
        )
        for c, col in {
            "0": mpl.cm.tab10.colors[8],
            r"SZ3": mpl.cm.tab10.colors[6],
            r"ZFP": mpl.cm.tab10.colors[0],
            r"SPERR": mpl.cm.tab10.colors[2],
        }.items()
    ],
    ncol=2,
    loc="upper left",
    title="Compressor",
)
ax1.legend(
    handles=[
        mpl.lines.Line2D(
            [0],
            [0],
            marker=m,
            color="none",
            markerfacecolor=mpl.cm.tab10.colors[8]
            if c in ["ZSTD(22)", "OptZConfig"]
            else col,
            markeredgecolor=col,
            markeredgewidth=mw,
            label=c,
        )
        for c, (col, m, mw) in {
            "ZSTD(22)": ("gray", "<", 1),
            "OptZConfig": (mpl.cm.tab20b.colors[10], "<", 1),
            "QPET-SPERR": (mpl.cm.tab20b.colors[4], "X", 0),
        }.items()
    ],
    ncol=1,
    loc="center left",
    title="Comparison",
)
ax1.add_artist(l1)

# plt.tight_layout()

plt.savefig(Path("plots") / "summary-corrections.pdf", dpi=300, bbox_inches="tight")

plt.show()
fig, (ax1, axm, ax2) = plt.subplots( 1, 3, width_ratios=[4, 0.5, 1], gridspec_kw=dict(wspace=0) ) rng = np.random.default_rng(seed=42) xticklabels = [] for i, (name, title) in enumerate( { "nan.tex": "Missing NaN Values", "missing.tex": "Missing Non-NaN Values", "specific-humidity-log10.tex": r"Pointwise $\log_{10}$", "kinetic-energy.tex": "Kinetic Energy", "derivative-radial.tex": r"Laplacian ($\Delta x$=const)", "derivative-log-exp.tex": "Logarithm of Laplacian", "vorticity.tex": "Relative Vorticity", "dssim.tex": "dSSIM (3x3)", }.items() ): crc = "CR (any NaN)" if name == "nan.tex" else "CR" cr = parse_latex_table(Path("tables") / name)[[crc, "C"]] # exclude results with no corrections cr = cr[cr["C"] != "0"][crc] cr = pd.to_numeric(cr.str.removeprefix(r"$\times$ "), errors="coerce") lossless = cr[cr.index.get_level_values("Corrections") == "lossless"].droplevel( "Corrections" ) oneshot = cr[cr.index.get_level_values("Corrections") == "one-shot"].droplevel( "Corrections" ) iterative = cr[cr.index.get_level_values("Corrections") == "iterative"].droplevel( "Corrections" ) zstd = ( cr[cr.index.get_level_values("Compressor") == "ZSTD(22)"].values / oneshot.values ) optzconfig = {} for c in colors.keys(): cname = { "zfp.rs": "ZFP", "sz3.rs": "SZ3", "sperr.rs": "SPERR", }[c] if (c == "sz3.rs") and ( r"SZ3[v3.2]($\epsilon_{abs}$)" in cr.index.get_level_values("Compressor") ): cname = "SZ3[v3.2]" optzconfig[c] = ( cr[cr.index.get_level_values("Compressor") == f"OptZConfig({cname})"].values / oneshot[ oneshot.index.get_level_values("Compressor").isin( [cname, rf"{cname}($\epsilon_{{abs}}$)"] ) ].values ) if np.any(cr.index.get_level_values("Compressor") == "QPET-SPERR"): qpet_sperr = ( cr[cr.index.get_level_values("Compressor") == "QPET-SPERR"].values / oneshot[ oneshot.index.get_level_values("Compressor").isin( ["SPERR", r"SPERR($\epsilon_{abs}$)"] ) ].values ) else: qpet_sperr = np.zeros(0) ax1.boxplot( lossless / oneshot, notch=False, showfliers=False, positions=[-i * 0.2], orientation="horizontal", ) ax2.boxplot( iterative / oneshot, notch=False, showfliers=False, positions=[-i * 0.2], orientation="horizontal", ) scatter_colors = [ colors_with_zero[c] for c in lossless.index.get_level_values("Compressor") ] ax1.scatter( lossless / oneshot, rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(oneshot)), marker="D", s=8, color=scatter_colors, ) ax2.scatter( iterative / oneshot, rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(oneshot)), marker="*", s=16, color=scatter_colors, ) qpet_scatter = rng.normal(loc=-i * 0.2, scale=0.1 / 5, size=len(qpet_sperr)) ax1.scatter( [x for x in qpet_sperr if x < 1], [y for x, y in zip(qpet_sperr, qpet_scatter) if x < 1], marker="X", s=24, color=mpl.cm.tab20b.colors[4], ) ax2.scatter( [x for x in qpet_sperr if x >= 1], [y for x, y in zip(qpet_sperr, qpet_scatter) if x >= 1], marker="X", s=24, color=mpl.cm.tab20b.colors[4], ) zstd_scatter = -i * 0.2 + 0.125 * (1 - np.linspace(0.0, 1.0, len(zstd))) axm.scatter( [1 / 3 for x in zstd if x < 1], [y for x, y in zip(zstd, zstd_scatter) if x < 1], marker="<", s=16, color=[c for x, c in zip(zstd, scatter_colors) if x < 1], edgecolors="gray", linewidths=1, ) axm.scatter( [1 / 3 for x in zstd if x >= 1], [y for x, y in zip(zstd, zstd_scatter) if x >= 1], marker=">", s=16, color=[c for x, c in zip(zstd, scatter_colors) if x >= 1], edgecolors="gray", linewidths=1, ) optzconfig_scatter = -i * 0.2 + 0.125 * ( 1 - np.linspace(0.0, 1.0, sum(len(o) for o in optzconfig.values()) * 4 // 3)[ -sum(len(o) for o in optzconfig.values()) : ] ) axm.scatter( [2 / 3 for c in colors.keys() for x in optzconfig[c] if x < 1], optzconfig_scatter[[x < 1 for c in colors.keys() for x in optzconfig[c]]], marker="<", s=16, color=[colors[c] for c in colors.keys() for x in optzconfig[c] if x < 1], edgecolors=mpl.cm.tab20b.colors[10], linewidths=1, ) axm.scatter( [2 / 3 for c in colors.keys() for x in optzconfig[c] if x >= 1], optzconfig_scatter[[x >= 1 for c in colors.keys() for x in optzconfig[c]]], marker=">", s=16, color=[colors[c] for c in colors.keys() for x in optzconfig[c] if x >= 1], edgecolors=mpl.cm.tab20b.colors[10], linewidths=1, ) xticklabels.append(title) ax1.set_title("Lossless") ax1.set_xscale("log") ax1.set_xlabel("compression ratio, relative to one-shot corrections") ax1.set_yticks(np.arange(len(xticklabels)) * -0.2) ax1.set_yticklabels(xticklabels) ax1.axvline(1, c="k", zorder=-1, ls=":") ax1.set_ylim(len(xticklabels) * -0.2 + 0.05, 0.15) ax1.set_xlim(0.015, 1.25) ax1.set_xticks([0.02, 0.05, 0.1, 0.2, 0.5, 1.0]) ax1.set_xticklabels( [r"$\div$50", r"$\div$20", r"$\div$10", r"$\div$5", r"$\div$2", r"$\div$1"] ) ax2.set_title("Iterative") ax2.set_xscale("log") ax2.set_yticks(np.arange(len(xticklabels)) * -0.2) ax2.set_yticklabels([""] * len(xticklabels)) ax2.axvline(1, c="k", zorder=-1, ls=":") ax2.set_ylim(len(xticklabels) * -0.2 + 0.05, 0.15) ax2.set_xlim( 1 / np.power( 10.0, np.log10(2.5) / (1 - 4 * np.log10(1.25) / (np.log10(1.25) - np.log10(0.015))) - np.log10(2.5), ), 2.5, ) ax2.set_xticks([1.0, 2.0]) ax2.get_xaxis().set_major_formatter( mpl.ticker.FuncFormatter(lambda x, y: rf"$\times${int(x)}") ) ax2.get_xaxis().set_minor_formatter("") axm.axis("off") axm.set_xlim(0, 1) l1 = ax1.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=col, markeredgecolor=col, label=c, ) for c, col in { "0": mpl.cm.tab10.colors[8], r"SZ3": mpl.cm.tab10.colors[6], r"ZFP": mpl.cm.tab10.colors[0], r"SPERR": mpl.cm.tab10.colors[2], }.items() ], ncol=2, loc="upper left", title="Compressor", ) ax1.legend( handles=[ mpl.lines.Line2D( [0], [0], marker=m, color="none", markerfacecolor=mpl.cm.tab10.colors[8] if c in ["ZSTD(22)", "OptZConfig"] else col, markeredgecolor=col, markeredgewidth=mw, label=c, ) for c, (col, m, mw) in { "ZSTD(22)": ("gray", "<", 1), "OptZConfig": (mpl.cm.tab20b.colors[10], "<", 1), "QPET-SPERR": (mpl.cm.tab20b.colors[4], "X", 0), }.items() ], ncol=1, loc="center left", title="Comparison", ) ax1.add_artist(l1) # plt.tight_layout() plt.savefig(Path("plots") / "summary-corrections.pdf", dpi=300, bbox_inches="tight") plt.show()
No description has been provided for this image
Copied!
fig = plt.figure(layout="tight", figsize=(9, 3))
gs = GridSpec(1, 3, figure=fig)

all_scatter_colors = []
all_codec_decode_timings = []
all_corrections_decode_timings = []
all_corrections_apply_timings = []

i = 0

for name, title in {
    "nan.json": "Missing NaN Values",
    "missing.json": "Missing Non-NaN Values",
    "specific-humidity-log10.json": r"Pointwise $\log_{10}$",
    "kinetic-energy.json": "Kinetic Energy",
    "derivative-radial.json": r"Laplacian ($\Delta x$=const)",
    "derivative-log-exp.json": "Logarithm of Laplacian",
    "vorticity.json": "Relative Vorticity",
    "dssim.json": "dSSIM (3x3)",
}.items():
    with Path("observations").joinpath(name).open() as f:
        observations = json.load(f)

    scatter_colors = []
    is_iteratives = []
    is_losslesss = []
    codec_encode_timings = []
    codec_decode_timings = []
    corrections_compute_timings = []
    corrections_encode_timings = []

    for result in observations:
        if result["codec"]["id"] != "safeguards":
            continue

        codec_id = result["codec"]["codec"]["id"]

        if codec_id == "combinators.stack":
            a, b = result["codec"]["codec"]["codecs"]
            assert a["id"] == "replace.filter", a["id"]
            codec_id = b["id"]
        elif codec_id == "mask.meta":
            codec_id = result["codec"]["codec"]["codec"]["id"]
        elif codec_id == "pw_ratio":
            codec_id = result["codec"]["codec"]["log_codec"]["id"]
        if codec_id in ["sz3.2.rs", "sz3.3.rs"]:
            codec_id = "sz3.rs"

        assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id

        if codec_id == "zero":
            continue

        is_iterative = result["codec"]["compute"]["unstable_iterative"]
        is_lossless = result["codec"]["compute"]["unstable_lossless_corrections"]

        assert result["codec"]["lossless"]["for_codec"] is None

        (safeguarded_encode_timing,) = result["encode_timing"][
            observe.json_hash(result["codec"])
        ]
        (codec_encode_encode_timing,) = result["encode_timing"][
            observe.json_hash(result["codec"]["codec"])
        ]
        codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][
            observe.json_hash(result["codec"]["codec"])
        ]
        # corrections lossless codec may be skipped if no corrections are needed
        (lossless_for_corrections_encode_timing,) = result["encode_timing"].get(
            observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None]
        )

        (safeguarded_decode_timing,) = result["decode_timing"][
            observe.json_hash(result["codec"])
        ]
        # corrections lossless codec may be skipped if no corrections are needed
        (lossless_for_corrections_decode_timing,) = result["decode_timing"].get(
            observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None]
        )

        # skip when no corrections were needed
        # - missing with mask meta-codec
        # - log10 with pwe meta-codec
        # - NaN with SZ3[v3.3]
        # - radial Laplacian with the 3rd error bound
        if lossless_for_corrections_encode_timing is None:
            continue
        assert lossless_for_corrections_decode_timing is not None

        scatter_colors.append(colors[codec_id])
        is_iteratives.append(is_iterative and not is_lossless)
        is_losslesss.append(is_iterative and is_lossless)
        codec_encode_timings.append(
            codec_encode_encode_timing / codec_encode_encode_timing
        )
        codec_decode_timings.append(
            codec_encode_decode_timing / codec_encode_encode_timing
        )
        corrections_compute_timings.append(
            lossless_for_corrections_encode_timing / codec_encode_encode_timing
        )
        corrections_encode_timings.append(
            (
                safeguarded_encode_timing
                - codec_encode_encode_timing
                - codec_encode_decode_timing
                - lossless_for_corrections_encode_timing
            )
            / codec_encode_encode_timing
        )

        all_scatter_colors.append(colors[codec_id])
        all_codec_decode_timings.append(codec_decode_timing / codec_decode_timing)
        all_corrections_decode_timings.append(
            lossless_for_corrections_decode_timing / codec_decode_timing
        )
        all_corrections_apply_timings.append(
            (
                safeguarded_decode_timing
                - codec_decode_timing
                - lossless_for_corrections_decode_timing
            )
            / codec_decode_timing
        )

    if name not in ["nan.json", "vorticity.json"]:
        continue

    is_losslesss = np.array(is_losslesss)
    is_iteratives = np.array(is_iteratives)

    ax = fig.add_subplot(gs[i])

    rng = np.random.default_rng(seed=42)

    b1 = ax.boxplot(codec_encode_timings, notch=True, showfliers=False, positions=[0])
    b2 = ax.boxplot(
        np.array(codec_decode_timings) + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.2],
    )
    b3 = ax.boxplot(
        np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.4],
    )
    b4 = ax.boxplot(
        np.array(corrections_compute_timings)
        + np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings),
        notch=False,
        showfliers=False,
        positions=[0.6],
    )

    extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10

    ax.set_xlim(-0.1, 0.7)
    ax.set_xticklabels(["CE", "+CD", "+Sg", "+CC"])
    ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra)
    ax.set_yticks(
        [
            1 if y == 0 else y
            for y in ax.get_yticks()
            if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0
        ]
    )
    ax.get_yticklabels()[-1].set_fontweight("bold")
    ax.text(
        0.68,
        b4["caps"][1].get_data()[1][0] + extra / 2,
        rf"max: $\times${np.round(np.amax(corrections_compute_timings) + np.amax(corrections_encode_timings) + np.amax(codec_decode_timings) + np.amax(codec_encode_timings), 1)}",
        ha="right",
        va="bottom",
    )

    cs = np.array(scatter_colors)

    xs = rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_encode_timings))
    ys = np.array(codec_encode_timings)
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.2, scale=0.1 / 5, size=len(codec_decode_timings))
    ys = np.array(codec_decode_timings) + np.array(codec_encode_timings)
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_encode_timings))
    ys = (
        np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings)
    )
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    xs = rng.normal(loc=0.6, scale=0.1 / 5, size=len(corrections_compute_timings))
    ys = (
        np.array(corrections_compute_timings)
        + np.array(corrections_encode_timings)
        + np.array(codec_decode_timings)
        + np.array(codec_encode_timings)
    )
    ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10)
    ax.scatter(
        xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24
    )
    ax.scatter(
        xs[~(is_iteratives | is_losslesss)],
        ys[~(is_iteratives | is_losslesss)],
        marker="o",
        c=cs[~(is_iteratives | is_losslesss)],
        s=16,
    )

    if i == 0:
        ax.legend(
            handles=[
                mpl.lines.Line2D(
                    [0],
                    [0],
                    marker="o",
                    color="none",
                    markerfacecolor=col,
                    markeredgecolor=col,
                    label={
                        "zfp.rs": "ZFP",
                        "sz3.rs": "SZ3",
                        "sperr.rs": "SPERR",
                    }[c],
                )
                for c, col in colors.items()
            ],
            loc="upper left",
            title="Compressor",
        )
    elif i == 1:
        ax.legend(
            handles=[
                mpl.lines.Line2D(
                    [0],
                    [0],
                    marker="D",
                    color="none",
                    markerfacecolor=colors["zfp.rs"],
                    markeredgecolor=colors["zfp.rs"],
                    label="lossless",
                ),
                mpl.lines.Line2D(
                    [0],
                    [0],
                    marker="o",
                    color="none",
                    markerfacecolor=colors["zfp.rs"],
                    markeredgecolor=colors["zfp.rs"],
                    label="one-shot",
                ),
                mpl.lines.Line2D(
                    [0],
                    [0],
                    marker="*",
                    color="none",
                    markerfacecolor=colors["zfp.rs"],
                    markeredgecolor=colors["zfp.rs"],
                    label="iterative",
                ),
            ],
            loc="upper left",
            title="Corrections",
        )

    i += 1

ax = fig.add_subplot(gs[2])

b1 = ax.boxplot(all_codec_decode_timings, notch=False, showfliers=False, positions=[0])
b2 = ax.boxplot(
    np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings),
    notch=False,
    showfliers=False,
    positions=[0.2],
)
b3 = ax.boxplot(
    np.array(all_corrections_apply_timings)
    + np.array(all_corrections_decode_timings)
    + np.array(all_codec_decode_timings),
    notch=False,
    showfliers=False,
    positions=[0.4],
)

extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10

ax.yaxis.tick_right()

ax.set_xlim(-0.1, 0.5)
ax.set_xticks([0, 0.2, 0.4])
ax.set_xticklabels(["CD", "+ DC", "+ AC"])
ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra)
ax.set_yticks(
    [
        1 if y == 0 else y
        for y in ax.get_yticks()
        if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0
    ]
)
ax.get_yticklabels()[-1].set_fontweight("bold")

ax.text(
    0.48,
    b3["caps"][1].get_data()[1][0] + extra / 2,
    rf"max: $\times${np.amax(all_corrections_apply_timings) + np.amax(all_corrections_decode_timings) + np.amax(all_codec_decode_timings):0.3}",
    ha="right",
    va="bottom",
)

ax.scatter(
    rng.normal(loc=0.0, scale=0.1 / 5, size=len(all_codec_decode_timings)),
    all_codec_decode_timings,
    marker=".",
    c=all_scatter_colors,
)
ax.scatter(
    rng.normal(loc=0.2, scale=0.1 / 5, size=len(all_corrections_decode_timings)),
    np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings),
    marker=".",
    c=all_scatter_colors,
)
ax.scatter(
    rng.normal(loc=0.4, scale=0.1 / 5, size=len(all_corrections_apply_timings)),
    np.array(all_corrections_apply_timings)
    + np.array(all_corrections_decode_timings)
    + np.array(all_codec_decode_timings),
    marker=".",
    c=all_scatter_colors,
)

plt.tight_layout()

ax.set_rasterized(True)

plt.tight_layout()

plt.savefig(Path("plots") / "summary-overhead-egu26.pdf", dpi=300)

plt.show()
fig = plt.figure(layout="tight", figsize=(9, 3)) gs = GridSpec(1, 3, figure=fig) all_scatter_colors = [] all_codec_decode_timings = [] all_corrections_decode_timings = [] all_corrections_apply_timings = [] i = 0 for name, title in { "nan.json": "Missing NaN Values", "missing.json": "Missing Non-NaN Values", "specific-humidity-log10.json": r"Pointwise $\log_{10}$", "kinetic-energy.json": "Kinetic Energy", "derivative-radial.json": r"Laplacian ($\Delta x$=const)", "derivative-log-exp.json": "Logarithm of Laplacian", "vorticity.json": "Relative Vorticity", "dssim.json": "dSSIM (3x3)", }.items(): with Path("observations").joinpath(name).open() as f: observations = json.load(f) scatter_colors = [] is_iteratives = [] is_losslesss = [] codec_encode_timings = [] codec_decode_timings = [] corrections_compute_timings = [] corrections_encode_timings = [] for result in observations: if result["codec"]["id"] != "safeguards": continue codec_id = result["codec"]["codec"]["id"] if codec_id == "combinators.stack": a, b = result["codec"]["codec"]["codecs"] assert a["id"] == "replace.filter", a["id"] codec_id = b["id"] elif codec_id == "mask.meta": codec_id = result["codec"]["codec"]["codec"]["id"] elif codec_id == "pw_ratio": codec_id = result["codec"]["codec"]["log_codec"]["id"] if codec_id in ["sz3.2.rs", "sz3.3.rs"]: codec_id = "sz3.rs" assert codec_id in ["zero", "zfp.rs", "sz3.rs", "sperr.rs"], codec_id if codec_id == "zero": continue is_iterative = result["codec"]["compute"]["unstable_iterative"] is_lossless = result["codec"]["compute"]["unstable_lossless_corrections"] assert result["codec"]["lossless"]["for_codec"] is None (safeguarded_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]) ] (codec_encode_encode_timing,) = result["encode_timing"][ observe.json_hash(result["codec"]["codec"]) ] codec_encode_decode_timing, codec_decode_timing = result["decode_timing"][ observe.json_hash(result["codec"]["codec"]) ] # corrections lossless codec may be skipped if no corrections are needed (lossless_for_corrections_encode_timing,) = result["encode_timing"].get( observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None] ) (safeguarded_decode_timing,) = result["decode_timing"][ observe.json_hash(result["codec"]) ] # corrections lossless codec may be skipped if no corrections are needed (lossless_for_corrections_decode_timing,) = result["decode_timing"].get( observe.json_hash(result["codec"]["lossless"]["for_corrections"]), [None] ) # skip when no corrections were needed # - missing with mask meta-codec # - log10 with pwe meta-codec # - NaN with SZ3[v3.3] # - radial Laplacian with the 3rd error bound if lossless_for_corrections_encode_timing is None: continue assert lossless_for_corrections_decode_timing is not None scatter_colors.append(colors[codec_id]) is_iteratives.append(is_iterative and not is_lossless) is_losslesss.append(is_iterative and is_lossless) codec_encode_timings.append( codec_encode_encode_timing / codec_encode_encode_timing ) codec_decode_timings.append( codec_encode_decode_timing / codec_encode_encode_timing ) corrections_compute_timings.append( lossless_for_corrections_encode_timing / codec_encode_encode_timing ) corrections_encode_timings.append( ( safeguarded_encode_timing - codec_encode_encode_timing - codec_encode_decode_timing - lossless_for_corrections_encode_timing ) / codec_encode_encode_timing ) all_scatter_colors.append(colors[codec_id]) all_codec_decode_timings.append(codec_decode_timing / codec_decode_timing) all_corrections_decode_timings.append( lossless_for_corrections_decode_timing / codec_decode_timing ) all_corrections_apply_timings.append( ( safeguarded_decode_timing - codec_decode_timing - lossless_for_corrections_decode_timing ) / codec_decode_timing ) if name not in ["nan.json", "vorticity.json"]: continue is_losslesss = np.array(is_losslesss) is_iteratives = np.array(is_iteratives) ax = fig.add_subplot(gs[i]) rng = np.random.default_rng(seed=42) b1 = ax.boxplot(codec_encode_timings, notch=True, showfliers=False, positions=[0]) b2 = ax.boxplot( np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.2], ) b3 = ax.boxplot( np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.4], ) b4 = ax.boxplot( np.array(corrections_compute_timings) + np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings), notch=False, showfliers=False, positions=[0.6], ) extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10 ax.set_xlim(-0.1, 0.7) ax.set_xticklabels(["CE", "+CD", "+Sg", "+CC"]) ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra) ax.set_yticks( [ 1 if y == 0 else y for y in ax.get_yticks() if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0 ] ) ax.get_yticklabels()[-1].set_fontweight("bold") ax.text( 0.68, b4["caps"][1].get_data()[1][0] + extra / 2, rf"max: $\times${np.round(np.amax(corrections_compute_timings) + np.amax(corrections_encode_timings) + np.amax(codec_decode_timings) + np.amax(codec_encode_timings), 1)}", ha="right", va="bottom", ) cs = np.array(scatter_colors) xs = rng.normal(loc=0.0, scale=0.1 / 5, size=len(codec_encode_timings)) ys = np.array(codec_encode_timings) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.2, scale=0.1 / 5, size=len(codec_decode_timings)) ys = np.array(codec_decode_timings) + np.array(codec_encode_timings) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.4, scale=0.1 / 5, size=len(corrections_encode_timings)) ys = ( np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings) ) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) xs = rng.normal(loc=0.6, scale=0.1 / 5, size=len(corrections_compute_timings)) ys = ( np.array(corrections_compute_timings) + np.array(corrections_encode_timings) + np.array(codec_decode_timings) + np.array(codec_encode_timings) ) ax.scatter(xs[is_losslesss], ys[is_losslesss], marker="D", c=cs[is_losslesss], s=10) ax.scatter( xs[is_iteratives], ys[is_iteratives], marker="*", c=cs[is_iteratives], s=24 ) ax.scatter( xs[~(is_iteratives | is_losslesss)], ys[~(is_iteratives | is_losslesss)], marker="o", c=cs[~(is_iteratives | is_losslesss)], s=16, ) if i == 0: ax.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=col, markeredgecolor=col, label={ "zfp.rs": "ZFP", "sz3.rs": "SZ3", "sperr.rs": "SPERR", }[c], ) for c, col in colors.items() ], loc="upper left", title="Compressor", ) elif i == 1: ax.legend( handles=[ mpl.lines.Line2D( [0], [0], marker="D", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="lossless", ), mpl.lines.Line2D( [0], [0], marker="o", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="one-shot", ), mpl.lines.Line2D( [0], [0], marker="*", color="none", markerfacecolor=colors["zfp.rs"], markeredgecolor=colors["zfp.rs"], label="iterative", ), ], loc="upper left", title="Corrections", ) i += 1 ax = fig.add_subplot(gs[2]) b1 = ax.boxplot(all_codec_decode_timings, notch=False, showfliers=False, positions=[0]) b2 = ax.boxplot( np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings), notch=False, showfliers=False, positions=[0.2], ) b3 = ax.boxplot( np.array(all_corrections_apply_timings) + np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings), notch=False, showfliers=False, positions=[0.4], ) extra = (ax.get_ylim()[1] - ax.get_ylim()[0]) / 10 ax.yaxis.tick_right() ax.set_xlim(-0.1, 0.5) ax.set_xticks([0, 0.2, 0.4]) ax.set_xticklabels(["CD", "+ DC", "+ AC"]) ax.set_ylim(ax.get_ylim()[0], ax.get_ylim()[1] + extra) ax.set_yticks( [ 1 if y == 0 else y for y in ax.get_yticks() if (y >= 1 and y <= ax.get_ylim()[1]) or y == 0 ] ) ax.get_yticklabels()[-1].set_fontweight("bold") ax.text( 0.48, b3["caps"][1].get_data()[1][0] + extra / 2, rf"max: $\times${np.amax(all_corrections_apply_timings) + np.amax(all_corrections_decode_timings) + np.amax(all_codec_decode_timings):0.3}", ha="right", va="bottom", ) ax.scatter( rng.normal(loc=0.0, scale=0.1 / 5, size=len(all_codec_decode_timings)), all_codec_decode_timings, marker=".", c=all_scatter_colors, ) ax.scatter( rng.normal(loc=0.2, scale=0.1 / 5, size=len(all_corrections_decode_timings)), np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings), marker=".", c=all_scatter_colors, ) ax.scatter( rng.normal(loc=0.4, scale=0.1 / 5, size=len(all_corrections_apply_timings)), np.array(all_corrections_apply_timings) + np.array(all_corrections_decode_timings) + np.array(all_codec_decode_timings), marker=".", c=all_scatter_colors, ) plt.tight_layout() ax.set_rasterized(True) plt.tight_layout() plt.savefig(Path("plots") / "summary-overhead-egu26.pdf", dpi=300) plt.show()
No description has been provided for this image
Copied!


Built with ProperDocs using a theme provided by Read the Docs.