switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
from collections import Counter
|
||||
|
||||
from sympy.core import Mul, sympify
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.expr import ExprBuilder
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.functions.elementary.exponential import log
|
||||
from sympy.matrices.expressions.matexpr import MatrixExpr
|
||||
from sympy.matrices.expressions._shape import validate_matadd_integer as validate
|
||||
from sympy.matrices.expressions.special import ZeroMatrix, OneMatrix
|
||||
from sympy.strategies import (
|
||||
unpack, flatten, condition, exhaust, rm_id, sort
|
||||
)
|
||||
from sympy.utilities.exceptions import sympy_deprecation_warning
|
||||
|
||||
|
||||
def hadamard_product(*matrices):
|
||||
"""
|
||||
Return the elementwise (aka Hadamard) product of matrices.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import hadamard_product, MatrixSymbol
|
||||
>>> A = MatrixSymbol('A', 2, 3)
|
||||
>>> B = MatrixSymbol('B', 2, 3)
|
||||
>>> hadamard_product(A)
|
||||
A
|
||||
>>> hadamard_product(A, B)
|
||||
HadamardProduct(A, B)
|
||||
>>> hadamard_product(A, B)[0, 1]
|
||||
A[0, 1]*B[0, 1]
|
||||
"""
|
||||
if not matrices:
|
||||
raise TypeError("Empty Hadamard product is undefined")
|
||||
if len(matrices) == 1:
|
||||
return matrices[0]
|
||||
return HadamardProduct(*matrices).doit()
|
||||
|
||||
|
||||
class HadamardProduct(MatrixExpr):
|
||||
"""
|
||||
Elementwise product of matrix expressions
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Hadamard product for matrix symbols:
|
||||
|
||||
>>> from sympy import hadamard_product, HadamardProduct, MatrixSymbol
|
||||
>>> A = MatrixSymbol('A', 5, 5)
|
||||
>>> B = MatrixSymbol('B', 5, 5)
|
||||
>>> isinstance(hadamard_product(A, B), HadamardProduct)
|
||||
True
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
This is a symbolic object that simply stores its argument without
|
||||
evaluating it. To actually compute the product, use the function
|
||||
``hadamard_product()`` or ``HadamardProduct.doit``
|
||||
"""
|
||||
is_HadamardProduct = True
|
||||
|
||||
def __new__(cls, *args, evaluate=False, check=None):
|
||||
args = list(map(sympify, args))
|
||||
if len(args) == 0:
|
||||
# We currently don't have a way to support one-matrices of generic dimensions:
|
||||
raise ValueError("HadamardProduct needs at least one argument")
|
||||
|
||||
if not all(isinstance(arg, MatrixExpr) for arg in args):
|
||||
raise TypeError("Mix of Matrix and Scalar symbols")
|
||||
|
||||
if check is not None:
|
||||
sympy_deprecation_warning(
|
||||
"Passing check to HadamardProduct is deprecated and the check argument will be removed in a future version.",
|
||||
deprecated_since_version="1.11",
|
||||
active_deprecations_target='remove-check-argument-from-matrix-operations')
|
||||
|
||||
if check is not False:
|
||||
validate(*args)
|
||||
|
||||
obj = super().__new__(cls, *args)
|
||||
if evaluate:
|
||||
obj = obj.doit(deep=False)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self.args[0].shape
|
||||
|
||||
def _entry(self, i, j, **kwargs):
|
||||
return Mul(*[arg._entry(i, j, **kwargs) for arg in self.args])
|
||||
|
||||
def _eval_transpose(self):
|
||||
from sympy.matrices.expressions.transpose import transpose
|
||||
return HadamardProduct(*list(map(transpose, self.args)))
|
||||
|
||||
def doit(self, **hints):
|
||||
expr = self.func(*(i.doit(**hints) for i in self.args))
|
||||
# Check for explicit matrices:
|
||||
from sympy.matrices.matrixbase import MatrixBase
|
||||
from sympy.matrices.immutable import ImmutableMatrix
|
||||
|
||||
explicit = [i for i in expr.args if isinstance(i, MatrixBase)]
|
||||
if explicit:
|
||||
remainder = [i for i in expr.args if i not in explicit]
|
||||
expl_mat = ImmutableMatrix([
|
||||
Mul.fromiter(i) for i in zip(*explicit)
|
||||
]).reshape(*self.shape)
|
||||
expr = HadamardProduct(*([expl_mat] + remainder))
|
||||
|
||||
return canonicalize(expr)
|
||||
|
||||
def _eval_derivative(self, x):
|
||||
terms = []
|
||||
args = list(self.args)
|
||||
for i in range(len(args)):
|
||||
factors = args[:i] + [args[i].diff(x)] + args[i+1:]
|
||||
terms.append(hadamard_product(*factors))
|
||||
return Add.fromiter(terms)
|
||||
|
||||
def _eval_derivative_matrix_lines(self, x):
|
||||
from sympy.tensor.array.expressions.array_expressions import ArrayDiagonal
|
||||
from sympy.tensor.array.expressions.array_expressions import ArrayTensorProduct
|
||||
from sympy.matrices.expressions.matexpr import _make_matrix
|
||||
|
||||
with_x_ind = [i for i, arg in enumerate(self.args) if arg.has(x)]
|
||||
lines = []
|
||||
for ind in with_x_ind:
|
||||
left_args = self.args[:ind]
|
||||
right_args = self.args[ind+1:]
|
||||
|
||||
d = self.args[ind]._eval_derivative_matrix_lines(x)
|
||||
hadam = hadamard_product(*(right_args + left_args))
|
||||
diagonal = [(0, 2), (3, 4)]
|
||||
diagonal = [e for j, e in enumerate(diagonal) if self.shape[j] != 1]
|
||||
for i in d:
|
||||
l1 = i._lines[i._first_line_index]
|
||||
l2 = i._lines[i._second_line_index]
|
||||
subexpr = ExprBuilder(
|
||||
ArrayDiagonal,
|
||||
[
|
||||
ExprBuilder(
|
||||
ArrayTensorProduct,
|
||||
[
|
||||
ExprBuilder(_make_matrix, [l1]),
|
||||
hadam,
|
||||
ExprBuilder(_make_matrix, [l2]),
|
||||
]
|
||||
),
|
||||
*diagonal],
|
||||
|
||||
)
|
||||
i._first_pointer_parent = subexpr.args[0].args[0].args
|
||||
i._first_pointer_index = 0
|
||||
i._second_pointer_parent = subexpr.args[0].args[2].args
|
||||
i._second_pointer_index = 0
|
||||
i._lines = [subexpr]
|
||||
lines.append(i)
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
# TODO Implement algorithm for rewriting Hadamard product as diagonal matrix
|
||||
# if matmul identy matrix is multiplied.
|
||||
def canonicalize(x):
|
||||
"""Canonicalize the Hadamard product ``x`` with mathematical properties.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import MatrixSymbol, HadamardProduct
|
||||
>>> from sympy import OneMatrix, ZeroMatrix
|
||||
>>> from sympy.matrices.expressions.hadamard import canonicalize
|
||||
>>> from sympy import init_printing
|
||||
>>> init_printing(use_unicode=False)
|
||||
|
||||
>>> A = MatrixSymbol('A', 2, 2)
|
||||
>>> B = MatrixSymbol('B', 2, 2)
|
||||
>>> C = MatrixSymbol('C', 2, 2)
|
||||
|
||||
Hadamard product associativity:
|
||||
|
||||
>>> X = HadamardProduct(A, HadamardProduct(B, C))
|
||||
>>> X
|
||||
A.*(B.*C)
|
||||
>>> canonicalize(X)
|
||||
A.*B.*C
|
||||
|
||||
Hadamard product commutativity:
|
||||
|
||||
>>> X = HadamardProduct(A, B)
|
||||
>>> Y = HadamardProduct(B, A)
|
||||
>>> X
|
||||
A.*B
|
||||
>>> Y
|
||||
B.*A
|
||||
>>> canonicalize(X)
|
||||
A.*B
|
||||
>>> canonicalize(Y)
|
||||
A.*B
|
||||
|
||||
Hadamard product identity:
|
||||
|
||||
>>> X = HadamardProduct(A, OneMatrix(2, 2))
|
||||
>>> X
|
||||
A.*1
|
||||
>>> canonicalize(X)
|
||||
A
|
||||
|
||||
Absorbing element of Hadamard product:
|
||||
|
||||
>>> X = HadamardProduct(A, ZeroMatrix(2, 2))
|
||||
>>> X
|
||||
A.*0
|
||||
>>> canonicalize(X)
|
||||
0
|
||||
|
||||
Rewriting to Hadamard Power
|
||||
|
||||
>>> X = HadamardProduct(A, A, A)
|
||||
>>> X
|
||||
A.*A.*A
|
||||
>>> canonicalize(X)
|
||||
.3
|
||||
A
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
As the Hadamard product is associative, nested products can be flattened.
|
||||
|
||||
The Hadamard product is commutative so that factors can be sorted for
|
||||
canonical form.
|
||||
|
||||
A matrix of only ones is an identity for Hadamard product,
|
||||
so every matrices of only ones can be removed.
|
||||
|
||||
Any zero matrix will make the whole product a zero matrix.
|
||||
|
||||
Duplicate elements can be collected and rewritten as HadamardPower
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Hadamard_product_(matrices)
|
||||
"""
|
||||
# Associativity
|
||||
rule = condition(
|
||||
lambda x: isinstance(x, HadamardProduct),
|
||||
flatten
|
||||
)
|
||||
fun = exhaust(rule)
|
||||
x = fun(x)
|
||||
|
||||
# Identity
|
||||
fun = condition(
|
||||
lambda x: isinstance(x, HadamardProduct),
|
||||
rm_id(lambda x: isinstance(x, OneMatrix))
|
||||
)
|
||||
x = fun(x)
|
||||
|
||||
# Absorbing by Zero Matrix
|
||||
def absorb(x):
|
||||
if any(isinstance(c, ZeroMatrix) for c in x.args):
|
||||
return ZeroMatrix(*x.shape)
|
||||
else:
|
||||
return x
|
||||
fun = condition(
|
||||
lambda x: isinstance(x, HadamardProduct),
|
||||
absorb
|
||||
)
|
||||
x = fun(x)
|
||||
|
||||
# Rewriting with HadamardPower
|
||||
if isinstance(x, HadamardProduct):
|
||||
tally = Counter(x.args)
|
||||
|
||||
new_arg = []
|
||||
for base, exp in tally.items():
|
||||
if exp == 1:
|
||||
new_arg.append(base)
|
||||
else:
|
||||
new_arg.append(HadamardPower(base, exp))
|
||||
|
||||
x = HadamardProduct(*new_arg)
|
||||
|
||||
# Commutativity
|
||||
fun = condition(
|
||||
lambda x: isinstance(x, HadamardProduct),
|
||||
sort(default_sort_key)
|
||||
)
|
||||
x = fun(x)
|
||||
|
||||
# Unpacking
|
||||
x = unpack(x)
|
||||
return x
|
||||
|
||||
|
||||
def hadamard_power(base, exp):
|
||||
base = sympify(base)
|
||||
exp = sympify(exp)
|
||||
if exp == 1:
|
||||
return base
|
||||
if not base.is_Matrix:
|
||||
return base**exp
|
||||
if exp.is_Matrix:
|
||||
raise ValueError("cannot raise expression to a matrix")
|
||||
return HadamardPower(base, exp)
|
||||
|
||||
|
||||
class HadamardPower(MatrixExpr):
|
||||
r"""
|
||||
Elementwise power of matrix expressions
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
base : scalar or matrix
|
||||
|
||||
exp : scalar or matrix
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
There are four definitions for the hadamard power which can be used.
|
||||
Let's consider `A, B` as `(m, n)` matrices, and `a, b` as scalars.
|
||||
|
||||
Matrix raised to a scalar exponent:
|
||||
|
||||
.. math::
|
||||
A^{\circ b} = \begin{bmatrix}
|
||||
A_{0, 0}^b & A_{0, 1}^b & \cdots & A_{0, n-1}^b \\
|
||||
A_{1, 0}^b & A_{1, 1}^b & \cdots & A_{1, n-1}^b \\
|
||||
\vdots & \vdots & \ddots & \vdots \\
|
||||
A_{m-1, 0}^b & A_{m-1, 1}^b & \cdots & A_{m-1, n-1}^b
|
||||
\end{bmatrix}
|
||||
|
||||
Scalar raised to a matrix exponent:
|
||||
|
||||
.. math::
|
||||
a^{\circ B} = \begin{bmatrix}
|
||||
a^{B_{0, 0}} & a^{B_{0, 1}} & \cdots & a^{B_{0, n-1}} \\
|
||||
a^{B_{1, 0}} & a^{B_{1, 1}} & \cdots & a^{B_{1, n-1}} \\
|
||||
\vdots & \vdots & \ddots & \vdots \\
|
||||
a^{B_{m-1, 0}} & a^{B_{m-1, 1}} & \cdots & a^{B_{m-1, n-1}}
|
||||
\end{bmatrix}
|
||||
|
||||
Matrix raised to a matrix exponent:
|
||||
|
||||
.. math::
|
||||
A^{\circ B} = \begin{bmatrix}
|
||||
A_{0, 0}^{B_{0, 0}} & A_{0, 1}^{B_{0, 1}} &
|
||||
\cdots & A_{0, n-1}^{B_{0, n-1}} \\
|
||||
A_{1, 0}^{B_{1, 0}} & A_{1, 1}^{B_{1, 1}} &
|
||||
\cdots & A_{1, n-1}^{B_{1, n-1}} \\
|
||||
\vdots & \vdots &
|
||||
\ddots & \vdots \\
|
||||
A_{m-1, 0}^{B_{m-1, 0}} & A_{m-1, 1}^{B_{m-1, 1}} &
|
||||
\cdots & A_{m-1, n-1}^{B_{m-1, n-1}}
|
||||
\end{bmatrix}
|
||||
|
||||
Scalar raised to a scalar exponent:
|
||||
|
||||
.. math::
|
||||
a^{\circ b} = a^b
|
||||
"""
|
||||
|
||||
def __new__(cls, base, exp):
|
||||
base = sympify(base)
|
||||
exp = sympify(exp)
|
||||
|
||||
if base.is_scalar and exp.is_scalar:
|
||||
return base ** exp
|
||||
|
||||
if isinstance(base, MatrixExpr) and isinstance(exp, MatrixExpr):
|
||||
validate(base, exp)
|
||||
|
||||
obj = super().__new__(cls, base, exp)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def base(self):
|
||||
return self._args[0]
|
||||
|
||||
@property
|
||||
def exp(self):
|
||||
return self._args[1]
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
if self.base.is_Matrix:
|
||||
return self.base.shape
|
||||
return self.exp.shape
|
||||
|
||||
def _entry(self, i, j, **kwargs):
|
||||
base = self.base
|
||||
exp = self.exp
|
||||
|
||||
if base.is_Matrix:
|
||||
a = base._entry(i, j, **kwargs)
|
||||
elif base.is_scalar:
|
||||
a = base
|
||||
else:
|
||||
raise ValueError(
|
||||
'The base {} must be a scalar or a matrix.'.format(base))
|
||||
|
||||
if exp.is_Matrix:
|
||||
b = exp._entry(i, j, **kwargs)
|
||||
elif exp.is_scalar:
|
||||
b = exp
|
||||
else:
|
||||
raise ValueError(
|
||||
'The exponent {} must be a scalar or a matrix.'.format(exp))
|
||||
|
||||
return a ** b
|
||||
|
||||
def _eval_transpose(self):
|
||||
from sympy.matrices.expressions.transpose import transpose
|
||||
return HadamardPower(transpose(self.base), self.exp)
|
||||
|
||||
def _eval_derivative(self, x):
|
||||
dexp = self.exp.diff(x)
|
||||
logbase = self.base.applyfunc(log)
|
||||
dlbase = logbase.diff(x)
|
||||
return hadamard_product(
|
||||
dexp*logbase + self.exp*dlbase,
|
||||
self
|
||||
)
|
||||
|
||||
def _eval_derivative_matrix_lines(self, x):
|
||||
from sympy.tensor.array.expressions.array_expressions import ArrayTensorProduct
|
||||
from sympy.tensor.array.expressions.array_expressions import ArrayDiagonal
|
||||
from sympy.matrices.expressions.matexpr import _make_matrix
|
||||
|
||||
lr = self.base._eval_derivative_matrix_lines(x)
|
||||
for i in lr:
|
||||
diagonal = [(1, 2), (3, 4)]
|
||||
diagonal = [e for j, e in enumerate(diagonal) if self.base.shape[j] != 1]
|
||||
l1 = i._lines[i._first_line_index]
|
||||
l2 = i._lines[i._second_line_index]
|
||||
subexpr = ExprBuilder(
|
||||
ArrayDiagonal,
|
||||
[
|
||||
ExprBuilder(
|
||||
ArrayTensorProduct,
|
||||
[
|
||||
ExprBuilder(_make_matrix, [l1]),
|
||||
self.exp*hadamard_power(self.base, self.exp-1),
|
||||
ExprBuilder(_make_matrix, [l2]),
|
||||
]
|
||||
),
|
||||
*diagonal],
|
||||
validator=ArrayDiagonal._validate
|
||||
)
|
||||
i._first_pointer_parent = subexpr.args[0].args[0].args
|
||||
i._first_pointer_index = 0
|
||||
i._first_line_index = 0
|
||||
i._second_pointer_parent = subexpr.args[0].args[2].args
|
||||
i._second_pointer_index = 0
|
||||
i._second_line_index = 0
|
||||
i._lines = [subexpr]
|
||||
return lr
|
||||
Reference in New Issue
Block a user