0.12.0a1 add Matrix()
This commit is contained in:
parent
eca16d781f
commit
d123b9c196
4 changed files with 173 additions and 1 deletions
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
121
src/suou/mat.py
Normal 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
47
tests/test_mat.py
Normal 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))
|
||||
Loading…
Add table
Add a link
Reference in a new issue