Permutation Example — sort_integer_indexed, sort_by_abs_indexed, permute_vector, permute_matrix

This example generates an integer array and a float array with mixed positive and negative values, then demonstrates four IMSL-style permutation utilities:

The permutation indices from the indexed sort are applied directly to other arrays, showing how all four functions compose naturally.

Example Code

"""IMSL permutation example: sort_integer_indexed, sort_by_abs_indexed, permute_vector, permute_matrix.

Outputs:
- Table printed to stdout
- SVG saved to test_output/example_imsl_permutation.svg
"""
from __future__ import annotations

from pathlib import Path
from typing import Dict

import matplotlib.pyplot as plt
import numpy as np

from utilities import (
    sort_integer_indexed,
    sort_by_abs_indexed,
    permute_vector,
    permute_matrix,
)


def run_demo_imsl_permutation() -> Dict[str, object]:
    """Demonstrate indexed sorting and permutation utilities.

    Shows how ``sort_integer_indexed`` and ``sort_by_abs_indexed`` return
    permutation index arrays that can then be applied to vectors and matrices
    via ``permute_vector`` / ``permute_matrix``.

    Args:
        None

    Returns:
        Dict[str, object]: Result dict with keys ``integers``,
            ``int_sorted``, ``int_perm``, ``floats``, ``abs_sorted``,
            ``abs_perm``, ``permuted_vector``, ``permuted_matrix``,
            and ``plot_path``.
    """
    rng = np.random.default_rng(7)

    # ── Integer indexed sort ──────────────────────────────────────────────────
    integers = np.array([42, -7, 15, 3, -28, 100, 0, -5], dtype=int)
    int_sorted, int_perm = sort_integer_indexed(integers, ascending=True)

    print("\nIMSL Indexed Sort Example")
    print("=" * 60)
    print("\n--- sort_integer_indexed (ascending) ---")
    print(f"{'Pos':<5} {'Original':>10} {'Sorted':>10} {'Perm idx':>10}")
    print("-" * 40)
    for i in range(len(integers)):
        print(f"{i:<5} {integers[i]:>10} {int_sorted[i]:>10} {int_perm[i]:>10}")
    print(f"\nPermutation: {int_perm.tolist()}")

    # ── Absolute-value indexed sort ───────────────────────────────────────────
    floats = rng.uniform(-5.0, 5.0, 8).round(2)
    abs_sorted, abs_perm = sort_by_abs_indexed(floats, ascending=True)

    print("\n--- sort_by_abs_indexed (ascending by |x|) ---")
    print(f"{'Pos':<5} {'Original':>10} {'|x| sorted':>12} {'Perm idx':>10}")
    print("-" * 42)
    for i in range(len(floats)):
        print(f"{i:<5} {floats[i]:>10.2f} {abs_sorted[i]:>12.2f} {abs_perm[i]:>10}")
    print(f"\nPermutation: {abs_perm.tolist()}")

    # ── permute_vector ────────────────────────────────────────────────────────
    labels = np.array([f"item-{i}" for i in range(8)])
    permuted_labels = permute_vector(labels, int_perm)

    print("\n--- permute_vector (apply integer sort perm to label array) ---")
    print(f"{'Pos':<5} {'Before':>12} {'After':>12}")
    print("-" * 34)
    for i in range(len(labels)):
        print(f"{i:<5} {labels[i]:>12} {permuted_labels[i]:>12}")

    # ── permute_matrix ────────────────────────────────────────────────────────
    A = np.arange(24, dtype=float).reshape(8, 3)
    A_row_perm = permute_matrix(A, int_perm, axis=0)

    print("\n--- permute_matrix (row permutation by integer sort perm) ---")
    print(f"Original rows (first 3 cols):  {A[:, 0].tolist()}")
    print(f"Permuted rows (first 3 cols):  {A_row_perm[:, 0].tolist()}")

    # ── Plot ──────────────────────────────────────────────────────────────────
    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "example_imsl_permutation.svg"

    fig, axes = plt.subplots(2, 2, figsize=(13, 9))
    bar_x = np.arange(len(integers))

    # Top-left: integer sort before/after
    ax = axes[0, 0]
    ax.bar(bar_x - 0.2, integers, width=0.4, label="Original", color="#1d4ed8", alpha=0.85)
    ax.bar(bar_x + 0.2, int_sorted, width=0.4, label="Sorted", color="#60a5fa", alpha=0.85)
    ax.axhline(0, color="black", linewidth=0.7)
    ax.set_xticks(bar_x)
    ax.set_xticklabels([str(i) for i in bar_x])
    ax.set_xlabel("Position")
    ax.set_ylabel("Value")
    ax.set_title("sort_integer_indexed — original vs sorted")
    ax.legend()
    ax.grid(True, alpha=0.3)

    # Top-right: abs sort before/after
    bar_f = np.arange(len(floats))
    ax2 = axes[0, 1]
    ax2.bar(bar_f - 0.2, floats, width=0.4, label="Original", color="#047857", alpha=0.85)
    ax2.bar(bar_f + 0.2, abs_sorted, width=0.4, label="|x|-sorted", color="#34d399", alpha=0.85)
    ax2.axhline(0, color="black", linewidth=0.7)
    ax2.set_xticks(bar_f)
    ax2.set_xticklabels([str(i) for i in bar_f])
    ax2.set_xlabel("Position")
    ax2.set_ylabel("Value (signed)")
    ax2.set_title("sort_by_abs_indexed — original vs |x|-sorted")
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    # Bottom-left: permute_vector numeric
    permuted_floats = permute_vector(floats, abs_perm)
    ax3 = axes[1, 0]
    ax3.bar(bar_f - 0.2, floats, width=0.4, label="Before permute", color="#7c3aed", alpha=0.85)
    ax3.bar(bar_f + 0.2, permuted_floats, width=0.4, label="After permute", color="#c084fc", alpha=0.85)
    ax3.axhline(0, color="black", linewidth=0.7)
    ax3.set_xticks(bar_f)
    ax3.set_xticklabels([str(i) for i in bar_f])
    ax3.set_xlabel("Position")
    ax3.set_ylabel("Value (signed)")
    ax3.set_title("permute_vector — before vs after (abs perm)")
    ax3.legend()
    ax3.grid(True, alpha=0.3)

    # Bottom-right: permute_matrix heatmap (8×3 before/after)
    ax4 = axes[1, 1]
    combined = np.hstack([A[:, :3], np.full((8, 1), np.nan), A_row_perm[:, :3]])
    im = ax4.imshow(combined, aspect="auto", cmap="Blues")
    ax4.set_title("permute_matrix — rows before | rows after")
    ax4.set_xlabel("Column index (| = divider)")
    ax4.set_ylabel("Row index")
    ax4.set_xticks([0, 1, 2, 4, 5, 6])
    ax4.set_xticklabels(["c0", "c1", "c2", "c0'", "c1'", "c2'"])
    fig.colorbar(im, ax=ax4, shrink=0.8)

    fig.suptitle(
        "IMSL Permutation: sort_integer_indexed / sort_by_abs_indexed / permute_vector / permute_matrix",
        fontweight="bold",
    )
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)

    print(f"\nPlot saved to: {plot_path}")

    return {
        "integers": integers,
        "int_sorted": int_sorted,
        "int_perm": int_perm,
        "floats": floats,
        "abs_sorted": abs_sorted,
        "abs_perm": abs_perm,
        "permuted_vector": permuted_floats,
        "permuted_matrix": A_row_perm,
        "plot_path": str(plot_path),
    }


