mirror of https://github.com/Nonannet/copapy.git
sharing the constant for scalar/vector and scalar/
matrix operations; volatile property for net objects added
This commit is contained in:
parent
a0ab604aad
commit
247fc1a28f
|
|
@ -61,7 +61,6 @@ class Node:
|
|||
return hash(self.name) ^ hash(frozenset(a.source.node_hash for a in self.args))
|
||||
return hash(self.name) ^ hash(tuple(a.source.node_hash for a in self.args))
|
||||
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self.node_hash
|
||||
|
||||
|
|
@ -77,6 +76,7 @@ class Net:
|
|||
def __init__(self, dtype: str, source: Node):
|
||||
self.dtype = dtype
|
||||
self.source = source
|
||||
self.volatile = False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
names = get_var_name(self)
|
||||
|
|
@ -93,7 +93,7 @@ class value(Generic[TNum], Net):
|
|||
Attributes:
|
||||
dtype (str): Data type of this value.
|
||||
"""
|
||||
def __init__(self, source: TNum | Node, dtype: str | None = None):
|
||||
def __init__(self, source: TNum | Node, dtype: str | None = None, volatile: bool = True):
|
||||
"""Instance a value.
|
||||
|
||||
Args:
|
||||
|
|
@ -113,6 +113,7 @@ class value(Generic[TNum], Net):
|
|||
else:
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'int'
|
||||
self.volatile = volatile
|
||||
|
||||
@overload
|
||||
def __add__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
||||
|
|
@ -220,33 +221,33 @@ class value(Generic[TNum], Net):
|
|||
return add_op('floordiv', [other, self])
|
||||
|
||||
def __neg__(self: TCPNum) -> TCPNum:
|
||||
if self.dtype == 'int':
|
||||
return cast(TCPNum, add_op('sub', [value(0), self]))
|
||||
return cast(TCPNum, add_op('sub', [value(0.0), self]))
|
||||
if self.dtype == 'float':
|
||||
return cast(TCPNum, add_op('sub', [value(0.0, volatile=False), self]))
|
||||
return cast(TCPNum, add_op('sub', [value(0, volatile=False), self]))
|
||||
|
||||
def __gt__(self, other: TVarNumb) -> 'value[int]':
|
||||
ret = add_op('gt', [self, other])
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
def __lt__(self, other: TVarNumb) -> 'value[int]':
|
||||
ret = add_op('gt', [other, self])
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
def __ge__(self, other: TVarNumb) -> 'value[int]':
|
||||
ret = add_op('ge', [self, other])
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
def __le__(self, other: TVarNumb) -> 'value[int]':
|
||||
ret = add_op('ge', [other, self])
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
def __eq__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
||||
ret = add_op('eq', [self, other], True)
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
def __ne__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
||||
ret = add_op('ne', [self, other], True)
|
||||
return value(ret.source, dtype='bool')
|
||||
return value(ret.source, dtype='bool', volatile=False)
|
||||
|
||||
@overload
|
||||
def __mod__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
||||
|
|
@ -358,7 +359,7 @@ class Op(Node):
|
|||
|
||||
def net_from_value(val: Any) -> value[Any]:
|
||||
vi = CPConstant(val)
|
||||
return value(vi, vi.dtype)
|
||||
return value(vi, vi.dtype, False)
|
||||
|
||||
|
||||
@overload
|
||||
|
|
|
|||
|
|
@ -78,8 +78,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(a + b for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(a + other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(a + other for a in row)
|
||||
tuple(a + o if isinstance(a, value) else a + other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -106,8 +112,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(a - b for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(a - other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(a - other for a in row)
|
||||
tuple(a - o if isinstance(a, value) else a - other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -123,8 +135,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(b - a for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(other - a for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(other - a for a in row)
|
||||
tuple(o - a if isinstance(a, value) else other - a for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -145,8 +163,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(a * b for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(a * other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(a * other for a in row)
|
||||
tuple(a * o if isinstance(a, value) else a * other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -166,8 +190,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(a / b for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(a / other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(a / other for a in row)
|
||||
tuple(a / o if isinstance(a, value) else a / other for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -179,8 +209,14 @@ class matrix(Generic[TNum]):
|
|||
tuple(b / a for a, b in zip(row1, row2))
|
||||
for row1, row2 in zip(self.values, other.values)
|
||||
)
|
||||
if isinstance(other, value):
|
||||
return matrix(
|
||||
tuple(other / a for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return matrix(
|
||||
tuple(other / a for a in row)
|
||||
tuple(o / a if isinstance(a, value) else other / a for a in row)
|
||||
for row in self.values
|
||||
)
|
||||
|
||||
|
|
@ -269,7 +305,7 @@ class matrix(Generic[TNum]):
|
|||
"""Convert all elements to copapy values if any element is a copapy value."""
|
||||
if any(isinstance(val, value) for row in self.values for val in row):
|
||||
return matrix(
|
||||
tuple(value(val) if not isinstance(val, value) else val for val in row)
|
||||
tuple(value(val, volatile=False) if not isinstance(val, value) else val for val in row)
|
||||
for row in self.values
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ def mixed_sum(scalars: Iterable[int | float | value[Any]]) -> Any:
|
|||
|
||||
def mixed_homogenize(scalars: Iterable[T | value[T]]) -> Iterable[T] | Iterable[value[T]]:
|
||||
if any(isinstance(val, value) for val in scalars):
|
||||
return (value(val) if not isinstance(val, value) else val for val in scalars)
|
||||
return (value(val, volatile=False) if not isinstance(val, value) else val for val in scalars)
|
||||
else:
|
||||
return (val for val in scalars if not isinstance(val, value))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from . import value
|
||||
from ._mixed import mixed_sum, mixed_homogenize
|
||||
from typing import TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
|
||||
from typing import Sequence, TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
|
||||
import copapy as cp
|
||||
from ._helper_types import TNum
|
||||
|
||||
|
|
@ -57,7 +57,10 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a + b for a, b in zip(self.values, other.values))
|
||||
return vector(a + other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a + other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a + o if isinstance(a, value) else a + other for a in self.values)
|
||||
|
||||
@overload
|
||||
def __radd__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
|
|
@ -80,7 +83,10 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a - b for a, b in zip(self.values, other.values))
|
||||
return vector(a - other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a - other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a - o if isinstance(a, value) else a - other for a in self.values)
|
||||
|
||||
@overload
|
||||
def __rsub__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
|
|
@ -92,7 +98,10 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(b - a for a, b in zip(self.values, other.values))
|
||||
return vector(other - a for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(other - a for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(o - a if isinstance(a, value) else other - a for a in self.values)
|
||||
|
||||
@overload
|
||||
def __mul__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
||||
|
|
@ -106,7 +115,10 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a * b for a, b in zip(self.values, other.values))
|
||||
return vector(a * other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a * other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a * o if isinstance(a, value) else a * other for a in self.values)
|
||||
|
||||
@overload
|
||||
def __rmul__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
|
|
@ -129,7 +141,10 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a ** b for a, b in zip(self.values, other.values))
|
||||
return vector(a ** other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a ** other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a ** o if isinstance(a, value) else a ** other for a in self.values)
|
||||
|
||||
@overload
|
||||
def __rpow__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
|
|
@ -138,19 +153,31 @@ class vector(Generic[TNum]):
|
|||
@overload
|
||||
def __rpow__(self, other: VecNumLike) -> 'vector[Any]': ...
|
||||
def __rpow__(self, other: VecNumLike) -> Any:
|
||||
return self ** other
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(b ** a for a, b in zip(self.values, other.values))
|
||||
if isinstance(other, value):
|
||||
return vector(other ** a for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(o ** a if isinstance(a, value) else other ** a for a in self.values)
|
||||
|
||||
def __truediv__(self, other: VecNumLike) -> 'vector[float]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a / b for a, b in zip(self.values, other.values))
|
||||
return vector(a / other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a / other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a / o if isinstance(a, value) else a / other for a in self.values)
|
||||
|
||||
def __rtruediv__(self, other: VecNumLike) -> 'vector[float]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(b / a for a, b in zip(self.values, other.values))
|
||||
return vector(other / a for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(other / a for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(o / a if isinstance(a, value) else other / a for a in self.values)
|
||||
|
||||
@overload
|
||||
def dot(self: 'vector[int]', other: 'vector[int]') -> int | value[int]: ...
|
||||
|
|
@ -191,37 +218,55 @@ class vector(Generic[TNum]):
|
|||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a > b for a, b in zip(self.values, other.values))
|
||||
return vector(a > other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a > other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a > o if isinstance(a, value) else a > other for a in self.values)
|
||||
|
||||
def __lt__(self, other: VecNumLike) -> 'vector[int]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a < b for a, b in zip(self.values, other.values))
|
||||
return vector(a < other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a < other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a < o if isinstance(a, value) else a < other for a in self.values)
|
||||
|
||||
def __ge__(self, other: VecNumLike) -> 'vector[int]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a >= b for a, b in zip(self.values, other.values))
|
||||
return vector(a >= other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a >= other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a >= o if isinstance(a, value) else a >= other for a in self.values)
|
||||
|
||||
def __le__(self, other: VecNumLike) -> 'vector[int]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a <= b for a, b in zip(self.values, other.values))
|
||||
return vector(a <= other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a <= other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a <= o if isinstance(a, value) else a <= other for a in self.values)
|
||||
|
||||
def __eq__(self, other: VecNumLike) -> 'vector[int]': # type: ignore
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a == b for a, b in zip(self.values, other.values))
|
||||
return vector(a == other for a in self.values)
|
||||
def __eq__(self, other: VecNumLike | Sequence[int | float]) -> 'vector[int]': # type: ignore
|
||||
if isinstance(other, vector | Sequence):
|
||||
assert len(self) == len(other)
|
||||
return vector(a == b for a, b in zip(self.values, other))
|
||||
if isinstance(other, value):
|
||||
return vector(a == other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a == o if isinstance(a, value) else a == other for a in self.values)
|
||||
|
||||
def __ne__(self, other: VecNumLike) -> 'vector[int]': # type: ignore
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(a != b for a, b in zip(self.values, other.values))
|
||||
return vector(a != other for a in self.values)
|
||||
if isinstance(other, value):
|
||||
return vector(a != other for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(a != o if isinstance(a, value) else a != other for a in self.values)
|
||||
|
||||
@property
|
||||
def shape(self) -> tuple[int]:
|
||||
|
|
@ -255,6 +300,15 @@ class vector(Generic[TNum]):
|
|||
def map(self, func: Callable[[Any], value[U] | U]) -> 'vector[U]':
|
||||
"""Applies a function to each element of the vector and returns a new vector."""
|
||||
return vector(func(x) for x in self.values)
|
||||
|
||||
def _map2(self, other: VecNumLike, func: Callable[[Any, Any], value[int | float]]) -> 'vector[Any]':
|
||||
if isinstance(other, vector):
|
||||
assert len(self.values) == len(other.values)
|
||||
return vector(func(a, b) for a, b in zip(self.values, other.values))
|
||||
if isinstance(other, value):
|
||||
return vector(func(a, other) for a in self.values)
|
||||
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||
return vector(func(a, o) if isinstance(a, value) else a + other for a in self.values)
|
||||
|
||||
|
||||
def cross_product(v1: vector[float], v2: vector[float]) -> vector[float]:
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ def test_matrix_scalar_division():
|
|||
m1 = cp.matrix([[6.0, 8.0], [12.0, 16.0]])
|
||||
m2 = m1 / 2.0
|
||||
|
||||
assert m2[0] == pytest.approx((3.0, 4.0)) # pyright: ignore[reportUnknownMemberType]
|
||||
assert m2[1] == pytest.approx((6.0, 8.0)) # pyright: ignore[reportUnknownMemberType]
|
||||
assert list(m2[0]) == pytest.approx((3.0, 4.0)) # pyright: ignore[reportUnknownMemberType]
|
||||
assert list(m2[1]) == pytest.approx((6.0, 8.0)) # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
|
||||
def test_matrix_vector_multiplication():
|
||||
|
|
|
|||
Loading…
Reference in New Issue