| > |
KroneckerRules := module()
option package;
export Kron, `&x`, Simplify, Expand, Shuffle, Check, VisualCheck, Verify, VisualVerify, NumericEvaluate, Info, SimplifyTrace, ToggleKronNotation;
# ---------- Inert notation ----------
Kron := (A,B) -> 'KroneckerProduct'(A,B):
`&x` := (A,B) -> 'KroneckerProduct'(A,B):
# ---------- Trace mechanism (with improved rule descriptions) ----------
local _trace_steps := [];
# Table with mathematical descriptions of each rule
local _rule_desc := table([
"KRON7 (chain)" = "(A5B)·(C5D) → (A·C)5(B·D)",
"Dot left-distribution" = "X·(Y+Z) → X·Y + X·Z",
"Dot right-distribution" = "(X+Y)·Z → X·Z + Y·Z",
"KRON5" = "(X+Y)5Z → X5Z + Y5Z",
"KRON6" = "X5(Y+Z) → X5Y + X5Z",
"KRON1 (scalar left)" = "(c·A)5B → c·(A5B)",
"KRON1 (scalar right)" = "A5(c·B) → c·(A5B)",
"KRON2" = "(A5B)^T → A^T 5 B^T",
"KRON3" = "(A5B)^H → A^H 5 B^H",
"KRON4" = "(A5B)5C → A5(B5C)",
"KRON8" = "Tr(A5B) → Tr(A)·Tr(B)",
"KRON9" = "det(A5B) → det(A)^(dim B) · det(B)^(dim A)",
"KRON10" = "(A5B)^(-1) → A^(-1) 5 B^(-1)",
"Transpose over sum" = "(A+B)^T → A^T + B^T",
"HermitianTranspose over sum" = "(A+B)^H → A^H + B^H",
"conjugate over sum" = "conjugate(A+B) → conjugate(A)+conjugate(B)",
"conjugate over product" = "conjugate(A·B) → conjugate(A)·conjugate(B)",
"Trace linearity" = "Tr(A+B) → Tr(A)+Tr(B)",
"Inverse of Shuffle" = "Shuffle(A,B)^(-1) → Shuffle(A^(-1), B^(-1))",
"Trace(Shuffle)" = "Tr(Shuffle(A,B)) → Tr(A)·Tr(B)"
]);
local _AddTrace := proc(rule, from_expr, to_expr)
local desc;
desc := _rule_desc[rule];
if desc = NULL then desc := rule; end if;
_trace_steps := [op(_trace_steps),
sprintf("%s (%s): %a -> %a", rule, desc, 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, outputstyle::string := "default")
local new, old, i, format_expr;
new := expr;
for i from 1 to maxiter do
old := new;
new := ApplyRulesNoTrace(new);
if new = old then break; end if;
end do;
format_expr := proc(e)
if outputstyle = "Kron" then
return subsindets(e, specfunc(KroneckerProduct),
proc(k) 'Kron'(op(k)); end proc);
else
return e;
end if;
end proc;
return format_expr(new);
end proc;
# ---------- SimplifyTrace (step-by-step output with optional outputstyle) ----------
SimplifyTrace := proc(expr, maxiter::integer := 50, outputstyle::string := "default")
local new, old, i, step, cnt, format_expr;
_trace_steps := [];
new := expr;
format_expr := proc(e)
if outputstyle = "Kron" then
return subsindets(e, specfunc(KroneckerProduct),
proc(k) 'Kron'(op(k)); end proc);
else
return e;
end if;
end proc;
printf("Start expression: %a\n\n", format_expr(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;
if outputstyle = "Kron" then
step := StringTools:-SubstituteAll(step, "KroneckerProduct", "Kron");
end if;
printf(" %d. %s\n", cnt, step);
end do;
printf("Result after iteration %d: %a\n\n", i, format_expr(new));
end do;
printf("--- Final simplified expression ---\n");
return ifelse(outputstyle = "Kron", format_expr(new), new);
end proc;
# ---------- Toggle GLOBAL output notation of KroneckerProduct ----------
ToggleKronNotation := proc()
global `print/KroneckerProduct`;
if assigned(`print/KroneckerProduct`) then
unassign(`print/KroneckerProduct`);
printf("KroneckerProduct output notation set to DEFAULT (KroneckerProduct(A,B))\n");
else
`print/KroneckerProduct` := (A,B) -> 'Kron'(A,B);
printf("KroneckerProduct output notation set to PRETTY (Kron(A,B))\n");
end if;
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;
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)));
return eval(e);
end proc;
# ---------- Info (educational) ----------
Info := proc()
printf("================================================================\n");
printf("KRONECKERRULES PACKAGE – USER GUIDE\n");
printf("================================================================\n\n");
printf("1. PURPOSE\n");
printf(" Simplify symbolic expressions involving Kronecker products (5),\n");
printf(" matrix multiplication (.), addition (+), subtraction (-), transpose (^T),\n");
printf(" Hermitian transpose (^H), inverse, determinant, trace, and shuffle.\n\n");
printf("2. INERT NOTATION (prevents immediate evaluation)\n");
printf(" - Kron(A,B) or A &x B\n");
printf(" Both produce an inert 'KroneckerProduct(A,B)' for symbolic simplification.\n");
printf(" Avoid using LinearAlgebra:-KroneckerProduct directly.\n\n");
printf("3. MAIN COMMANDS\n");
printf(" Simplify(expr) – apply all rules iteratively (KroneckerProduct output).\n");
printf(" Simplify(expr, \"Kron\") – same, but output uses short Kron notation.\n");
printf(" Simplify(expr, maxiter) – set maximum number of iterations (default 50).\n");
printf(" Simplify(expr, maxiter, \"Kron\") – both maxiter and short notation.\n\n");
printf(" SimplifyTrace(expr) – show steps (KroneckerProduct notation).\n");
printf(" SimplifyTrace(expr, \"Kron\") – show steps using short Kron notation.\n");
printf(" SimplifyTrace(expr, maxiter) – set iterations.\n");
printf(" SimplifyTrace(expr, maxiter, \"Kron\") – both.\n\n");
printf(" Expand(expr) – fully distribute 5 over sums.\n");
printf(" Shuffle(A,B) – realize B 5 A = S·(A5B)·S^T (perfect shuffle).\n");
printf(" ToggleKronNotation() – switch global output between Kron and KroneckerProduct.\n\n");
printf("4. VERIFICATION OF IDENTITIES\n");
printf(" Check(expr) – test numerically (random SQUARE matrices).\n");
printf(" VisualCheck(expr) – same, but shows matrices and results.\n");
printf(" Verify(expr) – compare Simplify(expr) with direct evaluation.\n");
printf(" * uses USER-supplied concrete matrices (any dimensions).\n");
printf(" VisualVerify(expr) – same, displays both computations.\n\n");
printf("5. NUMERIC EVALUATION\n");
printf(" NumericEvaluate(expr) – evaluate concrete expressions (inert -> active).\n\n");
printf("6. STEP-BY-STEP EXPLANATION – SimplifyTrace\n");
printf(" Each iteration scans the whole expression.\n");
printf(" All applicable rules are applied (order is not guaranteed).\n");
printf(" Output: rule name + mathematical description + old -> new subexpression.\n");
printf(" Example: KRON7 (chain) ((A5B)·(C5D) → (A·C)5(B·D)): ...\n\n");
printf("7. SUMMARY OF RULES (KRON1–KRON11)\n");
printf(" KRON1: (cA)5B = A5(cB) = c·(A5B)\n");
printf(" KRON2: (A5B)^T = A^T 5 B^T\n");
printf(" KRON3: (A5B)^H = A^H 5 B^H\n");
printf(" KRON4: (A5B)5C = A5(B5C)\n");
printf(" KRON5: (A+B)5C = A5C + B5C\n");
printf(" KRON6: A5(B+C) = A5B + A5C\n");
printf(" KRON7: (A5B)·(C5D) = (A·C)5(B·D)\n");
printf(" KRON8: Tr(A5B) = Tr(A)·Tr(B)\n");
printf(" KRON9: det(A5B) = det(A)^{dim(B)} · det(B)^{dim(A)}\n");
printf(" KRON10: (A5B)^{-1} = A^{-1} 5 B^{-1}\n");
printf(" KRON11: Shuffle(A,B) = S·(A5B)·S^T gives B5A\n");
printf(" Extra: dot distributes over sums, trace linearity, etc.\n\n");
printf("8. DIMENSIONS AND LIMITATIONS\n");
printf(" - Kronecker product works for any matrices (even rectangular).\n");
printf(" - Ordinary multiplication (.), determinant, trace, inverse require square matrices.\n");
printf(" - Shuffle needs exact dimensions; otherwise it remains inert.\n");
printf(" - Check and VisualCheck assume all matrix symbols are square.\n");
printf(" - Use Verify/VisualVerify for non‑square or user‑provided matrices.\n\n");
printf("9. EXAMPLES (copy and paste into Maple)\n");
printf(" with(KroneckerRules):\n");
printf(" expr := (A &x B) . (C &x D);\n");
printf(" SimplifyTrace(expr);\n");
printf(" SimplifyTrace(expr, \"Kron\");\n");
printf(" expr2 := (2*A &x (B+C)) . (Transpose(D) &x MatrixInverse(E));\n");
printf(" Simplify(expr2, \"Kron\");\n");
printf(" A := Matrix(2,2,[[1,2],[3,4]]); B := Matrix(2,2,[[0,1],[1,0]]);\n");
printf(" Verify( (A &x B) . (B &x A) );\n\n");
printf("10. ADDITIONAL INFORMATION\n");
printf(" - Use 'ToggleKronNotation()' to change global output style.\n");
printf(" - Adjust 'maxiter' for deep associativity (default 50).\n");
printf(" - Shuffle is only numerically evaluable when all dimensions are known.\n");
printf("================================================================\n");
end proc;
end module:
|