if __name__ == "__main__":
    run_demo_imsl_permutation()

Plot Output

Four-panel chart showing before/after integer sort, abs-value sort, vector permutation, and matrix heatmap

Top-left: original integer array vs sorted ascending. Top-right: float array vs sorted by absolute value (sign preserved). Bottom-left: vector before and after permutation. Bottom-right: 8×3 matrix row-permutation shown as a heatmap.

Console Output

IMSL Indexed Sort Example
============================================================

--- sort_integer_indexed (ascending) ---
Pos     Original     Sorted   Perm idx
----------------------------------------
0             42        -28          4
1             -7         -7          1
2             15         -5          7
3              3          0          6
4            -28          3          3
5            100         15          2
6              0         42          0
7             -5        100          5

Permutation: [4, 1, 7, 6, 3, 2, 0, 5]

--- sort_by_abs_indexed (ascending by |x|) ---
Pos     Original   |x| sorted   Perm idx
------------------------------------------
0           1.25         1.25          0
1           3.97        -2.00          4
2           2.76        -2.75          3
3          -2.75         2.76          2
4          -2.00         3.21          7
5           3.74         3.74          5
6          -4.95         3.97          1
7           3.21        -4.95          6

Permutation: [0, 4, 3, 2, 7, 5, 1, 6]

--- permute_vector (apply integer sort perm to label array) ---
Pos         Before        After
----------------------------------
0           item-0       item-4
1           item-1       item-1
2           item-2       item-7
3           item-3       item-6
4           item-4       item-3
5           item-5       item-2
6           item-6       item-0
7           item-7       item-5

--- permute_matrix (row permutation by integer sort perm) ---
Original rows (first 3 cols):  [0.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0]
Permuted rows (first 3 cols):  [12.0, 3.0, 21.0, 18.0, 9.0, 6.0, 0.0, 15.0]

Plot saved to: test_output\example_imsl_permutation.svg