KroneckerRules := module()
option package;
export Kron, `&x`, Simplify, Expand, Shuffle, Check, VisualCheck, Verify, VisualVerify, NumericEvaluate, Info, SimplifyTrace;
# ---------- Inert notation ----------
Kron := (A,B) -> 'KroneckerProduct'(A,B):
`&x` := (A,B) -> 'KroneckerProduct'(A,B):
# ---------- Trace mechanism ----------
local _trace_steps := []:
local _AddTrace := proc(rule, from_expr, to_expr)
_trace_steps := [op(_trace_steps),
sprintf("%s: **%a** -> **%a**", rule, from_expr, to_expr)];
end proc;
# ---------- Perfect shuffle matrix ----------
local _PerfectShuffleMatrix := proc(p::posint, r::posint)
local M, i, j;
M := Matrix(p*r, p*r, 0);
for i from 1 to p do
for j from 1 to r do
M[(j-1)*p + i, (i-1)*r + j] := 1;
end do;
end do;
return M;
end proc;
# ---------- Helper: flatten a dot product into a list of operands ----------
local _FlattenOperands := proc(p)
local terms, rec;
terms := [];
rec := proc(x)
if type(x, `.`) then
rec(op(1,x));
rec(op(2,x));
else
terms := [op(terms), x];
end if;
end proc;
rec(p);
return terms;
end proc;
# ---------- Distribute dot over sums (recursively, fully) ----------
local _DistributeDot := proc(expr, with_trace::boolean := false)
local e, changed, term;
e := expr;
changed := true;
while changed do
changed := false;
e := subsindets(e, `.`, proc(p)
local a,b,terms,res;
a := op(1,p); b := op(2,p);
if type(a, `+`) then
changed := true;
terms := [op(a)];
res := `+`(seq(term . b, term in terms));
if with_trace then _AddTrace("Dot left-distribution", p, res); end if;
return res;
elif type(b, `+`) then
changed := true;
terms := [op(b)];
res := `+`(seq(a . term, term in terms));
if with_trace then _AddTrace("Dot right-distribution", p, res); end if;
return res;
else
return p;
end if;
end proc);
end do;
return e;
end proc;
# ---------- Combine consecutive Kronecker products in a dot chain (full flatten) ----------
local _FlattenChain := proc(expr, with_trace::boolean := false)
local e, changed;
e := expr;
changed := true;
while changed do
changed := false;
e := subsindets(e, `.`, proc(p)
local ops, all_kron, left, right, i, to_pair;
ops := [op(p)];
all_kron := true;
for i in ops do
if not type(i, specfunc(KroneckerProduct)) then
all_kron := false;
break;
end if;
end do;
if all_kron then
left := op(1, ops[1]);
right := op(2, ops[1]);
for i from 2 to nops(ops) do
left := left . op(1, ops[i]);
right := right . op(2, ops[i]);
end do;
to_pair := Kron(left, right);
if with_trace then _AddTrace("KRON7 (chain)", p, to_pair); end if;
changed := true;
return to_pair;
else
return p;
end if;
end proc);
end do;
return e;
end proc;
# ---------- Core simplification rules (without trace) ----------
local ApplyRulesNoTrace := proc(expr)
local e, e_old, changed;
local sub, a, b, terms, term, new_sub;
e := expr;
changed := true;
while changed do
e_old := e;
e := _DistributeDot(e, false);
e := _FlattenChain(e, false);
if e = e_old then changed := false; end if;
end do;
for sub in indets(e, specfunc(Transpose)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Transpose(term), term in terms));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(HermitianTranspose)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(HermitianTranspose(term), term in terms));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(conjugate)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(conjugate(term), term in terms));
e := subs(sub = new_sub, e);
elif type(a, `.`) then
new_sub := conjugate(op(1,a)) . conjugate(op(2,a));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Determinant)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Determinant(op(1,a))^(dim(op(2,a))) * Determinant(op(2,a))^(dim(op(1,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
new_sub := sub;
if type(a, `*`) and type(op(1,a), numeric) then
new_sub := op(1,a) * Kron(op(2,a), b);
elif type(b, `*`) and type(op(1,b), numeric) then
new_sub := op(1,b) * Kron(a, op(2,b));
end if;
if new_sub <> sub then e := subs(sub = new_sub, e); end if;
end do;
for sub in indets(e, specfunc(Transpose)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(Transpose(op(1,a)), Transpose(op(2,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(HermitianTranspose)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(HermitianTranspose(op(1,a)), HermitianTranspose(op(2,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(conjugate)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(conjugate(op(1,a)), conjugate(op(2,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(op(1,a), Kron(op(2,a), b));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Kron(term, b), term in terms));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(b, `+`) then
terms := [op(b)];
new_sub := `+`(seq(Kron(a, term), term in terms));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Trace)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Trace(op(1,a)) * Trace(op(2,a));
e := subs(sub = new_sub, e);
elif type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Trace(term), term in terms));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(MatrixInverse)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(MatrixInverse(op(1,a)), MatrixInverse(op(2,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(MatrixInverse)) do
a := op(1,sub);
if type(a, specfunc(Shuffle)) then
new_sub := Shuffle(MatrixInverse(op(1,a)), MatrixInverse(op(2,a)));
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Trace)) do
a := op(1,sub);
if type(a, specfunc(Shuffle)) then
new_sub := Trace(op(1,a)) * Trace(op(2,a));
e := subs(sub = new_sub, e);
end if;
end do;
return e;
end proc;
# ---------- Same with trace (for SimplifyTrace) ----------
local ApplyRulesWithTrace := proc(expr)
local e, e_old, changed;
local sub, a, b, terms, term, new_sub;
e := expr;
changed := true;
while changed do
e_old := e;
e := _DistributeDot(e, true);
e := _FlattenChain(e, true);
if e = e_old then changed := false; end if;
end do;
for sub in indets(e, specfunc(Transpose)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Transpose(term), term in terms));
_AddTrace("Transpose over sum", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(HermitianTranspose)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(HermitianTranspose(term), term in terms));
_AddTrace("HermitianTranspose over sum", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(conjugate)) do
a := op(1,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(conjugate(term), term in terms));
_AddTrace("conjugate over sum", sub, new_sub);
e := subs(sub = new_sub, e);
elif type(a, `.`) then
new_sub := conjugate(op(1,a)) . conjugate(op(2,a));
_AddTrace("conjugate over product", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Determinant)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Determinant(op(1,a))^(dim(op(2,a))) * Determinant(op(2,a))^(dim(op(1,a)));
_AddTrace("KRON9", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
new_sub := sub;
if type(a, `*`) and type(op(1,a), numeric) then
new_sub := op(1,a) * Kron(op(2,a), b);
_AddTrace("KRON1 (scalar left)", sub, new_sub);
elif type(b, `*`) and type(op(1,b), numeric) then
new_sub := op(1,b) * Kron(a, op(2,b));
_AddTrace("KRON1 (scalar right)", sub, new_sub);
end if;
if new_sub <> sub then e := subs(sub = new_sub, e); end if;
end do;
for sub in indets(e, specfunc(Transpose)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(Transpose(op(1,a)), Transpose(op(2,a)));
_AddTrace("KRON2", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(HermitianTranspose)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(HermitianTranspose(op(1,a)), HermitianTranspose(op(2,a)));
_AddTrace("KRON3", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(conjugate)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(conjugate(op(1,a)), conjugate(op(2,a)));
_AddTrace("conjugate over KroneckerProduct", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(op(1,a), Kron(op(2,a), b));
_AddTrace("KRON4", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Kron(term, b), term in terms));
_AddTrace("KRON5", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(KroneckerProduct)) do
a := op(1,sub); b := op(2,sub);
if type(b, `+`) then
terms := [op(b)];
new_sub := `+`(seq(Kron(a, term), term in terms));
_AddTrace("KRON6", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Trace)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Trace(op(1,a)) * Trace(op(2,a));
_AddTrace("KRON8", sub, new_sub);
e := subs(sub = new_sub, e);
elif type(a, `+`) then
terms := [op(a)];
new_sub := `+`(seq(Trace(term), term in terms));
_AddTrace("Trace linearity", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(MatrixInverse)) do
a := op(1,sub);
if type(a, specfunc(KroneckerProduct)) then
new_sub := Kron(MatrixInverse(op(1,a)), MatrixInverse(op(2,a)));
_AddTrace("KRON10", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(MatrixInverse)) do
a := op(1,sub);
if type(a, specfunc(Shuffle)) then
new_sub := Shuffle(MatrixInverse(op(1,a)), MatrixInverse(op(2,a)));
_AddTrace("Inverse of Shuffle", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
for sub in indets(e, specfunc(Trace)) do
a := op(1,sub);
if type(a, specfunc(Shuffle)) then
new_sub := Trace(op(1,a)) * Trace(op(2,a));
_AddTrace("Trace(Shuffle)", sub, new_sub);
e := subs(sub = new_sub, e);
end if;
end do;
return e;
end proc;
# ---------- Simplify (without trace) ----------
Simplify := proc(expr, maxiter::integer := 50)
local new, old, i;
new := expr;
for i from 1 to maxiter do
old := new;
new := ApplyRulesNoTrace(new);
if new = old then break; end if;
end do;
return new;
end proc;
# ---------- SimplifyTrace (step-by-step output) ----------
SimplifyTrace := proc(expr, maxiter::integer := 50)
local new, old, i, step, cnt;
_trace_steps := [];
new := expr;
printf("Start expression: %a\n\n", new);
for i from 1 to maxiter do
old := new;
_trace_steps := [];
new := ApplyRulesWithTrace(new);
if new = old then
printf("No more rules apply after iteration %d.\n", i-1);
break;
end if;
printf("Iteration %d:\n", i);
cnt := 0;
for step in _trace_steps do
cnt := cnt + 1;
printf(" %d. %s\n", cnt, step);
end do;
printf("Result after iteration %d: %a\n\n", i, new);
end do;
printf("--- Final simplified expression ---\n");
return new;
end proc;
# ---------- Expand ----------
local _ExpandKron := proc(expr)
local a, b, new_a, new_b, term;
if not type(expr, specfunc(KroneckerProduct)) then
return expr;
end if;
a := op(1, expr);
b := op(2, expr);
new_a := _ExpandKron(a);
new_b := _ExpandKron(b);
if type(new_a, `+`) then
return `+`(seq(Kron(term, new_b), term in [op(new_a)]));
elif type(new_b, `+`) then
return `+`(seq(Kron(new_a, term), term in [op(new_b)]));
else
return Kron(new_a, new_b);
end if;
end proc;
Expand := proc(expr)
local e, new_e;
e := expr;
while true do
new_e := subsindets(e, specfunc(KroneckerProduct), _ExpandKron);
if new_e = e then break; end if;
e := new_e;
end do;
return e;
end proc;
# ---------- Shuffle (KRON11) ----------
Shuffle := proc(A, B)
local p, q, r, s, S1, S2;
try
p := LinearAlgebra:-RowDimension(A);
q := LinearAlgebra:-ColumnDimension(A);
r := LinearAlgebra:-RowDimension(B);
s := LinearAlgebra:-ColumnDimension(B);
catch:
return 'Shuffle'(A, B);
end try;
if not (type(p, posint) and type(q, posint) and type(r, posint) and type(s, posint)) then
return 'Shuffle'(A, B);
end if;
S1 := _PerfectShuffleMatrix(p, r);
S2 := _PerfectShuffleMatrix(q, s);
return S1 . LinearAlgebra:-KroneckerProduct(A, B) . LinearAlgebra:-Transpose(S2);
end proc;
# ---------- Activation for numeric evaluation (used in Check/VisualCheck) ----------
local Activate := proc(expr)
subs([ 'Determinant' = LinearAlgebra:-Determinant,
'Trace' = LinearAlgebra:-Trace,
'MatrixInverse' = LinearAlgebra:-MatrixInverse,
'KroneckerProduct' = LinearAlgebra:-KroneckerProduct,
'Transpose' = LinearAlgebra:-Transpose,
'HermitianTranspose' = LinearAlgebra:-HermitianTranspose,
dim = LinearAlgebra:-RowDimension
], expr);
end proc;
# ---------- Check (symbolic test, square matrices only) ----------
Check := proc(expr::uneval, {n::posint := 2, samples::posint := 5})
printf("Check: Use inert Kron(A,B) or A &x B. Avoid direct LinearAlgebra:-KroneckerProduct.\n");
printf(" Note: This command assumes all matrix symbols are SQUARE. For non‑square matrices, use Simplify and Verify.\n");
local e, syms, i, subs_set, gen, simp_expr, val_simp, orig_expr, val_orig, ok;
e := eval(expr);
syms := indets(e, symbol) minus {Kron, Determinant, Trace, MatrixInverse, KroneckerProduct,
'Kron', 'Determinant', 'Trace', 'MatrixInverse', 'KroneckerProduct',
dim, 'dim', `&x`, Shuffle, Expand, Transpose, HermitianTranspose, conjugate};
if syms = {} then error "No symbolic variables found. Use symbolic names like A,B,..."; end if;
gen := proc() local M; do M := LinearAlgebra:-RandomMatrix(n, n, generator=-5..5); until LinearAlgebra:-Determinant(M) <> 0; return M; end proc;
ok := true;
for i from 1 to samples do
subs_set := map(s -> s = gen(), syms);
simp_expr := Simplify(e);
simp_expr := Activate(simp_expr);
val_simp := eval(eval(simp_expr, subs_set));
orig_expr := subs('Kron' = KroneckerProduct, `&x` = KroneckerProduct, e);
orig_expr := Activate(orig_expr);
val_orig := eval(eval(orig_expr, subs_set));
if type(val_orig, Matrix) and val_simp = 0 then
if LinearAlgebra:-Norm(val_orig, infinity) = 0 then
# ok
else
ok := false; break;
end if;
elif type(val_simp, Matrix) and val_orig = 0 then
if LinearAlgebra:-Norm(val_simp, infinity) = 0 then
# ok
else
ok := false; break;
end if;
elif type(val_orig, Matrix) and type(val_simp, Matrix) then
if not LinearAlgebra:-Equal(val_orig, val_simp) then ok := false; break; end if;
else
if evalb(val_orig <> val_simp) then ok := false; break; end if;
end if;
end do;
return ok;
end proc;
# ---------- VisualCheck (same as Check, shows matrices) ----------
VisualCheck := proc(expr::uneval, {n::posint := 2})
printf("VisualCheck: Use inert Kron(A,B) or A &x B. Avoid direct LinearAlgebra:-KroneckerProduct.\n");
printf(" Note: This command assumes all matrix symbols are SQUARE. For non‑square matrices, use Simplify and Verify.\n");
local e, syms, subs_set, gen, simp_expr, val_simp, orig_expr, val_orig, a;
e := eval(expr);
syms := indets(e, symbol) minus {Kron, Determinant, Trace, MatrixInverse, KroneckerProduct,
'Kron', 'Determinant', 'Trace', 'MatrixInverse', 'KroneckerProduct',
dim, 'dim', `&x`, Shuffle, Expand, Transpose, HermitianTranspose, conjugate};
if syms = {} then error "No symbolic variables found. Use symbolic names like A,B,..."; end if;
gen := proc() local M; do M := LinearAlgebra:-RandomMatrix(n, n, generator=-5..5); until LinearAlgebra:-Determinant(M) <> 0; return M; end proc;
subs_set := map(s -> s = gen(), syms);
printf("Substitutions used:\n");
for a in subs_set do printf("%a\n", a); end do;
printf("\n");
simp_expr := Simplify(e);
simp_expr := Activate(simp_expr);
val_simp := eval(eval(simp_expr, subs_set));
orig_expr := subs('Kron' = KroneckerProduct, `&x` = KroneckerProduct, e);
orig_expr := Activate(orig_expr);
val_orig := eval(eval(orig_expr, subs_set));
printf("========== Direct calculation ==========\n");
print(val_orig);
printf("\n========== Simplified calculation (via module) ==========\n");
print(val_simp);
printf("\n");
local identical;
if type(val_orig, Matrix) and val_simp = 0 then
identical := LinearAlgebra:-Norm(val_orig, infinity) = 0;
elif type(val_simp, Matrix) and val_orig = 0 then
identical := LinearAlgebra:-Norm(val_simp, infinity) = 0;
elif type(val_orig, Matrix) and type(val_simp, Matrix) then
identical := LinearAlgebra:-Equal(val_orig, val_simp);
else
identical := evalb(val_orig = val_simp);
end if;
if identical then
printf("Results are IDENTICAL (visual proof).\n");
return true;
else
printf("ERROR – results differ.\n");
return false;
end if;
end proc;
# ---------- Verify (for concrete matrices, returns boolean) ----------
Verify := proc(expr)
printf("Verify: Comparing Simplify(expr) with direct evaluation using concrete matrices.\n");
local simp, direct;
simp := Simplify(expr);
simp := Activate(simp);
direct := subs(`&x` = LinearAlgebra:-KroneckerProduct, expr);
direct := Activate(direct);
if type(simp, Matrix) and type(direct, Matrix) then
if LinearAlgebra:-Equal(simp, direct) then
printf("Results are IDENTICAL.\n");
return true;
else
printf("ERROR – results differ.\n");
return false;
end if;
else
if evalb(simp = direct) then
printf("Results are IDENTICAL.\n");
return true;
else
printf("ERROR – results differ.\n");
return false;
end if;
end if;
end proc;
# ---------- VisualVerify (visual comparison for concrete matrices) ----------
VisualVerify := proc(expr)
printf("VisualVerify: Comparing Simplify(expr) with direct evaluation using concrete matrices.\n");
local simp, direct;
simp := Simplify(expr);
simp := Activate(simp);
direct := subs(`&x` = LinearAlgebra:-KroneckerProduct, expr);
direct := Activate(direct);
printf("========== Direct calculation ==========\n");
print(direct);
printf("\n========== Simplified calculation (via module) ==========\n");
print(simp);
printf("\n");
if type(simp, Matrix) and type(direct, Matrix) then
if LinearAlgebra:-Equal(simp, direct) then
printf("Results are IDENTICAL (visual proof).\n");
return true;
else
printf("ERROR – results differ.\n");
return false;
end if;
else
if evalb(simp = direct) then
printf("Results are IDENTICAL (visual proof).\n");
return true;
else
printf("ERROR – results differ.\n");
return false;
end if;
end if;
end proc;
# ---------- NumericEvaluate (robust numeric evaluation for concrete matrices) ----------
NumericEvaluate := proc(expr)
local e;
# First replace all non-Kronecker functions that can appear inside KroneckerProduct arguments
# Order: innermost first
e := expr;
e := subsindets(e, specfunc(MatrixInverse), m -> LinearAlgebra:-MatrixInverse(op(m)));
e := subsindets(e, specfunc(Transpose), t -> LinearAlgebra:-Transpose(op(t)));
e := subsindets(e, specfunc(HermitianTranspose), h -> LinearAlgebra:-HermitianTranspose(op(h)));
e := subsindets(e, specfunc(conjugate), c -> conjugate(op(c)));
e := subsindets(e, specfunc(Shuffle), s -> LinearAlgebra:-KroneckerProduct(op(2,s), op(1,s)));
e := subsindets(e, specfunc(dim), d -> LinearAlgebra:-RowDimension(op(d)));
e := subsindets(e, specfunc(KroneckerProduct), k -> LinearAlgebra:-KroneckerProduct(op(k)));
e := subsindets(e, specfunc(Determinant), d -> LinearAlgebra:-Determinant(op(d)));
e := subsindets(e, specfunc(Trace), t -> LinearAlgebra:-Trace(op(t)));
# Now evaluate the whole expression (including sums)
return eval(e);
end proc;
# ---------- Info (educational) ----------
Info := proc()
printf("================================================================\n");
printf("KRONECKERRULES PACKAGE - COMPREHENSIVE EDUCATIONAL GUIDE\n");
printf("================================================================\n\n");
printf("1. PURPOSE\n");
printf(" Simplify symbolic expressions involving Kronecker products,\n");
printf(" matrix multiplication, determinants, traces, inverses, transposes,\n");
printf(" conjugates, and shuffling.\n\n");
printf("2. INERT NOTATION (to avoid immediate evaluation)\n");
printf(" Use either: Kron(A,B) or A &x B\n");
printf(" Both produce an inert 'KroneckerProduct(A,B)' that the module can simplify.\n");
printf(" Avoid using LinearAlgebra:-KroneckerProduct directly in expressions\n");
printf(" you want to simplify symbolically.\n\n");
printf("3. MAIN COMMANDS\n");
printf(" Simplify(expr) - apply all algebraic rules (KRON1‑KRON11) iteratively.\n");
printf(" Expand(expr) - fully distribute Kronecker products over sums.\n");
printf(" Shuffle(A,B) - implement perfect shuffle: B ⊗ A = S (A⊗B) S^T.\n");
printf(" Check(expr) - test symbolic identities using random square matrices.\n");
printf(" VisualCheck(expr) - same as Check but shows random matrices and results.\n");
printf(" Verify(expr) - compare Simplify(expr) with direct evaluation for user‑supplied concrete matrices.\n");
printf(" VisualVerify(expr) - same as Verify but displays both results (for any matrix dimensions).\n");
printf(" NumericEvaluate(expr) - directly evaluate concrete expressions to numeric matrices (robust).\n");
printf(" SimplifyTrace(expr) - same as Simplify but prints each applied rule (with markers).\n");
printf(" Info() - this help text.\n\n");
printf("4. CHECK AND VISUALCHECK – VERIFY SYMBOLIC IDENTITIES (SQUARE MATRICES ONLY)\n");
printf(" These commands generate random square matrices (size n x n) for each symbol.\n");
printf(" They are useful for testing algebraic identities, but assume all matrices are square.\n");
printf(" For non‑square matrices, use Simplify and test with your own concrete matrices via Verify or VisualVerify.\n\n");
printf("5. NUMERIC EVALUATION (FOR CONCRETE MATRICES)\n");
printf(" NumericEvaluate(expr) is the recommended way to compute a numeric result from an expression\n");
printf(" built with &x, Shuffle, Transpose, etc., after you have assigned concrete matrices.\n");
printf(" It replaces all inert functions in the correct order and returns the final matrix or scalar.\n\n");
printf("6. VERIFY AND VISUALVERIFY – FOR CONCRETE MATRICES (ANY DIMENSIONS)\n");
printf(" Verify(expr) returns true/false after comparing simplified and direct results.\n");
printf(" VisualVerify(expr) shows the direct and simplified results for visual inspection.\n");
printf(" Use these after you have defined your matrices (e.g., A := Matrix(...)).\n\n");
printf("7. SIMPLIFYTRACE – EDUCATIONAL STEP-BY-STEP OUTPUT\n");
printf(" SimplifyTrace(expr) works like Simplify but prints each rule application.\n");
printf(" The changing subexpression is marked with ** **. Iterations and steps are numbered.\n");
printf(" This helps you understand how the simplification proceeds.\n\n");
printf("8. MATRIX DIMENSION REQUIREMENTS\n");
printf(" - Kronecker product works for any matrices (even non‑square).\n");
printf(" - Ordinary multiplication (A . B), determinant, trace, inverse require square matrices.\n");
printf(" - For determinant rule, dim(X) returns the row dimension (assumed square).\n");
printf(" - Shuffle requires known numeric dimensions; otherwise returns inert.\n\n");
printf("9. LIST OF SIMPLIFICATION RULES (KRON1‑KRON11)\n");
printf(" KRON1: (cA)⊗B = A⊗(cB) = c (A⊗B)\n");
printf(" KRON2: (A⊗B)^T = A^T ⊗ B^T\n");
printf(" KRON3: (A⊗B)^H = A^H ⊗ B^H\n");
printf(" KRON4: (A⊗B)⊗C = A⊗(B⊗C)\n");
printf(" KRON5: (A+B)⊗C = A⊗C + B⊗C\n");
printf(" KRON6: A⊗(B+C) = A⊗B + A⊗C\n");
printf(" KRON7: (A⊗B)(C⊗D) = (A·C)⊗(B·D)\n");
printf(" KRON8: trace(A⊗B) = trace(A)·trace(B), linear over sums\n");
printf(" KRON9: det(A⊗B) = det(A)^(dim B) · det(B)^(dim A)\n");
printf(" KRON10: (A⊗B)^(-1) = A^(-1) ⊗ B^(-1)\n");
printf(" KRON11: Shuffle(A,B) = S (A⊗B) S^T gives B⊗A.\n");
printf(" Extra: transpose, conjugate, Hermitian transpose distribute over sums and products.\n\n");
printf("10. NOTES AND LIMITATIONS\n");
printf(" - No automatic dimension checking; ensure compatibility.\n");
printf(" - Shuffle requires known dimensions.\n");
printf(" - Avoid using LinearAlgebra:-KroneckerProduct directly.\n");
printf(" - Check and VisualCheck are only for symbolic square matrices; for concrete or non‑square, use NumericEvaluate or Verify/VisualVerify.\n");
printf("================================================================\n");
end proc;
end module:
with(KroneckerRules):
Info();
MAIN COMMANDS
Simplify(expr) - apply all algebraic rules (KRON1‑KRON11) iteratively.
Expand(expr) - fully distribute Kronecker products over sums.
Shuffle(A,B) - implement perfect shuffle: B ⊗ A = S (A⊗B) S^T.
Check(expr) - test symbolic identities using random square matrices.
VisualCheck(expr) - same as Check but shows random matrices and results.
Verify(expr) - compare Simplify(expr) with direct evaluation for user‑supplied concrete matrices.
VisualVerify(expr) - same as Verify but displays both results (for any matrix dimensions).
NumericEvaluate(expr) - directly evaluate concrete expressions to numeric matrices (robust).
SimplifyTrace(expr) - same as Simplify but prints each applied rule (with markers).
Info() - this help text.

#restart;
with(KroneckerRules):
# Symbolic matrices (square)
A := 'A': B := 'B': C := 'C': X := 'X': Y := 'Y': Z := 'Z':
# Complex expression involving several rules
expr := (2*A &x B) . (C &x X) # KRON1 (scalar) + KRON7
+ (A+B) &x (C+X) # KRON5 and KRON6 (distribution)
- Transpose( (A &x B) ) # KRON2
+ conjugate( (A &x B) . (C &x X) ) # conjugate of product
+ Determinant( A &x B ) # KRON9 ######################## vierkant
- Trace( Shuffle(A, B) ) # KRON11 + KRON8################# vierkant
+ MatrixInverse( A &x B ) . (C &x X); # KRON10 + KRON7 ############### vierkant
# Simplify using all rules
simp := Simplify(expr);
# Display the simplified symbolic expression
simp;
expr := 2 (KroneckerProduct(A, B)) . (KroneckerProduct(C, X))
+ KroneckerProduct(A + B, C + X)
- Transpose(KroneckerProduct(A, B))
___________________________________________________
+ (KroneckerProduct(A, B)) . (KroneckerProduct(C, X))
+ Determinant(KroneckerProduct(A, B)) - Trace(Shuffle(A, B)) +
(MatrixInverse(KroneckerProduct(A, B))) . (KroneckerProduct(C, X
))
simp := 2 KroneckerProduct(A . C, B . X) + KroneckerProduct(A, C)
+ KroneckerProduct(A, X) + KroneckerProduct(B, C)
+ KroneckerProduct(B, X)
- KroneckerProduct(Transpose(A), Transpose(B))
//_\ /_\ /_\ /_\\
+ KroneckerProduct\\A/ . \C/, \B/ . \X//
dim(B) dim(A)
+ Determinant(A) Determinant(B)
- Trace(A) Trace(B)
+ KroneckerProduct((MatrixInverse(A)) . C,
(MatrixInverse(B)) . X)
2 KroneckerProduct(A . C, B . X) + KroneckerProduct(A, C)
+ KroneckerProduct(A, X) + KroneckerProduct(B, C)
+ KroneckerProduct(B, X)
- KroneckerProduct(Transpose(A), Transpose(B))
//_\ /_\ /_\ /_\\
+ KroneckerProduct\\A/ . \C/, \B/ . \X//
dim(B) dim(A)
+ Determinant(A) Determinant(B)
- Trace(A) Trace(B)
+ KroneckerProduct((MatrixInverse(A)) . C,
(MatrixInverse(B)) . X)
# Assign concrete matrices (all 2x2, invertible)
A := Matrix([[1,2],[3,4]]);
B := Matrix([[5,6],[7,8]]);
C := Matrix([[1,0],[0,1]]);
X := Matrix([[2,0],[0,2]]);
# Use the simplified expression or the original one
result := NumericEvaluate(simp); # or NumericEvaluate(expr)
#print(result);
[1 2]
A := [ ]
[3 4]
[5 6]
B := [ ]
[7 8]
[1 0]
C := [ ]
[0 1]
[2 0]
X := [ ]
[0 2]
[10 17 61 57 ]
[ ]
[22 19 73 91 ]
[ ]
result := [98 103 91 113]
[ ]
[249 301 281 299]
[--- --- --- ---]
[ 2 2 2 2 ]