142 lines
4.2 KiB
Python
142 lines
4.2 KiB
Python
|
"""This module defines a Vector class."""
|
||
|
|
||
|
from .matrix import Matrix
|
||
|
|
||
|
|
||
|
class Vector:
|
||
|
"""A standard one-dimensional vector from linear algebra.
|
||
|
|
||
|
The class is designed for sub-classing in such a way that
|
||
|
the user can adapt the typing class attribute to change,
|
||
|
for example, how the entries are stored (e.g., as integers).
|
||
|
|
||
|
Attributes:
|
||
|
storage (callable): must return an iterable that is used
|
||
|
to store the entries of the vector; defaults to tuple
|
||
|
typing (callable): type casting applied to all vector
|
||
|
entries upon creation; defaults to float
|
||
|
zero_threshold (float): maximum difference allowed when
|
||
|
comparing an entry to zero; defaults to 1e-12
|
||
|
"""
|
||
|
|
||
|
storage = tuple
|
||
|
typing = float
|
||
|
zero_threshold = 1e-12
|
||
|
|
||
|
def __init__(self, data):
|
||
|
"""Initiate a new vector.
|
||
|
|
||
|
Args:
|
||
|
data (iterable): the vector's entries;
|
||
|
must have at least one element
|
||
|
|
||
|
Raises:
|
||
|
ValueError: if the provided data do not have enough entries
|
||
|
"""
|
||
|
self._entries = self.storage(self.typing(x) for x in data)
|
||
|
if len(self) == 0:
|
||
|
raise ValueError("the vector must have at least one entry")
|
||
|
|
||
|
def __repr__(self):
|
||
|
name, args = self.__class__.__name__, ", ".join(f"{x:.3f}" for x in self)
|
||
|
return f"{name}(({args}))"
|
||
|
|
||
|
def __str__(self):
|
||
|
name, first, last, entries = (
|
||
|
self.__class__.__name__,
|
||
|
self[0],
|
||
|
self[-1],
|
||
|
len(self),
|
||
|
)
|
||
|
return f"{name}({first:.1f}, ..., {last:.1f})[{entries:d}]"
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self._entries)
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
if not isinstance(index, int):
|
||
|
raise TypeError("index must be an integer")
|
||
|
return self._entries[index]
|
||
|
|
||
|
def __iter__(self):
|
||
|
return iter(self._entries)
|
||
|
|
||
|
def __reversed__(self):
|
||
|
return reversed(self._entries)
|
||
|
|
||
|
def __add__(self, other):
|
||
|
if isinstance(other, self.__class__):
|
||
|
if len(self) != len(other):
|
||
|
raise ValueError("vectors need to be of the same length")
|
||
|
return self.__class__(x + y for (x, y) in zip(self, other))
|
||
|
elif isinstance(other, numbers.Number):
|
||
|
return self.__class__(x + other for x in self)
|
||
|
return NotImplemented
|
||
|
|
||
|
def __radd__(self, other):
|
||
|
return self + other
|
||
|
|
||
|
def __sub__(self, other):
|
||
|
return self + (-other)
|
||
|
|
||
|
def __rsub__(self, other):
|
||
|
return (-self) + other
|
||
|
|
||
|
def __mul__(self, other):
|
||
|
if isinstance(other, self.__class__):
|
||
|
if len(self) != len(other):
|
||
|
raise ValueError("vectors need to be of the same length")
|
||
|
return sum(x * y for (x, y) in zip(self, other))
|
||
|
elif isinstance(other, numbers.Number):
|
||
|
return self.__class__(x * other for x in self)
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rmul__(self, other):
|
||
|
return self * other
|
||
|
|
||
|
def __truediv__(self, other):
|
||
|
if isinstance(other, numbers.Number):
|
||
|
return self * (1 / other)
|
||
|
return NotImplemented
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
if isinstance(other, self.__class__):
|
||
|
if len(self) != len(other):
|
||
|
raise ValueError("vectors need to be of the same length")
|
||
|
for x, y in zip(self, other):
|
||
|
if abs(x - y) > self.zero_threshold:
|
||
|
return False
|
||
|
return True
|
||
|
return NotImplemented
|
||
|
|
||
|
def __pos__(self):
|
||
|
return self
|
||
|
|
||
|
def __neg__(self):
|
||
|
return self.__class__(-x for x in self)
|
||
|
|
||
|
def __abs__(self):
|
||
|
return norm(self)
|
||
|
|
||
|
def __bool__(self):
|
||
|
return bool(abs(self))
|
||
|
|
||
|
def __float__(self):
|
||
|
if len(self) != 1:
|
||
|
raise RuntimeError("vector must have exactly one entry to become a scalar")
|
||
|
return self[0]
|
||
|
|
||
|
def as_matrix(self, *, column=True):
|
||
|
"""Convert the vector into a matrix.
|
||
|
|
||
|
Args:
|
||
|
column (bool): if the vector should be interpreted as
|
||
|
as a column vector or not; defaults to True
|
||
|
|
||
|
Returns:
|
||
|
matrix (Matrix)
|
||
|
"""
|
||
|
if column:
|
||
|
return Matrix([x] for x in self)
|
||
|
return Matrix([(x for x in self)])
|