Matrix Operations¶
The SimpleArray family carries a matrix layer on top of the array operations (Buffers and Arrays).
Matrix Multiplication¶
matmul(other) multiplies one- and two-dimensional operands of the same
class: matrix-matrix, matrix-vector, vector-matrix, and vector-vector, with
the operand shapes chained as in numpy. The __matmul__ protocol is bound to
it, so the @ operator is the equivalent spelling. The results are verified
equal to numpy.matmul with one divergence: the vector-vector product returns
a one-element array of shape (1,) where numpy returns a scalar:
a = solvcon.SimpleArrayFloat64(array=np.array([[1., 2.], [3., 4.]]))
b = solvcon.SimpleArrayFloat64(array=np.array([[5., 6.], [7., 8.]]))
assert ((a @ b).ndarray == np.array([[19., 22.], [43., 50.]])).all()
v = solvcon.SimpleArrayFloat64(array=np.array([1., 2.]))
w = solvcon.SimpleArrayFloat64(array=np.array([3., 4.]))
assert v.matmul(w).shape == (1,) and v.matmul(w)[0] == 11.0
A mismatched inner dimension and an operand of more than two dimensions each
raise IndexError; the shape text uses no spaces:
a.matmul(solvcon.SimpleArrayFloat64((3, 3), value=0.0))
# IndexError: SimpleArray::matmul(): shape mismatch: this=(2,2)
# other=(3,3)
c = solvcon.SimpleArrayFloat64((2, 2, 2), value=0.0)
c.matmul(c)
# IndexError: SimpleArray::matmul(): unsupported dimensions:
# this=(2,2,2) other=(2,2,2). SimpleArray must be 1D or 2D.
In-Place Matrix Multiplication¶
imatmul computes the product and replaces the receiver’s content, reshaping
it to the result. It returns None, like the in-place arithmetic of
the elementwise page, under the same open
decision on returning the receiver.
The __imatmul__ protocol is bound so that a @= b computes, but the binding
returns the updated receiver by value: the statement mutates the original
storage in place and then rebinds a to a fresh copy, so a prior alias of a
sees the product but no later change made through the rebound name. The
binding’s own comment records that the __i*__ protocols must return the
receiver itself, as the Python data model expects, so doing so is target
behavior; until then, do not rely on a and its old aliases staying the same
object across @=.
Constructors and Transforms¶
eye(n) and scaled_eye(n, scale) are static methods constructing an n by
n identity and scaled identity; n must be positive or ValueError is
raised (SimpleArray::eye(): size must be greater than 0, but got 0). eye
matches numpy.eye(n) in the class dtype; scaled_eye has no direct numpy
spelling.
pow(n) raises a square matrix to a non-negative integer power by squaring,
with pow(0) the identity, matching numpy.linalg.matrix_power on that
domain. A negative exponent raises ValueError instead of computing the numpy
matrix inverse:
m = solvcon.SimpleArrayFloat64(array=np.array([[1., 2.], [3., 4.]]))
assert (m.pow(2).ndarray == np.linalg.matrix_power(m.ndarray,
2)).all()
m.pow(-1)
# ValueError: SimpleArray::pow(): exponent must be non-negative, but
# got -1
hermitian() returns the conjugate transpose as a copy of a two-dimensional
array, equal to narr.conj().T; on the non-complex classes it is the
transpose copy. symmetrize() averages a square matrix with its (conjugate)
transpose. trace() sums the diagonal of a square matrix into a scalar;
numpy’s trace also accepts non-square input, which these methods reject. A
wrong rank or a non-square shape raises RuntimeError naming the requirement:
solvcon.SimpleArrayFloat64(5).trace()
# RuntimeError: SimpleArray::trace(): operation requires 2D
# SimpleArray, but got 1D SimpleArray
solvcon.SimpleArrayFloat64((3, 4), value=0.0).symmetrize()
# RuntimeError: SimpleArray::symmetrize(): operation requires square
# SimpleArray, but got 3x4 shape
The bindings register the whole matrix family on every typed class. The tests
exercise matmul, pow, eye, and scaled_eye on the floating-point
classes, hermitian and symmetrize on complex128, and trace on one
class from each of the floating-point, integer, and complex groups; the other
class-operation combinations follow the same kernels but are unverified.