#!/usr/bin/env python3 """ example.py — Demonstrate hv_octonion_tree. NumPy only. """ import numpy as np from hv_octonion_tree import ( orandom, omul, oconj, random_tree, eval_tree, batch_eval, all_trees, tree_depth, tree_key, unique_random_trees, injectivity_exhaustive, injectivity_sampled, mitm_analysis, hierarchical_injectivity, check_alternativity, check_moufang, check_norm_preservation, noise_test, structured_input_test, notarize, hierarchical_key, ) def demo_single_tree(): print("=" * 72) print("Demo 1 — single tree evaluation") print("=" * 72) print() rng = np.random.default_rng(0) n = 8 vals = np.stack([orandom(rng) for _ in range(n)]) tree = random_tree(n, rng) product = eval_tree(tree, vals) print(f" n = {n}") print(f" tree depth = {tree_depth(tree)}") print(f" ||product|| = {np.linalg.norm(product):.15f}") print(f" product = {np.round(product, 4)}") print() def demo_batch(): print("=" * 72) print("Demo 2 — batched evaluation") print("=" * 72) print() rng = np.random.default_rng(0) n = 8 vals = np.stack([orandom(rng) for _ in range(n)]) trees = unique_random_trees(n, 100, rng) products = batch_eval(trees, vals) print(f" n = {n}, K = {len(trees)} trees") print(f" products shape: {products.shape}") print(f" pairwise min distance: ", end="") D = np.linalg.norm(products[:, None, :] - products[None, :, :], axis=-1) iu = np.triu_indices(len(products), k=1) print(f"{D[iu].min():.4f}") print() def demo_injectivity(): print("=" * 72) print("Demo 3 — injectivity tests") print("=" * 72) print() print(" Exhaustive:") for n in [6, 8, 10]: r = injectivity_exhaustive(n) print(f" n={n:>2d} {r['K']:>5d} trees min_d={r['min_d']:.6f} " f"distinct={r['distinct']}") print() print(" Sampled:") for n, K in [(16, 500), (24, 500), (32, 500)]: r = injectivity_sampled(n, K) print(f" n={n:>2d} {r['K']:>5d} trees min_d={r['min_d']:.6f} " f"distinct={r['distinct']}") print() def demo_mitm(): print("=" * 72) print("Demo 4 — MITM analysis (correction to #100)") print("=" * 72) print() print(f" {'n':>3s} {'brute':>12s} {'mitm':>10s} {'saved':>8s}") print(" " + "-" * 40) for n in [8, 10, 12, 14, 16, 18, 20]: r = mitm_analysis(n) print(f" {n:>3d} {r['brute_force']:>12d} {r['mitm_cost']:>10d} " f"{r['bits_saved']:>8.2f}") print() def demo_moufang(): print("=" * 72) print("Demo 5 — algebraic properties (correction to #100)") print("=" * 72) print() alt = check_alternativity() mouf = check_moufang() print(f" left alternativity: {alt['left_alt_err']:.2e}") print(f" right alternativity: {alt['right_alt_err']:.2e}") print(f" left Moufang: {mouf['left_moufang_err']:.2e}") print(f" right Moufang: {mouf['right_moufang_err']:.2e} " f"(#100 said this fails)") print(f" middle Moufang: {mouf['mid_moufang_err']:.2e}") print() def demo_norm(): print("=" * 72) print("Demo 6 — norm preservation") print("=" * 72) print() norm_results = check_norm_preservation() for n, nm in norm_results: print(f" n={n:>5d} ||product|| = {nm:.15f} " f"deviation = {abs(nm - 1.0):.2e}") print() def demo_noise(): print("=" * 72) print("Demo 7 — noise sensitivity (correction to #100)") print("=" * 72) print() for n, sigma in [(8, 0.01), (16, 0.01), (32, 0.01)]: r = noise_test(n=n, sigma=sigma, seed=n) print(f" n={n:>3d} sigma={sigma:.3f} " f"mean_err={r['mean_err']:.5f} " f"sqrt(n)*sigma={r['sqrt_n_sigma']:.5f} " f"ratio={r['ratio_to_sqrt_n_sigma']:.3f}") print() def demo_structured(): print("=" * 72) print("Demo 8 — structured inputs break injectivity") print("=" * 72) print() s = structured_input_test(n=8, K=200) for name, vals in s.items(): print(f" {name:<14s} min_d={vals['min_d']:.6f} " f"mean_d={vals['mean_d']:.4f}") print() def demo_notarize(): print("=" * 72) print("Demo 9 — notarization (proof of computation)") print("=" * 72) print() rng = np.random.default_rng(0) n = 8 operands = np.stack([orandom(rng) for _ in range(n)]) tree = random_tree(n, rng) proof = notarize(operands, tree, withhold_bits=32) print(f" Published operands (n={n}), product, commitment = " f"{proof['commitment']}") print(f" Tree withheld. Verify by guessing the tree:") # Verifier guesses a random tree guess_wrong = random_tree(n, rng) guess_right = tree print(f" guess (random): {proof['verify'](guess_wrong)}") print(f" guess (correct): {proof['verify'](guess_right)}") print() def demo_hierarchical_key(): print("=" * 72) print("Demo 10 — hierarchical key derivation") print("=" * 72) print() rng = np.random.default_rng(0) k, m = 4, 3 n = k * m operands = np.stack([orandom(rng) for _ in range(n)]) sub_shapes = unique_random_trees(k, 5, rng) top_shapes = unique_random_trees(m, 5, rng) print(f" k={k}, m={m}, key space = Cat({k-1})^{m} * Cat({m-1}) = " f"{hierarchical_injectivity(k, m, 1)['total_programs']}") print(f" Sample keys:") seen = set() for _ in range(5): top = top_shapes[int(rng.integers(0, len(top_shapes)))] subs = [sub_shapes[int(rng.integers(0, len(sub_shapes)))] for _ in range(m)] key = hierarchical_key(operands, top, subs, k) key_hash = hash(round(float(key.sum()), 8)) print(f" key = {np.round(key[:4], 4)}... hash = " f"{key_hash % 10000:04d}") print() def main(): demo_single_tree() demo_batch() demo_injectivity() demo_mitm() demo_moufang() demo_norm() demo_noise() demo_structured() demo_notarize() demo_hierarchical_key() if __name__ == "__main__": main()