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:
utilities.sort_integer_indexed()— sort integers and return permutation indicesutilities.sort_by_abs_indexed()— sort by absolute value, return indices and signed valuesutilities.permute_vector()— reorder a vector using a permutation index arrayutilities.permute_matrix()— reorder rows (or columns) of a matrix
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¶
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