use
5.006;
our
@ISA
=
qw(Exporter)
;
our
@EXPORT
=
qw(
make_matrix
make_symbolic_matrix
identity_matrix
add_matrix
sub_matrix
multiply_matrix
scalar_multiply_matrix
scalar_divide_matrix
order_of_matrix
simplify_matrix
transpose_matrix
evaluate_matrix
implement_matrix
set_matrix
cofactors_matrix
adjugate_matrix
invert_matrix
is_square_matrix
is_equals_matrix
is_symmetric_matrix
is_skew_symmetric_matrix
)
;
our
$VERSION
=
'0.21'
;
sub
make_matrix {
my
(
$scalar
,
$r
,
$c
) =
@_
;
$scalar
= Math::Symbolic::parse_from_string(
$scalar
)
if
ref
(
$scalar
) !~ /^Math::Symbolic/;
my
@m
;
foreach
my
$i
(0..
$r
-1) {
foreach
my
$j
(0..
$c
-1) {
$m
[
$i
][
$j
] =
$scalar
;
}
}
return
\
@m
;
}
sub
make_symbolic_matrix {
my
(
$mat
) =
@_
;
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@sm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$v
=
$mat
->[
$i
][
$j
];
my
$ov
=
$v
;
if
(
ref
(
$ov
) !~ /^Math::Symbolic/ ) {
$ov
= Math::Symbolic::parse_from_string(
$v
);
}
$sm
[
$i
][
$j
] =
$ov
;
}
}
return
simplify_matrix(\
@sm
);
}
sub
identity_matrix {
my
(
$size
) =
@_
;
my
@I
;
foreach
my
$i
(0..
$size
-1) {
foreach
my
$j
(0..
$size
-1) {
$I
[
$i
][
$j
] = (
$i
==
$j
? Math::Symbolic::Constant->new(1) : Math::Symbolic::Constant->new(0));
}
}
return
\
@I
;
}
sub
add_matrix {
my
(
$m_a
,
$m_b
) =
@_
;
my
@ao
= order_of_matrix(
$m_a
);
my
@bo
= order_of_matrix(
$m_b
);
return
undef
unless
(
$ao
[0] ==
$bo
[0]) && (
$ao
[1] ==
$bo
[1]);
my
@m_o
;
foreach
my
$i
(0..
$ao
[0]-1) {
foreach
my
$j
(0..
$ao
[1]-1) {
my
$a_val
=
$m_a
->[
$i
][
$j
];
my
$b_val
=
$m_b
->[
$i
][
$j
];
$a_val
= Math::Symbolic::parse_from_string(
$a_val
)
if
ref
(
$a_val
) !~ /^Math::Symbolic/;
$b_val
= Math::Symbolic::parse_from_string(
$b_val
)
if
ref
(
$b_val
) !~ /^Math::Symbolic/;
$m_o
[
$i
][
$j
] = Math::Symbolic::Operator->new(
'+'
,
$a_val
,
$b_val
);
}
}
return
simplify_matrix(\
@m_o
);
}
sub
sub_matrix {
my
(
$m_a
,
$m_b
) =
@_
;
my
@ao
= order_of_matrix(
$m_a
);
my
@bo
= order_of_matrix(
$m_b
);
return
undef
unless
(
$ao
[0] ==
$bo
[0]) && (
$ao
[1] ==
$bo
[1]);
my
@m_o
;
foreach
my
$i
(0..
$ao
[0]-1) {
foreach
my
$j
(0..
$ao
[1]-1) {
my
$a_val
=
$m_a
->[
$i
][
$j
];
my
$b_val
=
$m_b
->[
$i
][
$j
];
$a_val
= Math::Symbolic::parse_from_string(
$a_val
)
if
ref
(
$a_val
) !~ /^Math::Symbolic/;
$b_val
= Math::Symbolic::parse_from_string(
$b_val
)
if
ref
(
$b_val
) !~ /^Math::Symbolic/;
$m_o
[
$i
][
$j
] = Math::Symbolic::Operator->new(
'-'
,
$a_val
,
$b_val
);
}
}
return
simplify_matrix(\
@m_o
);
}
sub
multiply_matrix {
my
(
$m_a
,
$m_b
) =
@_
;
$m_a
= make_symbolic_matrix(
$m_a
);
$m_b
= make_symbolic_matrix(
$m_b
);
my
(
$m_a_rows
,
$m_a_cols
) = order_of_matrix(
$m_a
);
my
(
$m_b_rows
,
$m_b_cols
) = order_of_matrix(
$m_b
);
return
undef
unless
$m_a_cols
==
$m_b_rows
;
my
@m_o
;
foreach
my
$i
(0..
$m_a_rows
-1) {
foreach
my
$j
(0..
$m_b_cols
-1) {
my
$m_o_ij
;
foreach
my
$k
(0..
$m_a_cols
-1) {
if
(
defined
$m_o_ij
) {
$m_o_ij
= Math::Symbolic::Operator->new(
'+'
,
$m_o_ij
, Math::Symbolic::Operator->new(
'*'
,
$m_a
->[
$i
][
$k
],
$m_b
->[
$k
][
$j
]));
}
else
{
$m_o_ij
= Math::Symbolic::Operator->new(
'*'
,
$m_a
->[
$i
][
$k
],
$m_b
->[
$k
][
$j
]);
}
}
$m_o
[
$i
][
$j
] =
$m_o_ij
;
}
}
return
simplify_matrix(\
@m_o
);
}
sub
scalar_multiply_matrix {
my
(
$scalar
,
$mat
) =
@_
;
$scalar
= Math::Symbolic::parse_from_string(
$scalar
)
if
ref
(
$scalar
) !~ /^Math::Symbolic/;
$mat
= make_symbolic_matrix(
$mat
);
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@sm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$m_val
=
$mat
->[
$i
][
$j
];
$sm
[
$i
][
$j
] = Math::Symbolic::Operator->new(
'*'
,
$scalar
,
$m_val
);
}
}
return
simplify_matrix(\
@sm
);
}
sub
scalar_divide_matrix {
my
(
$scalar
,
$mat
) =
@_
;
$scalar
= Math::Symbolic::parse_from_string(
$scalar
)
if
ref
(
$scalar
) !~ /^Math::Symbolic/;
$mat
= make_symbolic_matrix(
$mat
);
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@sm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$m_val
=
$mat
->[
$i
][
$j
];
my
$m_val_v
=
$m_val
->value();
if
(
defined
(
$m_val_v
) && (
$m_val_v
== 0) ) {
$sm
[
$i
][
$j
] = Math::Symbolic::Constant->new(0);
}
else
{
$sm
[
$i
][
$j
] = Math::Symbolic::Operator->new(
'/'
,
$scalar
,
$m_val
);
}
}
}
return
simplify_matrix(\
@sm
);
}
sub
order_of_matrix {
my
(
$mat
) =
@_
;
my
$rows
=
scalar
(@{
$mat
});
my
$cols
;
foreach
my
$row
(@{
$mat
}) {
my
$c
=
scalar
(@{
$row
});
if
(
defined
$cols
) {
if
(
$c
!=
$cols
) {
carp
"order_of_matrix: Matrix is malformed!"
;
return
undef
;
}
}
else
{
$cols
=
$c
;
}
}
return
(
$rows
,
$cols
);
}
sub
simplify_matrix {
my
(
$mat
) =
@_
;
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@sm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$m_val
=
$mat
->[
$i
][
$j
];
$m_val
= Math::Symbolic::parse_from_string(
$m_val
)
if
ref
(
$m_val
) !~ /^Math::Symbolic/;
if
(
defined
(
my
$m_val_s
=
$m_val
->simplify()) ) {
$sm
[
$i
][
$j
] =
$m_val_s
;
}
else
{
carp
"simplify_matrix: Could not simplify!: $m_val"
;
$sm
[
$i
][
$j
] =
$m_val
;
}
}
}
return
\
@sm
;
}
sub
transpose_matrix {
my
(
$mat
) =
@_
;
return
undef
unless
defined
$mat
;
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@t
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$v
=
$mat
->[
$i
][
$j
];
return
undef
unless
defined
$v
;
$t
[
$j
][
$i
] =
$v
;
}
}
return
\
@t
;
}
sub
evaluate_matrix {
my
(
$mat
,
$vals
) =
@_
;
my
%vals
= %{
$vals
};
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@vm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$v
=
$mat
->[
$i
][
$j
];
if
(
ref
(
$v
) =~ /^Math::Symbolic/ ) {
$vm
[
$i
][
$j
] =
$v
->value(
%vals
);
}
}
}
return
\
@vm
;
}
sub
implement_matrix {
my
(
$mat
,
$vals
) =
@_
;
my
%vals
= %{
$vals
};
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@vm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$v
=
$mat
->[
$i
][
$j
];
if
(
ref
(
$v
) =~ /^Math::Symbolic/ ) {
$vm
[
$i
][
$j
] =
$v
->implement(
%vals
);
}
}
}
return
\
@vm
;
}
sub
set_matrix {
my
(
$mat
,
$vals
) =
@_
;
my
%vals
= %{
$vals
};
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@vm
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
$v
=
$mat
->[
$i
][
$j
];
if
(
ref
(
$v
) =~ /^Math::Symbolic/ ) {
$vm
[
$i
][
$j
] =
$v
->set_value(
%vals
);
}
}
}
return
\
@vm
;
}
sub
cofactors_matrix {
my
(
$mat
) =
@_
;
return
undef
unless
is_square_matrix(
$mat
);
my
(
$n_r
,
$n_c
) = order_of_matrix(
$mat
);
my
@cofactors
;
foreach
my
$i
(0..
$n_r
-1) {
foreach
my
$j
(0..
$n_c
-1) {
my
@minor
;
my
$x_i
= 0;
X_LOOP:
foreach
my
$x
(0..
$n_r
-1) {
next
X_LOOP
if
$x
==
$i
;
my
$y_i
= 0;
Y_LOOP:
foreach
my
$y
(0..
$n_c
-1) {
next
Y_LOOP
if
$y
==
$j
;
$minor
[
$x_i
][
$y_i
] =
$mat
->[
$x
][
$y
];
$y_i
++;
}
$x_i
++;
}
my
$minor
= det
@minor
;
my
$sign
= (-1)**(
$i
+
$j
);
$cofactors
[
$i
][
$j
] = Math::Symbolic::Operator->new(
'*'
, Math::Symbolic::Constant->new(
$sign
),
$minor
);
}
}
return
\
@cofactors
;
}
sub
adjugate_matrix {
my
(
$mat
) =
@_
;
return
undef
unless
is_square_matrix(
$mat
);
return
transpose_matrix(cofactors_matrix(
$mat
));
}
sub
invert_matrix {
my
(
$mat
) =
@_
;
return
undef
unless
is_square_matrix(
$mat
);
my
$det
= det @{
$mat
};
my
$s_det
=
$det
->simplify();
my
$s_det_v
=
$s_det
->value();
return
undef
if
defined
(
$s_det_v
) && (
$s_det_v
== 0);
my
$one
= Math::Symbolic::Constant->new(1);
my
$det_reciprocal
= Math::Symbolic::Operator->new(
'/'
,
$one
,
$s_det
);
my
$adj
= adjugate_matrix(
$mat
);
return
undef
unless
defined
$adj
;
my
$inv
= scalar_multiply_matrix(
$det_reciprocal
,
$adj
);
return
simplify_matrix(
$inv
);
}
sub
is_square_matrix {
my
(
$mat
) =
@_
;
my
(
$r
,
$c
) = order_of_matrix(
$mat
);
return
1
if
$r
==
$c
;
return
0;
}
sub
is_equals_matrix {
my
(
$m_a
,
$m_b
) =
@_
;
my
@ao
= order_of_matrix(
$m_a
);
my
@bo
= order_of_matrix(
$m_b
);
return
0
unless
(
$ao
[0] ==
$bo
[0]) && (
$ao
[1] ==
$bo
[1]);
my
$a_s
= simplify_matrix(
$m_a
);
my
$b_s
= simplify_matrix(
$m_b
);
foreach
my
$i
(0..
$ao
[0]-1) {
foreach
my
$j
(0..
$ao
[1]-1) {
return
0
unless
$m_a
->[
$i
][
$j
]->to_string() eq
$m_b
->[
$i
][
$j
]->to_string();
}
}
return
1;
}
sub
is_symmetric_matrix {
my
(
$mat
) =
@_
;
return
0
unless
is_square_matrix(
$mat
);
return
is_equals_matrix(
$mat
, transpose_matrix(
$mat
));
}
sub
is_skew_symmetric_matrix {
my
(
$mat
) =
@_
;
return
0
unless
is_square_matrix(
$mat
);
return
is_equals_matrix(
$mat
, simplify_matrix(scalar_multiply_matrix(-1, transpose_matrix(
$mat
))) );
}
1;