0.12.0a1 add Matrix()

This commit is contained in:
Yusur 2025-12-12 11:03:10 +01:00
parent eca16d781f
commit d123b9c196
4 changed files with 173 additions and 1 deletions

View file

@ -1,5 +1,9 @@
# Changelog
## 0.12.0
* New module `mat` adds a shallow reimplementation of `Matrix()` in order to implement matrix multiplication
## 0.11.2
+ increase test coverage of `validators`

View file

@ -37,7 +37,7 @@ from .redact import redact_url_password
from .http import WantsContentType
from .color import chalk, WebColor
__version__ = "0.11.2"
__version__ = "0.12.0a1"
__all__ = (
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',

121
src/suou/mat.py Normal file
View file

@ -0,0 +1,121 @@
"""
Matrix (not the movie...)
*New in 0.12.0*
---
Copyright (c) 2025 Sakuragasaki46.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
See LICENSE for the specific language governing permissions and
limitations under the License.
This software is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
from __future__ import annotations
from typing import Collection, Iterable, TypeVar
from .functools import deprecated
_T = TypeVar('_T')
class Matrix(Collection[_T]):
"""
Shallow reimplementation of numpy's matrices in pure Python.
*New in 0.12.0*
"""
_shape: tuple[int, int]
_elements: list[_T]
def shape(self):
return self._shape
def __init__(self, iterable: Iterable[_T] | Iterable[Collection[_T]], shape: tuple[int, int] | None = None):
elements = []
boundary_x = boundary_y = 0
for row in iterable:
if isinstance(row, Collection):
if not boundary_y:
boundary_y = len(row)
elements.extend(row)
boundary_x += 1
elif boundary_y != len(row):
raise ValueError('row length mismatch')
else:
elements.extend(row)
boundary_x += 1
elif shape:
if not boundary_x:
boundary_x, boundary_y = shape
elements.append(row)
self._shape = boundary_x, boundary_y
self._elements = elements
assert len(self._elements) == boundary_x * boundary_y
def __getitem__(self, key: tuple[int, int]) -> _T:
(x, y), (_, sy) = key, self.shape()
return self._elements[x * sy + y]
@property
def T(self):
sx, sy = self.shape()
return Matrix(
[
[
self[j, i] for j in range(sx)
] for i in range(sy)
]
)
def __matmul__(self, other: Matrix) -> Matrix:
(ax, ay), (bx, by) = self.shape(), other.shape()
if ay != bx:
raise ValueError('cannot multiply matrices with incompatible shape')
return Matrix([
[
sum(self[i, k] * other[k, j] for k in range(ay)) for j in range(by)
] for i in range(ax)
])
def __eq__(self, other: Matrix):
try:
return self._elements == other._elements and self._shape == other._shape
except Exception:
return False
def __len__(self):
ax, ay = self.shape()
return ax * ay
@deprecated('please use .rows() or .columns() instead')
def __iter__(self):
return iter(self._elements)
def __contains__(self, x: object, /) -> bool:
return x in self._elements
def __repr__(self):
return f'{self.__class__.__name__}({list(self.rows())})'
def rows(self):
sx, sy = self.shape()
return (
[self[j, i] for j in range(sy)] for i in range(sx)
)
def columns(self):
sx, sy = self.shape()
return (
[self[j, i] for j in range(sx)] for i in range(sy)
)
## TODO write tests!

47
tests/test_mat.py Normal file
View file

@ -0,0 +1,47 @@
import unittest
from suou.mat import Matrix
class TestMat(unittest.TestCase):
def setUp(self):
self.m_a = Matrix([
[2, 2],
[1, 3]
])
self.m_b = Matrix([
[1], [-4]
])
def tearDown(self) -> None:
...
def test_transpose(self):
self.assertEqual(
self.m_a.T,
Matrix([
[2, 1],
[2, 3]
])
)
self.assertEqual(
self.m_b.T,
Matrix([[1, -4]])
)
def test_mul(self):
self.assertEqual(
self.m_b.T @ self.m_a,
Matrix([
[-2, -10]
])
)
self.assertEqual(
self.m_a @ self.m_b,
Matrix([
[-6], [-11]
])
)
def test_shape(self):
self.assertEqual(self.m_a.shape(), (2, 2))
self.assertEqual(self.m_b.shape(), (2, 1))
self.assertEqual(self.m_b.T.shape(), (1, 2))