"variable" type renamed to "value"

This commit is contained in:
Nicolas 2025-12-06 18:09:25 +01:00
parent 423ca30ac6
commit 2e48fe5ec2
31 changed files with 369 additions and 369 deletions

View File

@ -1,5 +1,5 @@
from ._target import Target
from ._basic_types import NumLike, variable, generic_sdb, iif
from ._basic_types import NumLike, value, generic_sdb, iif
from ._vectors import vector, distance, scalar_projection, angle_between, rotate_vector, vector_projection
from ._matrices import matrix, identity, zeros, ones, diagonal, eye
from ._math import sqrt, abs, sign, sin, cos, tan, asin, acos, atan, atan2, log, exp, pow, get_42, clamp, min, max, relu
@ -8,7 +8,7 @@ from ._autograd import grad
__all__ = [
"Target",
"NumLike",
"variable",
"value",
"generic_sdb",
"iif",
"vector",

View File

@ -1,4 +1,4 @@
from . import variable, vector, matrix
from . import value, vector, matrix
import copapy.backend as cpb
from typing import Any, Sequence, overload
import copapy as cp
@ -6,14 +6,14 @@ from ._basic_types import Net, unifloat
@overload
def grad(x: Any, y: variable[Any]) -> unifloat: ...
def grad(x: Any, y: value[Any]) -> unifloat: ...
@overload
def grad(x: Any, y: vector[Any]) -> vector[float]: ...
@overload
def grad(x: Any, y: Sequence[variable[Any]]) -> list[unifloat]: ...
def grad(x: Any, y: Sequence[value[Any]]) -> list[unifloat]: ...
@overload
def grad(x: Any, y: matrix[Any]) -> matrix[float]: ...
def grad(x: Any, y: variable[Any] | Sequence[variable[Any]] | vector[Any] | matrix[Any]) -> Any:
def grad(x: Any, y: value[Any] | Sequence[value[Any]] | vector[Any] | matrix[Any]) -> Any:
"""Returns the partial derivative dx/dy where x needs to be a scalar
and y might be a scalar, a list of scalars, a vector or matrix.
@ -24,9 +24,9 @@ def grad(x: Any, y: variable[Any] | Sequence[variable[Any]] | vector[Any] | matr
Returns:
Derivative of x with the type and dimensions of y
"""
assert isinstance(x, variable), f"Argument x for grad function must be a variable but is {type(x)}."
assert isinstance(x, value), f"Argument x for grad function must be a copapy value but is {type(x)}."
if isinstance(y, variable):
if isinstance(y, value):
y_set = {y}
if isinstance(y, matrix):
y_set = {v for row in y for v in row}
@ -40,7 +40,7 @@ def grad(x: Any, y: variable[Any] | Sequence[variable[Any]] | vector[Any] | matr
net_lookup = {net.source: net for node in ordered_ops for net in node.args}
grad_dict: dict[Net, unifloat] = dict()
def add_grad(val: variable[Any], gradient_value: unifloat) -> None:
def add_grad(val: value[Any], gradient_value: unifloat) -> None:
grad_dict[val] = grad_dict.get(val, 0.0) + gradient_value
for node in reversed(ordered_ops):
@ -49,8 +49,8 @@ def grad(x: Any, y: variable[Any] | Sequence[variable[Any]] | vector[Any] | matr
args: Sequence[Any] = list(node.args)
g = 1.0 if node is x.source else grad_dict[net_lookup[node]]
opn = node.name.split('_')[0]
a: variable[Any] = args[0]
b: variable[Any] = args[1] if len(args) > 1 else a
a: value[Any] = args[0]
b: value[Any] = args[1] if len(args) > 1 else a
if opn in ['ge', 'gt', 'eq', 'ne', 'floordiv', 'bwand', 'bwor', 'bwxor']:
pass # Derivative is 0 for all ops returning integers
@ -117,10 +117,10 @@ def grad(x: Any, y: variable[Any] | Sequence[variable[Any]] | vector[Any] | matr
else:
raise ValueError(f"Operation {opn} not yet supported for auto diff.")
if isinstance(y, variable):
if isinstance(y, value):
return grad_dict[y]
if isinstance(y, vector):
return vector(grad_dict[yi] if isinstance(yi, variable) else 0.0 for yi in y)
return vector(grad_dict[yi] if isinstance(yi, value) else 0.0 for yi in y)
if isinstance(y, matrix):
return matrix((grad_dict[yi] if isinstance(yi, variable) else 0.0 for yi in row) for row in y)
return matrix((grad_dict[yi] if isinstance(yi, value) else 0.0 for yi in row) for row in y)
return [grad_dict[yi] for yi in y]

View File

@ -4,12 +4,12 @@ from ._stencils import stencil_database, detect_process_arch
import copapy as cp
from ._helper_types import TNum
NumLike: TypeAlias = 'variable[int] | variable[float] | int | float'
unifloat: TypeAlias = 'variable[float] | float'
uniint: TypeAlias = 'variable[int] | int'
NumLike: TypeAlias = 'value[int] | value[float] | int | float'
unifloat: TypeAlias = 'value[float] | float'
uniint: TypeAlias = 'value[int] | int'
TCPNum = TypeVar("TCPNum", bound='variable[Any]')
TVarNumb: TypeAlias = 'variable[Any] | int | float'
TCPNum = TypeVar("TCPNum", bound='value[Any]')
TVarNumb: TypeAlias = 'value[Any] | int | float'
stencil_cache: dict[tuple[str, str], stencil_database] = {}
@ -67,7 +67,7 @@ class Node:
class Net:
"""A Net represents a variable in the computation graph - or more generally it
"""A Net represents a scalar type in the computation graph - or more generally it
connects Nodes together.
Attributes:
@ -86,19 +86,19 @@ class Net:
return self.source.node_hash
class variable(Generic[TNum], Net):
"""A "variable" represents a typed variable. It supports arithmetic and
class value(Generic[TNum], Net):
"""A "value" represents a typed scalar variable. It supports arithmetic and
comparison operations.
Attributes:
dtype (str): Data type of this variable.
dtype (str): Data type of this value.
"""
def __init__(self, source: TNum | Node, dtype: str | None = None):
"""Instance a variable.
"""Instance a value.
Args:
source: A numeric value or Node object.
dtype: Data type of this variable. Required if source is a Node.
dtype: Data type of this value. Required if source is a Node.
"""
if isinstance(source, Node):
self.source = source
@ -115,67 +115,67 @@ class variable(Generic[TNum], Net):
self.dtype = 'int'
@overload
def __add__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __add__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __add__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __add__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __add__(self, other: unifloat) -> 'variable[float]': ...
def __add__(self, other: unifloat) -> 'value[float]': ...
@overload
def __add__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __add__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __add__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __add__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __add__(self, other: TVarNumb) -> Any:
if not isinstance(other, variable) and other == 0:
if not isinstance(other, value) and other == 0:
return self
return add_op('add', [self, other], True)
@overload
def __radd__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __radd__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __radd__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __radd__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __radd__(self, other: float) -> 'variable[float]': ...
def __radd__(self, other: float) -> 'value[float]': ...
def __radd__(self, other: NumLike) -> Any:
return self + other
@overload
def __sub__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __sub__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __sub__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __sub__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __sub__(self, other: unifloat) -> 'variable[float]': ...
def __sub__(self, other: unifloat) -> 'value[float]': ...
@overload
def __sub__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __sub__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __sub__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __sub__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __sub__(self, other: TVarNumb) -> Any:
if isinstance(other, int | float) and other == 0:
return self
return add_op('sub', [self, other])
@overload
def __rsub__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __rsub__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __rsub__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __rsub__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __rsub__(self, other: float) -> 'variable[float]': ...
def __rsub__(self, other: float) -> 'value[float]': ...
def __rsub__(self, other: NumLike) -> Any:
return add_op('sub', [other, self])
@overload
def __mul__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __mul__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __mul__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __mul__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __mul__(self, other: unifloat) -> 'variable[float]': ...
def __mul__(self, other: unifloat) -> 'value[float]': ...
@overload
def __mul__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __mul__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __mul__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __mul__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __mul__(self, other: TVarNumb) -> Any:
if self.dtype == 'float' and isinstance(other, int):
other = float(other) # Prevent runtime conversion of consts; TODO: add this for other operations
if not isinstance(other, variable):
if not isinstance(other, value):
if other == 1:
return self
elif other == 0:
@ -183,112 +183,112 @@ class variable(Generic[TNum], Net):
return add_op('mul', [self, other], True)
@overload
def __rmul__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __rmul__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __rmul__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __rmul__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __rmul__(self, other: float) -> 'variable[float]': ...
def __rmul__(self, other: float) -> 'value[float]': ...
def __rmul__(self, other: NumLike) -> Any:
return self * other
def __truediv__(self, other: NumLike) -> 'variable[float]':
def __truediv__(self, other: NumLike) -> 'value[float]':
return add_op('div', [self, other])
def __rtruediv__(self, other: NumLike) -> 'variable[float]':
def __rtruediv__(self, other: NumLike) -> 'value[float]':
return add_op('div', [other, self])
@overload
def __floordiv__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __floordiv__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __floordiv__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __floordiv__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __floordiv__(self, other: unifloat) -> 'variable[float]': ...
def __floordiv__(self, other: unifloat) -> 'value[float]': ...
@overload
def __floordiv__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __floordiv__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __floordiv__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __floordiv__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __floordiv__(self, other: TVarNumb) -> Any:
return add_op('floordiv', [self, other])
@overload
def __rfloordiv__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __rfloordiv__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __rfloordiv__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __rfloordiv__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __rfloordiv__(self, other: float) -> 'variable[float]': ...
def __rfloordiv__(self, other: float) -> 'value[float]': ...
def __rfloordiv__(self, other: NumLike) -> Any:
return add_op('floordiv', [other, self])
def __neg__(self: TCPNum) -> TCPNum:
if self.dtype == 'int':
return cast(TCPNum, add_op('sub', [variable(0), self]))
return cast(TCPNum, add_op('sub', [variable(0.0), self]))
return cast(TCPNum, add_op('sub', [value(0), self]))
return cast(TCPNum, add_op('sub', [value(0.0), self]))
def __gt__(self, other: TVarNumb) -> 'variable[int]':
def __gt__(self, other: TVarNumb) -> 'value[int]':
ret = add_op('gt', [self, other])
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
def __lt__(self, other: TVarNumb) -> 'variable[int]':
def __lt__(self, other: TVarNumb) -> 'value[int]':
ret = add_op('gt', [other, self])
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
def __ge__(self, other: TVarNumb) -> 'variable[int]':
def __ge__(self, other: TVarNumb) -> 'value[int]':
ret = add_op('ge', [self, other])
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
def __le__(self, other: TVarNumb) -> 'variable[int]':
def __le__(self, other: TVarNumb) -> 'value[int]':
ret = add_op('ge', [other, self])
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
def __eq__(self, other: TVarNumb) -> 'variable[int]': # type: ignore
def __eq__(self, other: TVarNumb) -> 'value[int]': # type: ignore
ret = add_op('eq', [self, other], True)
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
def __ne__(self, other: TVarNumb) -> 'variable[int]': # type: ignore
def __ne__(self, other: TVarNumb) -> 'value[int]': # type: ignore
ret = add_op('ne', [self, other], True)
return variable(ret.source, dtype='bool')
return value(ret.source, dtype='bool')
@overload
def __mod__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __mod__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __mod__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __mod__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __mod__(self, other: unifloat) -> 'variable[float]': ...
def __mod__(self, other: unifloat) -> 'value[float]': ...
@overload
def __mod__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __mod__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __mod__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __mod__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __mod__(self, other: TVarNumb) -> Any:
return add_op('mod', [self, other])
@overload
def __rmod__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __rmod__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __rmod__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __rmod__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __rmod__(self, other: float) -> 'variable[float]': ...
def __rmod__(self, other: float) -> 'value[float]': ...
def __rmod__(self, other: NumLike) -> Any:
return add_op('mod', [other, self])
@overload
def __pow__(self: 'variable[TNum]', other: 'variable[TNum] | TNum') -> 'variable[TNum]': ...
def __pow__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
@overload
def __pow__(self: 'variable[int]', other: uniint) -> 'variable[int]': ...
def __pow__(self: 'value[int]', other: uniint) -> 'value[int]': ...
@overload
def __pow__(self, other: unifloat) -> 'variable[float]': ...
def __pow__(self, other: unifloat) -> 'value[float]': ...
@overload
def __pow__(self: 'variable[float]', other: NumLike) -> 'variable[float]': ...
def __pow__(self: 'value[float]', other: NumLike) -> 'value[float]': ...
@overload
def __pow__(self, other: TVarNumb) -> 'variable[float] | variable[int]': ...
def __pow__(self, other: TVarNumb) -> 'value[float] | value[int]': ...
def __pow__(self, other: TVarNumb) -> Any:
return cp.pow(self, other)
@overload
def __rpow__(self: 'variable[TNum]', other: TNum) -> 'variable[TNum]': ...
def __rpow__(self: 'value[TNum]', other: TNum) -> 'value[TNum]': ...
@overload
def __rpow__(self: 'variable[int]', other: int) -> 'variable[int]': ...
def __rpow__(self: 'value[int]', other: int) -> 'value[int]': ...
@overload
def __rpow__(self, other: float) -> 'variable[float]': ...
def __rpow__(self, other: float) -> 'value[float]': ...
def __rpow__(self, other: NumLike) -> Any:
return cp.pow(other, self)
@ -296,34 +296,34 @@ class variable(Generic[TNum], Net):
return super().__hash__()
# Bitwise and shift operations for cp[int]
def __lshift__(self, other: uniint) -> 'variable[int]':
def __lshift__(self, other: uniint) -> 'value[int]':
return add_op('lshift', [self, other])
def __rlshift__(self, other: uniint) -> 'variable[int]':
def __rlshift__(self, other: uniint) -> 'value[int]':
return add_op('lshift', [other, self])
def __rshift__(self, other: uniint) -> 'variable[int]':
def __rshift__(self, other: uniint) -> 'value[int]':
return add_op('rshift', [self, other])
def __rrshift__(self, other: uniint) -> 'variable[int]':
def __rrshift__(self, other: uniint) -> 'value[int]':
return add_op('rshift', [other, self])
def __and__(self, other: uniint) -> 'variable[int]':
def __and__(self, other: uniint) -> 'value[int]':
return add_op('bwand', [self, other], True)
def __rand__(self, other: uniint) -> 'variable[int]':
def __rand__(self, other: uniint) -> 'value[int]':
return add_op('bwand', [other, self], True)
def __or__(self, other: uniint) -> 'variable[int]':
def __or__(self, other: uniint) -> 'value[int]':
return add_op('bwor', [self, other], True)
def __ror__(self, other: uniint) -> 'variable[int]':
def __ror__(self, other: uniint) -> 'value[int]':
return add_op('bwor', [other, self], True)
def __xor__(self, other: uniint) -> 'variable[int]':
def __xor__(self, other: uniint) -> 'value[int]':
return add_op('bwxor', [self, other], True)
def __rxor__(self, other: uniint) -> 'variable[int]':
def __rxor__(self, other: uniint) -> 'value[int]':
return add_op('bwxor', [other, self], True)
@ -356,30 +356,30 @@ class Op(Node):
self.node_hash = self.get_node_hash(commutative)
def net_from_value(value: Any) -> variable[Any]:
vi = CPConstant(value)
return variable(vi, vi.dtype)
def net_from_value(val: Any) -> value[Any]:
vi = CPConstant(val)
return value(vi, vi.dtype)
@overload
def iif(expression: variable[Any], true_result: uniint, false_result: uniint) -> variable[int]: ... # pyright: ignore[reportOverlappingOverload]
def iif(expression: value[Any], true_result: uniint, false_result: uniint) -> value[int]: ... # pyright: ignore[reportOverlappingOverload]
@overload
def iif(expression: variable[Any], true_result: unifloat, false_result: unifloat) -> variable[float]: ...
def iif(expression: value[Any], true_result: unifloat, false_result: unifloat) -> value[float]: ...
@overload
def iif(expression: float | int, true_result: TNum, false_result: TNum) -> TNum: ...
@overload
def iif(expression: float | int, true_result: TNum | variable[TNum], false_result: variable[TNum]) -> variable[TNum]: ...
def iif(expression: float | int, true_result: TNum | value[TNum], false_result: value[TNum]) -> value[TNum]: ...
@overload
def iif(expression: float | int, true_result: variable[TNum], false_result: TNum | variable[TNum]) -> variable[TNum]: ...
def iif(expression: float | int, true_result: value[TNum], false_result: TNum | value[TNum]) -> value[TNum]: ...
@overload
def iif(expression: float | int | variable[Any], true_result: TNum | variable[TNum], false_result: TNum | variable[TNum]) -> variable[TNum] | TNum: ...
def iif(expression: float | int | value[Any], true_result: TNum | value[TNum], false_result: TNum | value[TNum]) -> value[TNum] | TNum: ...
def iif(expression: Any, true_result: Any, false_result: Any) -> Any:
allowed_type = (variable, int, float)
allowed_type = (value, int, float)
assert isinstance(true_result, allowed_type) and isinstance(false_result, allowed_type), "Result type not supported"
return (expression != 0) * true_result + (expression == 0) * false_result
def add_op(op: str, args: list[variable[Any] | int | float], commutative: bool = False) -> variable[Any]:
def add_op(op: str, args: list[value[Any] | int | float], commutative: bool = False) -> value[Any]:
arg_nets = [a if isinstance(a, Net) else net_from_value(a) for a in args]
if commutative:
@ -393,9 +393,9 @@ def add_op(op: str, args: list[variable[Any] | int | float], commutative: bool =
result_type = generic_sdb.stencil_definitions[typed_op].split('_')[0]
if result_type == 'float':
return variable[float](Op(typed_op, arg_nets, commutative), result_type)
return value[float](Op(typed_op, arg_nets, commutative), result_type)
else:
return variable[int](Op(typed_op, arg_nets, commutative), result_type)
return value[int](Op(typed_op, arg_nets, commutative), result_type)
def _get_data_and_dtype(value: Any) -> tuple[str, float | int]:

View File

@ -1,18 +1,18 @@
from . import vector
from ._vectors import VecNumLike
from . import variable, NumLike
from . import value, NumLike
from typing import TypeVar, Any, overload, Callable
from ._basic_types import add_op, unifloat
import math
T = TypeVar("T", int, float, variable[int], variable[float])
T = TypeVar("T", int, float, value[int], value[float])
U = TypeVar("U", int, float)
@overload
def exp(x: float | int) -> float: ...
@overload
def exp(x: variable[Any]) -> variable[float]: ...
def exp(x: value[Any]) -> value[float]: ...
@overload
def exp(x: vector[Any]) -> vector[float]: ...
def exp(x: Any) -> Any:
@ -24,7 +24,7 @@ def exp(x: Any) -> Any:
Returns:
result of e**x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('exp', [x])
if isinstance(x, vector):
return x.map(exp)
@ -34,7 +34,7 @@ def exp(x: Any) -> Any:
@overload
def log(x: float | int) -> float: ...
@overload
def log(x: variable[Any]) -> variable[float]: ...
def log(x: value[Any]) -> value[float]: ...
@overload
def log(x: vector[Any]) -> vector[float]: ...
def log(x: Any) -> Any:
@ -46,7 +46,7 @@ def log(x: Any) -> Any:
Returns:
result of ln(x)
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('log', [x])
if isinstance(x, vector):
return x.map(log)
@ -56,9 +56,9 @@ def log(x: Any) -> Any:
@overload
def pow(x: float | int, y: float | int) -> float: ...
@overload
def pow(x: variable[Any], y: NumLike) -> variable[float]: ...
def pow(x: value[Any], y: NumLike) -> value[float]: ...
@overload
def pow(x: NumLike, y: variable[Any]) -> variable[float]: ...
def pow(x: NumLike, y: value[Any]) -> value[float]: ...
@overload
def pow(x: vector[Any], y: Any) -> vector[float]: ...
def pow(x: VecNumLike, y: VecNumLike) -> Any:
@ -81,7 +81,7 @@ def pow(x: VecNumLike, y: VecNumLike) -> Any:
return m
if y == -1:
return 1 / x
if isinstance(x, variable) or isinstance(y, variable):
if isinstance(x, value) or isinstance(y, value):
return add_op('pow', [x, y])
else:
return float(x ** y)
@ -90,7 +90,7 @@ def pow(x: VecNumLike, y: VecNumLike) -> Any:
@overload
def sqrt(x: float | int) -> float: ...
@overload
def sqrt(x: variable[Any]) -> variable[float]: ...
def sqrt(x: value[Any]) -> value[float]: ...
@overload
def sqrt(x: vector[Any]) -> vector[float]: ...
def sqrt(x: Any) -> Any:
@ -102,7 +102,7 @@ def sqrt(x: Any) -> Any:
Returns:
Square root of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('sqrt', [x])
if isinstance(x, vector):
return x.map(sqrt)
@ -112,7 +112,7 @@ def sqrt(x: Any) -> Any:
@overload
def sin(x: float | int) -> float: ...
@overload
def sin(x: variable[Any]) -> variable[float]: ...
def sin(x: value[Any]) -> value[float]: ...
@overload
def sin(x: vector[Any]) -> vector[float]: ...
def sin(x: Any) -> Any:
@ -124,7 +124,7 @@ def sin(x: Any) -> Any:
Returns:
Square root of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('sin', [x])
if isinstance(x, vector):
return x.map(sin)
@ -134,7 +134,7 @@ def sin(x: Any) -> Any:
@overload
def cos(x: float | int) -> float: ...
@overload
def cos(x: variable[Any]) -> variable[float]: ...
def cos(x: value[Any]) -> value[float]: ...
@overload
def cos(x: vector[Any]) -> vector[float]: ...
def cos(x: Any) -> Any:
@ -146,7 +146,7 @@ def cos(x: Any) -> Any:
Returns:
Cosine of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('cos', [x])
if isinstance(x, vector):
return x.map(cos)
@ -156,7 +156,7 @@ def cos(x: Any) -> Any:
@overload
def tan(x: float | int) -> float: ...
@overload
def tan(x: variable[Any]) -> variable[float]: ...
def tan(x: value[Any]) -> value[float]: ...
@overload
def tan(x: vector[Any]) -> vector[float]: ...
def tan(x: Any) -> Any:
@ -168,7 +168,7 @@ def tan(x: Any) -> Any:
Returns:
Tangent of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('tan', [x])
if isinstance(x, vector):
#return x.map(tan)
@ -179,7 +179,7 @@ def tan(x: Any) -> Any:
@overload
def atan(x: float | int) -> float: ...
@overload
def atan(x: variable[Any]) -> variable[float]: ...
def atan(x: value[Any]) -> value[float]: ...
@overload
def atan(x: vector[Any]) -> vector[float]: ...
def atan(x: Any) -> Any:
@ -191,7 +191,7 @@ def atan(x: Any) -> Any:
Returns:
Inverse tangent of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('atan', [x])
if isinstance(x, vector):
return x.map(atan)
@ -201,9 +201,9 @@ def atan(x: Any) -> Any:
@overload
def atan2(x: float | int, y: float | int) -> float: ...
@overload
def atan2(x: variable[Any], y: NumLike) -> variable[float]: ...
def atan2(x: value[Any], y: NumLike) -> value[float]: ...
@overload
def atan2(x: NumLike, y: variable[Any]) -> variable[float]: ...
def atan2(x: NumLike, y: value[Any]) -> value[float]: ...
@overload
def atan2(x: vector[float], y: VecNumLike) -> vector[float]: ...
@overload
@ -220,7 +220,7 @@ def atan2(x: VecNumLike, y: VecNumLike) -> Any:
"""
if isinstance(x, vector) or isinstance(y, vector):
return _map2(x, y, atan2)
if isinstance(x, variable) or isinstance(y, variable):
if isinstance(x, value) or isinstance(y, value):
return add_op('atan2', [x, y])
return math.atan2(x, y)
@ -228,7 +228,7 @@ def atan2(x: VecNumLike, y: VecNumLike) -> Any:
@overload
def asin(x: float | int) -> float: ...
@overload
def asin(x: variable[Any]) -> variable[float]: ...
def asin(x: value[Any]) -> value[float]: ...
@overload
def asin(x: vector[Any]) -> vector[float]: ...
def asin(x: Any) -> Any:
@ -240,7 +240,7 @@ def asin(x: Any) -> Any:
Returns:
Inverse sine of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('asin', [x])
if isinstance(x, vector):
return x.map(asin)
@ -250,7 +250,7 @@ def asin(x: Any) -> Any:
@overload
def acos(x: float | int) -> float: ...
@overload
def acos(x: variable[Any]) -> variable[float]: ...
def acos(x: value[Any]) -> value[float]: ...
@overload
def acos(x: vector[Any]) -> vector[float]: ...
def acos(x: Any) -> Any:
@ -262,7 +262,7 @@ def acos(x: Any) -> Any:
Returns:
Inverse cosine of x
"""
if isinstance(x, variable):
if isinstance(x, value):
return add_op('acos', [x])
if isinstance(x, vector):
return x.map(acos)
@ -272,10 +272,10 @@ def acos(x: Any) -> Any:
@overload
def get_42(x: float | int) -> float: ...
@overload
def get_42(x: variable[Any]) -> variable[float]: ...
def get_42(x: NumLike) -> variable[float] | float:
"""Returns the variable representing the constant 42"""
if isinstance(x, variable):
def get_42(x: value[Any]) -> value[float]: ...
def get_42(x: NumLike) -> value[float] | float:
"""Returns the value representing the constant 42"""
if isinstance(x, value):
return add_op('get_42', [x, x])
return float((int(x) * 3.0 + 42.0) * 5.0 + 21.0)
@ -284,10 +284,10 @@ def get_42(x: NumLike) -> variable[float] | float:
@overload
def abs(x: U) -> U: ...
@overload
def abs(x: variable[U]) -> variable[U]: ...
def abs(x: value[U]) -> value[U]: ...
@overload
def abs(x: vector[U]) -> vector[U]: ...
def abs(x: U | variable[U] | vector[U]) -> Any:
def abs(x: U | value[U] | vector[U]) -> Any:
"""Absolute value function
Arguments:
@ -304,10 +304,10 @@ def abs(x: U | variable[U] | vector[U]) -> Any:
@overload
def sign(x: U) -> U: ...
@overload
def sign(x: variable[U]) -> variable[U]: ...
def sign(x: value[U]) -> value[U]: ...
@overload
def sign(x: vector[U]) -> vector[U]: ...
def sign(x: U | variable[U] | vector[U]) -> Any:
def sign(x: U | value[U] | vector[U]) -> Any:
"""Return 1 for positive numbers and -1 for negative numbers.
For an input of 0 the return value is 0.
@ -322,16 +322,16 @@ def sign(x: U | variable[U] | vector[U]) -> Any:
@overload
def clamp(x: variable[U], min_value: U | variable[U], max_value: U | variable[U]) -> variable[U]: ...
def clamp(x: value[U], min_value: U | value[U], max_value: U | value[U]) -> value[U]: ...
@overload
def clamp(x: U | variable[U], min_value: variable[U], max_value: U | variable[U]) -> variable[U]: ...
def clamp(x: U | value[U], min_value: value[U], max_value: U | value[U]) -> value[U]: ...
@overload
def clamp(x: U | variable[U], min_value: U | variable[U], max_value: variable[U]) -> variable[U]: ...
def clamp(x: U | value[U], min_value: U | value[U], max_value: value[U]) -> value[U]: ...
@overload
def clamp(x: U, min_value: U, max_value: U) -> U: ...
@overload
def clamp(x: vector[U], min_value: 'U | variable[U]', max_value: 'U | variable[U]') -> vector[U]: ...
def clamp(x: U | variable[U] | vector[U], min_value: U | variable[U], max_value: U | variable[U]) -> Any:
def clamp(x: vector[U], min_value: 'U | value[U]', max_value: 'U | value[U]') -> vector[U]: ...
def clamp(x: U | value[U] | vector[U], min_value: U | value[U], max_value: U | value[U]) -> Any:
"""Clamp function to limit a value between a minimum and maximum.
Arguments:
@ -351,12 +351,12 @@ def clamp(x: U | variable[U] | vector[U], min_value: U | variable[U], max_value:
@overload
def min(x: variable[U], y: U | variable[U]) -> variable[U]: ...
def min(x: value[U], y: U | value[U]) -> value[U]: ...
@overload
def min(x: U | variable[U], y: variable[U]) -> variable[U]: ...
def min(x: U | value[U], y: value[U]) -> value[U]: ...
@overload
def min(x: U, y: U) -> U: ...
def min(x: U | variable[U], y: U | variable[U]) -> Any:
def min(x: U | value[U], y: U | value[U]) -> Any:
"""Minimum function to get the smaller of two values.
Arguments:
@ -370,12 +370,12 @@ def min(x: U | variable[U], y: U | variable[U]) -> Any:
@overload
def max(x: variable[U], y: U | variable[U]) -> variable[U]: ...
def max(x: value[U], y: U | value[U]) -> value[U]: ...
@overload
def max(x: U | variable[U], y: variable[U]) -> variable[U]: ...
def max(x: U | value[U], y: value[U]) -> value[U]: ...
@overload
def max(x: U, y: U) -> U: ...
def max(x: U | variable[U], y: U | variable[U]) -> Any:
def max(x: U | value[U], y: U | value[U]) -> Any:
"""Maximum function to get the larger of two values.
Arguments:
@ -389,16 +389,16 @@ def max(x: U | variable[U], y: U | variable[U]) -> Any:
@overload
def lerp(v1: variable[U], v2: U | variable[U], t: unifloat) -> variable[U]: ...
def lerp(v1: value[U], v2: U | value[U], t: unifloat) -> value[U]: ...
@overload
def lerp(v1: U | variable[U], v2: variable[U], t: unifloat) -> variable[U]: ...
def lerp(v1: U | value[U], v2: value[U], t: unifloat) -> value[U]: ...
@overload
def lerp(v1: U | variable[U], v2: U | variable[U], t: variable[float]) -> variable[U]: ...
def lerp(v1: U | value[U], v2: U | value[U], t: value[float]) -> value[U]: ...
@overload
def lerp(v1: U, v2: U, t: float) -> U: ...
@overload
def lerp(v1: vector[U], v2: vector[U], t: unifloat) -> vector[U]: ...
def lerp(v1: U | variable[U] | vector[U], v2: U | variable[U] | vector[U], t: unifloat) -> Any:
def lerp(v1: U | value[U] | vector[U], v2: U | value[U] | vector[U], t: unifloat) -> Any:
"""Linearly interpolate between two values or vectors v1 and v2 by a factor t."""
if isinstance(v1, vector) or isinstance(v2, vector):
assert isinstance(v1, vector) and isinstance(v2, vector), "None or both v1 and v2 must be vectors."
@ -410,16 +410,16 @@ def lerp(v1: U | variable[U] | vector[U], v2: U | variable[U] | vector[U], t: u
@overload
def relu(x: U) -> U: ...
@overload
def relu(x: variable[U]) -> variable[U]: ...
def relu(x: value[U]) -> value[U]: ...
@overload
def relu(x: vector[U]) -> vector[U]: ...
def relu(x: U | variable[U] | vector[U]) -> Any:
def relu(x: U | value[U] | vector[U]) -> Any:
"""Returns x for x > 0 and otherwise 0."""
ret = (x > 0) * x
return ret
def _map2(self: VecNumLike, other: VecNumLike, func: Callable[[Any, Any], variable[U] | U]) -> vector[U]:
def _map2(self: VecNumLike, other: VecNumLike, func: Callable[[Any, Any], value[U] | U]) -> vector[U]:
"""Applies a function to each element of the vector and a second vector or scalar."""
if isinstance(self, vector) and isinstance(other, vector):
return vector(func(x, y) for x, y in zip(self.values, other.values))

View File

@ -1,23 +1,23 @@
from . import variable
from . import value
from ._vectors import vector
from ._mixed import mixed_sum
from typing import TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
from ._helper_types import TNum
MatNumLike: TypeAlias = 'matrix[int] | matrix[float] | variable[int] | variable[float] | int | float'
MatIntLike: TypeAlias = 'matrix[int] | variable[int] | int'
MatFloatLike: TypeAlias = 'matrix[float] | variable[float] | float'
MatNumLike: TypeAlias = 'matrix[int] | matrix[float] | value[int] | value[float] | int | float'
MatIntLike: TypeAlias = 'matrix[int] | value[int] | int'
MatFloatLike: TypeAlias = 'matrix[float] | value[float] | float'
U = TypeVar("U", int, float)
class matrix(Generic[TNum]):
"""Mathematical matrix class supporting basic operations and interactions with variables.
"""Mathematical matrix class supporting basic operations and interactions with values.
"""
def __init__(self, values: Iterable[Iterable[TNum | variable[TNum]]] | vector[TNum]):
"""Create a matrix with given values and variables.
def __init__(self, values: Iterable[Iterable[TNum | value[TNum]]] | vector[TNum]):
"""Create a matrix with given values.
Args:
values: iterable of iterable of constant values and variables
values: iterable of iterable of constant values
"""
if isinstance(values, vector):
rows = [values.values]
@ -27,7 +27,7 @@ class matrix(Generic[TNum]):
if rows:
row_len = len(rows[0])
assert all(len(row) == row_len for row in rows), "All rows must have the same length"
self.values: tuple[tuple[variable[TNum] | TNum, ...], ...] = tuple(rows)
self.values: tuple[tuple[value[TNum] | TNum, ...], ...] = tuple(rows)
self.rows = len(self.values)
self.cols = len(self.values[0]) if self.values else 0
@ -41,7 +41,7 @@ class matrix(Generic[TNum]):
@overload
def __getitem__(self, key: int) -> vector[TNum]: ...
@overload
def __getitem__(self, key: tuple[int, int]) -> variable[TNum] | TNum: ...
def __getitem__(self, key: tuple[int, int]) -> value[TNum] | TNum: ...
def __getitem__(self, key: int | tuple[int, int]) -> Any:
"""Get a row as a vector or a specific element.
Args:
@ -56,7 +56,7 @@ class matrix(Generic[TNum]):
else:
return vector(self.values[key])
def __iter__(self) -> Iterator[tuple[variable[TNum] | TNum, ...]]:
def __iter__(self) -> Iterator[tuple[value[TNum] | TNum, ...]]:
return iter(self.values)
def __neg__(self) -> 'matrix[TNum]':
@ -86,7 +86,7 @@ class matrix(Generic[TNum]):
@overload
def __radd__(self: 'matrix[float]', other: MatNumLike) -> 'matrix[float]': ...
@overload
def __radd__(self: 'matrix[int]', other: variable[int] | int) -> 'matrix[int]': ...
def __radd__(self: 'matrix[int]', other: value[int] | int) -> 'matrix[int]': ...
def __radd__(self, other: Any) -> Any:
return self + other
@ -114,7 +114,7 @@ class matrix(Generic[TNum]):
@overload
def __rsub__(self: 'matrix[float]', other: MatNumLike) -> 'matrix[float]': ...
@overload
def __rsub__(self: 'matrix[int]', other: variable[int] | int) -> 'matrix[int]': ...
def __rsub__(self: 'matrix[int]', other: value[int] | int) -> 'matrix[int]': ...
def __rsub__(self, other: MatNumLike) -> Any:
if isinstance(other, matrix):
assert self.rows == other.rows and self.cols == other.cols, \
@ -153,7 +153,7 @@ class matrix(Generic[TNum]):
@overload
def __rmul__(self: 'matrix[float]', other: MatNumLike) -> 'matrix[float]': ...
@overload
def __rmul__(self: 'matrix[int]', other: variable[int] | int) -> 'matrix[int]': ...
def __rmul__(self: 'matrix[int]', other: value[int] | int) -> 'matrix[int]': ...
def __rmul__(self, other: MatNumLike) -> Any:
return self * other
@ -199,9 +199,9 @@ class matrix(Generic[TNum]):
assert isinstance(other, matrix), "Cannot multiply matrix with {type(other)}"
assert self.cols == other.rows, \
f"Matrix columns ({self.cols}) must match other matrix rows ({other.rows})"
result: list[list[TNum | variable[TNum]]] = []
result: list[list[TNum | value[TNum]]] = []
for row in self.values:
new_row: list[TNum | variable[TNum]] = []
new_row: list[TNum | value[TNum]] = []
for col_idx in range(other.cols):
col = tuple(other.values[i][col_idx] for i in range(other.rows))
element = sum(a * b for a, b in zip(row, col))
@ -238,27 +238,27 @@ class matrix(Generic[TNum]):
return vector(self.values[i][index] for i in range(self.rows))
@overload
def trace(self: 'matrix[TNum]') -> TNum | variable[TNum]: ...
def trace(self: 'matrix[TNum]') -> TNum | value[TNum]: ...
@overload
def trace(self: 'matrix[int]') -> int | variable[int]: ...
def trace(self: 'matrix[int]') -> int | value[int]: ...
@overload
def trace(self: 'matrix[float]') -> float | variable[float]: ...
def trace(self: 'matrix[float]') -> float | value[float]: ...
def trace(self) -> Any:
"""Calculate the trace (sum of diagonal elements)."""
assert self.rows == self.cols, "Trace is only defined for square matrices"
return mixed_sum(self.values[i][i] for i in range(self.rows))
@overload
def sum(self: 'matrix[TNum]') -> TNum | variable[TNum]: ...
def sum(self: 'matrix[TNum]') -> TNum | value[TNum]: ...
@overload
def sum(self: 'matrix[int]') -> int | variable[int]: ...
def sum(self: 'matrix[int]') -> int | value[int]: ...
@overload
def sum(self: 'matrix[float]') -> float | variable[float]: ...
def sum(self: 'matrix[float]') -> float | value[float]: ...
def sum(self) -> Any:
"""Calculate the sum of all elements."""
return mixed_sum(a for row in self.values for a in row)
def map(self, func: Callable[[Any], variable[U] | U]) -> 'matrix[U]':
def map(self, func: Callable[[Any], value[U] | U]) -> 'matrix[U]':
"""Applies a function to each element of the matrix and returns a new matrix."""
return matrix(
tuple(func(a) for a in row)
@ -266,10 +266,10 @@ class matrix(Generic[TNum]):
)
def homogenize(self) -> 'matrix[TNum]':
"""Convert all elements to variables if any element is a variable."""
if any(isinstance(val, variable) for row in self.values for val in row):
"""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(variable(val) if not isinstance(val, variable) else val for val in row)
tuple(value(val) if not isinstance(val, value) else val for val in row)
for row in self.values
)
else:

View File

@ -1,24 +1,24 @@
from . import variable
from . import value
from typing import TypeVar, Iterable, Any, overload
T = TypeVar("T", int, float)
@overload
def mixed_sum(scalars: Iterable[float | variable[float]]) -> float | variable[float]: ...
def mixed_sum(scalars: Iterable[float | value[float]]) -> float | value[float]: ...
@overload
def mixed_sum(scalars: Iterable[int | variable[int]]) -> int | variable[int]: ...
def mixed_sum(scalars: Iterable[int | value[int]]) -> int | value[int]: ...
@overload
def mixed_sum(scalars: Iterable[T | variable[T]]) -> T | variable[T]: ...
def mixed_sum(scalars: Iterable[int | float | variable[Any]]) -> Any:
def mixed_sum(scalars: Iterable[T | value[T]]) -> T | value[T]: ...
def mixed_sum(scalars: Iterable[int | float | value[Any]]) -> Any:
sl = list(scalars)
return sum(a for a in sl if not isinstance(a, variable)) +\
sum(a for a in sl if isinstance(a, variable))
return sum(a for a in sl if not isinstance(a, value)) +\
sum(a for a in sl if isinstance(a, value))
def mixed_homogenize(scalars: Iterable[T | variable[T]]) -> Iterable[T] | Iterable[variable[T]]:
if any(isinstance(val, variable) for val in scalars):
return (variable(val) if not isinstance(val, variable) else val for val in scalars)
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)
else:
return (val for val in scalars if not isinstance(val, variable))
return (val for val in scalars if not isinstance(val, value))

View File

@ -3,7 +3,7 @@ from . import _binwrite as binw
from coparun_module import coparun, read_data_mem
import struct
from ._basic_types import stencil_db_from_package
from ._basic_types import variable, Net, Node, Write, NumLike
from ._basic_types import value, Net, Node, Write, NumLike
from ._compiler import compile_to_dag
T = TypeVar("T", int, float)
@ -28,16 +28,16 @@ class Target():
optimization: Optimization level
"""
self.sdb = stencil_db_from_package(arch, optimization)
self._variables: dict[Net, tuple[int, int, str]] = {}
self._values: dict[Net, tuple[int, int, str]] = {}
def compile(self, *variables: int | float | variable[int] | variable[float] | Iterable[int | float | variable[int] | variable[float]]) -> None:
"""Compiles the code to compute the given variables.
def compile(self, *values: int | float | value[int] | value[float] | Iterable[int | float | value[int] | value[float]]) -> None:
"""Compiles the code to compute the given values.
Arguments:
variables: Variables to compute
values: Values to compute
"""
nodes: list[Node] = []
for s in variables:
for s in values:
if isinstance(s, Iterable):
for net in s:
if isinstance(net, Net):
@ -46,7 +46,7 @@ class Target():
if isinstance(s, Net):
nodes.append(Write(s))
dw, self._variables = compile_to_dag(nodes, self.sdb)
dw, self._values = compile_to_dag(nodes, self.sdb)
dw.write_com(binw.Command.END_COM)
assert coparun(dw.get_data()) > 0
@ -59,56 +59,56 @@ class Target():
assert coparun(dw.get_data()) > 0
@overload
def read_value(self, net: variable[T]) -> T: ...
def read_value(self, net: value[T]) -> T: ...
@overload
def read_value(self, net: NumLike) -> float | int | bool: ...
@overload
def read_value(self, net: Iterable[T | variable[T]]) -> list[T]: ...
def read_value(self, net: NumLike | variable[T] | Iterable[T | variable[T]]) -> Any:
"""Reads the value of a variable.
def read_value(self, net: Iterable[T | value[T]]) -> list[T]: ...
def read_value(self, net: NumLike | value[T] | Iterable[T | value[T]]) -> Any:
"""Reads the numeric value of a copapy type.
Arguments:
net: Variable to read
net: Values to read
Returns:
Value of the variable
Numeric value
"""
if isinstance(net, Iterable):
return [self.read_value(ni) if isinstance(ni, variable) else ni for ni in net]
return [self.read_value(ni) if isinstance(ni, value) else ni for ni in net]
if isinstance(net, float | int):
print("Warning: value is not a copypy value")
return net
assert isinstance(net, Net), "Variable must be a copapy variable object"
assert net in self._variables, f"Variable {net} not found. It might not have been compiled for the target."
addr, lengths, var_type = self._variables[net]
assert isinstance(net, Net), "Argument must be a copapy value"
assert net in self._values, f"Value {net} not found. It might not have been compiled for the target."
addr, lengths, var_type = self._values[net]
assert lengths > 0
data = read_data_mem(addr, lengths)
assert data is not None and len(data) == lengths, f"Failed to read variable {net}"
assert data is not None and len(data) == lengths, f"Failed to read value {net}"
en = {'little': '<', 'big': '>'}[self.sdb.byteorder]
if var_type == 'float':
if lengths == 4:
value = struct.unpack(en + 'f', data)[0]
val = struct.unpack(en + 'f', data)[0]
elif lengths == 8:
value = struct.unpack(en + 'd', data)[0]
val = struct.unpack(en + 'd', data)[0]
else:
raise ValueError(f"Unsupported float length: {lengths} bytes")
assert isinstance(value, float)
return value
assert isinstance(val, float)
return val
elif var_type == 'int':
assert lengths in (1, 2, 4, 8), f"Unsupported int length: {lengths} bytes"
value = int.from_bytes(data, byteorder=self.sdb.byteorder, signed=True)
return value
val = int.from_bytes(data, byteorder=self.sdb.byteorder, signed=True)
return val
elif var_type == 'bool':
assert lengths in (1, 2, 4, 8), f"Unsupported int length: {lengths} bytes"
value = bool.from_bytes(data, byteorder=self.sdb.byteorder, signed=True)
return value
val = bool.from_bytes(data, byteorder=self.sdb.byteorder, signed=True)
return val
else:
raise ValueError(f"Unsupported variable type: {var_type}")
raise ValueError(f"Unsupported value type: {var_type}")
def read_value_remote(self, net: Net) -> None:
"""Reads the raw data of a variable by the runner."""
"""Reads the raw data of a value by the runner."""
dw = binw.data_writer(self.sdb.byteorder)
add_read_command(dw, self._variables, net)
add_read_command(dw, self._values, net)
assert coparun(dw.get_data()) > 0

View File

@ -1,28 +1,28 @@
from . import variable
from . import value
from ._mixed import mixed_sum, mixed_homogenize
from typing import TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
import copapy as cp
from ._helper_types import TNum
#VecNumLike: TypeAlias = 'vector[int] | vector[float] | variable[int] | variable[float] | int | float | bool'
VecNumLike: TypeAlias = 'vector[Any] | variable[Any] | int | float | bool'
VecIntLike: TypeAlias = 'vector[int] | variable[int] | int'
VecFloatLike: TypeAlias = 'vector[float] | variable[float] | float'
#VecNumLike: TypeAlias = 'vector[int] | vector[float] | value[int] | value[float] | int | float | bool'
VecNumLike: TypeAlias = 'vector[Any] | value[Any] | int | float | bool'
VecIntLike: TypeAlias = 'vector[int] | value[int] | int'
VecFloatLike: TypeAlias = 'vector[float] | value[float] | float'
U = TypeVar("U", int, float)
epsilon = 1e-20
class vector(Generic[TNum]):
"""Mathematical vector class supporting basic operations and interactions with variables.
"""Mathematical vector class supporting basic operations and interactions with values.
"""
def __init__(self, values: Iterable[TNum | variable[TNum]]):
"""Create a vector with given values and variables.
def __init__(self, values: Iterable[TNum | value[TNum]]):
"""Create a vector with given values.
Args:
values: iterable of constant values and variables
values: iterable of constant values
"""
self.values: tuple[variable[TNum] | TNum, ...] = tuple(values)
self.values: tuple[value[TNum] | TNum, ...] = tuple(values)
def __repr__(self) -> str:
return f"vector({self.values})"
@ -31,10 +31,10 @@ class vector(Generic[TNum]):
return len(self.values)
@overload
def __getitem__(self, index: int) -> variable[TNum] | TNum: ...
def __getitem__(self, index: int) -> value[TNum] | TNum: ...
@overload
def __getitem__(self, index: slice) -> 'vector[TNum]': ...
def __getitem__(self, index: int | slice) -> 'vector[TNum] | variable[TNum] | TNum':
def __getitem__(self, index: int | slice) -> 'vector[TNum] | value[TNum] | TNum':
if isinstance(index, slice):
return vector(self.values[index])
return self.values[index]
@ -42,7 +42,7 @@ class vector(Generic[TNum]):
def __neg__(self) -> 'vector[TNum]':
return vector(-a for a in self.values)
def __iter__(self) -> Iterator[variable[TNum] | TNum]:
def __iter__(self) -> Iterator[value[TNum] | TNum]:
return iter(self.values)
@overload
@ -62,7 +62,7 @@ class vector(Generic[TNum]):
@overload
def __radd__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
@overload
def __radd__(self: 'vector[int]', other: variable[int] | int) -> 'vector[int]': ...
def __radd__(self: 'vector[int]', other: value[int] | int) -> 'vector[int]': ...
@overload
def __radd__(self, other: VecNumLike) -> 'vector[Any]': ...
def __radd__(self, other: Any) -> Any:
@ -85,7 +85,7 @@ class vector(Generic[TNum]):
@overload
def __rsub__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
@overload
def __rsub__(self: 'vector[int]', other: variable[int] | int) -> 'vector[int]': ...
def __rsub__(self: 'vector[int]', other: value[int] | int) -> 'vector[int]': ...
@overload
def __rsub__(self, other: VecNumLike) -> 'vector[Any]': ...
def __rsub__(self, other: VecNumLike) -> Any:
@ -111,7 +111,7 @@ class vector(Generic[TNum]):
@overload
def __rmul__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
@overload
def __rmul__(self: 'vector[int]', other: variable[int] | int) -> 'vector[int]': ...
def __rmul__(self: 'vector[int]', other: value[int] | int) -> 'vector[int]': ...
@overload
def __rmul__(self, other: VecNumLike) -> 'vector[Any]': ...
def __rmul__(self, other: VecNumLike) -> Any:
@ -134,7 +134,7 @@ class vector(Generic[TNum]):
@overload
def __rpow__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
@overload
def __rpow__(self: 'vector[int]', other: variable[int] | int) -> 'vector[int]': ...
def __rpow__(self: 'vector[int]', other: value[int] | int) -> 'vector[int]': ...
@overload
def __rpow__(self, other: VecNumLike) -> 'vector[Any]': ...
def __rpow__(self, other: VecNumLike) -> Any:
@ -153,26 +153,26 @@ class vector(Generic[TNum]):
return vector(other / a for a in self.values)
@overload
def dot(self: 'vector[int]', other: 'vector[int]') -> int | variable[int]: ...
def dot(self: 'vector[int]', other: 'vector[int]') -> int | value[int]: ...
@overload
def dot(self, other: 'vector[float]') -> float | variable[float]: ...
def dot(self, other: 'vector[float]') -> float | value[float]: ...
@overload
def dot(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | variable[float]: ...
def dot(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | value[float]: ...
@overload
def dot(self, other: 'vector[int] | vector[float]') -> float | int | variable[float] | variable[int]: ...
def dot(self, other: 'vector[int] | vector[float]') -> float | int | value[float] | value[int]: ...
def dot(self, other: 'vector[int] | vector[float]') -> Any:
assert len(self.values) == len(other.values), "Vectors must be of same length."
return mixed_sum(a * b for a, b in zip(self.values, other.values))
# @ operator
@overload
def __matmul__(self: 'vector[int]', other: 'vector[int]') -> int | variable[int]: ...
def __matmul__(self: 'vector[int]', other: 'vector[int]') -> int | value[int]: ...
@overload
def __matmul__(self, other: 'vector[float]') -> float | variable[float]: ...
def __matmul__(self, other: 'vector[float]') -> float | value[float]: ...
@overload
def __matmul__(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | variable[float]: ...
def __matmul__(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | value[float]: ...
@overload
def __matmul__(self, other: 'vector[int] | vector[float]') -> float | int | variable[float] | variable[int]: ...
def __matmul__(self, other: 'vector[int] | vector[float]') -> float | int | value[float] | value[int]: ...
def __matmul__(self, other: 'vector[int] | vector[float]') -> Any:
return self.dot(other)
@ -229,14 +229,14 @@ class vector(Generic[TNum]):
return (len(self.values),)
@overload
def sum(self: 'vector[int]') -> int | variable[int]: ...
def sum(self: 'vector[int]') -> int | value[int]: ...
@overload
def sum(self: 'vector[float]') -> float | variable[float]: ...
def sum(self: 'vector[float]') -> float | value[float]: ...
def sum(self) -> Any:
"""Sum of all vector elements."""
return mixed_sum(self.values)
def magnitude(self) -> 'float | variable[float]':
def magnitude(self) -> 'float | value[float]':
"""Magnitude (length) of the vector."""
s = mixed_sum(a * a for a in self.values)
return cp.sqrt(s)
@ -247,12 +247,12 @@ class vector(Generic[TNum]):
return self / mag
def homogenize(self) -> 'vector[TNum]':
if any(isinstance(val, variable) for val in self.values):
if any(isinstance(val, value) for val in self.values):
return vector(mixed_homogenize(self))
else:
return self
def map(self, func: Callable[[Any], variable[U] | U]) -> 'vector[U]':
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)
@ -262,18 +262,18 @@ def cross_product(v1: vector[float], v2: vector[float]) -> vector[float]:
return v1.cross(v2)
def dot_product(v1: vector[float], v2: vector[float]) -> 'float | variable[float]':
def dot_product(v1: vector[float], v2: vector[float]) -> 'float | value[float]':
"""Calculate the dot product of two vectors."""
return v1.dot(v2)
def distance(v1: vector[float], v2: vector[float]) -> 'float | variable[float]':
def distance(v1: vector[float], v2: vector[float]) -> 'float | value[float]':
"""Calculate the Euclidean distance between two vectors."""
diff = v1 - v2
return diff.magnitude()
def scalar_projection(v1: vector[float], v2: vector[float]) -> 'float | variable[float]':
def scalar_projection(v1: vector[float], v2: vector[float]) -> 'float | value[float]':
"""Calculate the scalar projection of v1 onto v2."""
dot_prod = v1.dot(v2)
mag_v2 = v2.magnitude() + epsilon
@ -288,7 +288,7 @@ def vector_projection(v1: vector[float], v2: vector[float]) -> vector[float]:
return v2 * scalar_proj
def angle_between(v1: vector[float], v2: vector[float]) -> 'float | variable[float]':
def angle_between(v1: vector[float], v2: vector[float]) -> 'float | value[float]':
"""Calculate the angle in radians between two vectors."""
dot_prod = v1.dot(v2)
mag_v1 = v1.magnitude()
@ -297,7 +297,7 @@ def angle_between(v1: vector[float], v2: vector[float]) -> 'float | variable[flo
return cp.acos(cos_angle)
def rotate_vector(v: vector[float], axis: vector[float], angle: 'float | variable[float]') -> vector[float]:
def rotate_vector(v: vector[float], axis: vector[float], angle: 'float | value[float]') -> vector[float]:
"""Rotate vector v around a given axis by a specified angle using Rodrigues' rotation formula."""
k = axis.normalize()
cos_angle = cp.cos(angle)

View File

@ -1,15 +1,15 @@
from . import variable, vector
from . import value, vector
from ._basic_types import iif, unifloat
from._helper_types import TNum
from typing import Any, Iterable
def homogenize_vector(input_values: Iterable[TNum | variable[TNum]]) -> Iterable[TNum] | Iterable[variable[TNum]]:
def homogenize_vector(input_values: Iterable[TNum | value[TNum]]) -> Iterable[TNum] | Iterable[value[TNum]]:
input_list = list(input_values)
if any(isinstance(val, variable) for val in input_list):
return (v if isinstance(v, variable) else variable(v) for v in input_list)
if any(isinstance(val, value) for val in input_list):
return (v if isinstance(v, value) else value(v) for v in input_list)
else:
return (v for v in input_list if not isinstance(v, variable))
return (v for v in input_list if not isinstance(v, value))
def _inv_argsort(input_vector: vector[TNum]) -> vector[int]:
@ -31,7 +31,7 @@ def argsort(input_vector: vector[TNum]) -> vector[int]:
return _inv_argsort(_inv_argsort(input_vector))
def median(input_vector: vector[TNum]) -> TNum | variable[TNum]:
def median(input_vector: vector[TNum]) -> TNum | value[TNum]:
"""
Applies a median filter to the input vector and returns the median as a unifloat.

View File

@ -27,8 +27,8 @@ def cp_vs_python(path: str):
#v_size = 400
iter_size = 30000
v1 = cp.vector(cp.variable(float(v)) for v in range(v_size))
v2 = cp.vector(cp.variable(float(v)) for v in [5]*v_size)
v1 = cp.vector(cp.value(float(v)) for v in range(v_size))
v2 = cp.vector(cp.value(float(v)) for v in [5]*v_size)
v3 = sum((v1 + i) @ v2 for i in range(sum_size))
@ -95,8 +95,8 @@ def cp_vs_python_sparse(path: str = 'benchmark_results_001_sparse.json'):
#v_size = 400
iter_size = 3000
v1 = cp.vector(cp.variable(float(v)) for v in range(v_size))
v2 = cp.vector(cp.variable(float(v)) for v in [5]*v_size)
v1 = cp.vector(cp.value(float(v)) for v in range(v_size))
v2 = cp.vector(cp.value(float(v)) for v in [5]*v_size)
test = cp.vector(np.linspace(0, 1, v_size))

View File

@ -1,4 +1,4 @@
from copapy import variable
from copapy import value
from copapy.backend import Write
import copapy.backend as cpb
@ -21,8 +21,8 @@ def test_ast_generation():
#r2 = i1 + 9
#out = [Write(r1), Write(r2)]
c1 = variable(4)
c2 = variable(2)
c1 = value(4)
c2 = value(2)
#i1 = c1 * 2
#r1 = i1 + 7 + (c2 + 7 * 9)
#r2 = i1 + 9

View File

@ -1,4 +1,4 @@
from copapy import variable, grad
from copapy import value, grad
import copapy as cp
import pytest
@ -6,8 +6,8 @@ import pytest
def test_autograd():
# Validate against micrograd results from Andrej Karpathy
# https://github.com/karpathy/micrograd/blob/master/test/test_engine.py
a = variable(-4.0)
b = variable(2.0)
a = value(-4.0)
b = value(2.0)
c = a + b
d = a * b + b**3
c += c + 1

View File

@ -1,4 +1,4 @@
from copapy import variable
from copapy import value
from copapy.backend import Write, compile_to_dag, add_read_command
import copapy as cp
import subprocess
@ -20,7 +20,7 @@ def test_compile():
test_vals = [0.0, -1.5, -2.0, -2.5, -3.0]
# Function with no passing-on-jump as last instruction:
ret_test = [r for v in test_vals for r in (cp.tan(variable(v)),)]
ret_test = [r for v in test_vals for r in (cp.tan(value(v)),)]
out = [Write(r) for r in ret_test]

View File

@ -9,9 +9,9 @@ from copapy._compiler import patch_entry, CPConstant, get_aux_func_layout
def test_timing_compiler():
t1 = cp.vector([10, 11]*128) + cp.vector(cp.variable(v) for v in range(256))
t1 = cp.vector([10, 11]*128) + cp.vector(cp.value(v) for v in range(256))
#t2 = t1.sum()
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(256))
t3 = cp.vector(cp.value(1 / (v + 1)) for v in range(256))
t5 = ((t3 * t1) * 2).magnitude()
out = [Write(t5)]

View File

@ -49,10 +49,10 @@ def test_compile():
#ret = function(c1, c2)
#ret = [c1 // 3.3 + 5]
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.variable(v) for v in range(3))
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.value(v) for v in range(3))
t2 = t1.sum()
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(3))
t3 = cp.vector(cp.value(1 / (v + 1)) for v in range(3))
t4 = ((t3 * t1) * 2).sum()
t5 = ((t3 * t1) * 2).magnitude()

View File

@ -43,10 +43,10 @@ def function(c1: NumLike, c2: NumLike) -> tuple[NumLike, ...]:
@pytest.mark.runner
def test_compile():
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.variable(v) for v in range(3))
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.value(v) for v in range(3))
t2 = t1.sum()
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(3))
t3 = cp.vector(cp.value(1 / (v + 1)) for v in range(3))
t4 = ((t3 * t1) * 2).sum()
t5 = ((t3 * t1) * 2).magnitude()

View File

@ -43,10 +43,10 @@ def function(c1: NumLike, c2: NumLike) -> tuple[NumLike, ...]:
@pytest.mark.runner
def test_compile():
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.variable(v) for v in range(3))
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.value(v) for v in range(3))
t2 = t1.sum()
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(3))
t3 = cp.vector(cp.value(1 / (v + 1)) for v in range(3))
t4 = ((t3 * t1) * 2).sum()
t5 = ((t3 * t1) * 2).magnitude()

View File

@ -1,4 +1,4 @@
from copapy import variable, NumLike
from copapy import value, NumLike
from copapy.backend import Write, compile_to_dag
import copapy
import subprocess
@ -22,7 +22,7 @@ def function(c1: NumLike) -> list[NumLike]:
@pytest.mark.runner
def test_compile():
c1 = variable(16)
c1 = value(16)
ret = function(c1)

View File

@ -1,4 +1,4 @@
from copapy import variable
from copapy import value
from copapy.backend import Write, compile_to_dag, add_read_command
import copapy as cp
import subprocess
@ -18,7 +18,7 @@ def run_command(command: list[str]) -> str:
def test_compile_sqrt():
test_vals = [0.0, 0.0001, 0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.28318530718, 100.0, 1000.0, 100000.0]
ret = [r for v in test_vals for r in (cp.sqrt(variable(v)),)]
ret = [r for v in test_vals for r in (cp.sqrt(value(v)),)]
out = [Write(r) for r in ret]
@ -52,7 +52,7 @@ def test_compile_sqrt():
def test_compile_log():
test_vals = [0.0, 0.0001, 0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.28318530718, 100.0, 1000.0, 100000.0]
ret = [r for v in test_vals for r in (cp.log(variable(v)),)]
ret = [r for v in test_vals for r in (cp.log(value(v)),)]
out = [Write(r) for r in ret]
@ -86,7 +86,7 @@ def test_compile_log():
def test_compile_sin():
test_vals = [0.0, 0.0001, 0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.28318530718, 100.0, 1000.0, 100000.0]
ret = [r for v in test_vals for r in (cp.sin(variable(v)),)]
ret = [r for v in test_vals for r in (cp.sin(value(v)),)]
out = [Write(r) for r in ret]

View File

@ -1,4 +1,4 @@
from copapy import variable, Target, NumLike
from copapy import value, Target, NumLike
import pytest
@ -14,7 +14,7 @@ def function(c1: NumLike) -> list[NumLike]:
def test_compile():
c1 = variable(16)
c1 = value(16)
ret = function(c1)

View File

@ -1,4 +1,4 @@
from copapy import variable, Target
from copapy import value, Target
import pytest
import copapy as cp
import math as ma
@ -7,8 +7,8 @@ import warnings
def test_fine():
a_i = 9
a_f = 2.5
c_i = variable(a_i)
c_f = variable(a_f)
c_i = value(a_i)
c_f = value(a_f)
# c_b = variable(True)
ret_test = (c_f ** 2,
@ -49,7 +49,7 @@ def test_fine():
print('* finished')
for test, val2, ref, name in zip(ret_test, re2_test, ret_refe, ('^2', '**-1', 'sqrt_int', 'sqrt_float', 'sin', 'cos', 'tan')):
assert isinstance(test, cp.variable)
assert isinstance(test, cp.value)
val = tg.read_value(test)
print('+', val, ref, type(val), test.dtype)
#for t in (int, float, bool):
@ -63,7 +63,7 @@ def test_trig_precision():
test_vals = [0.0, 0.0001, 0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.28318530718, 100.0, 1000.0, 100000.0,
-0.0001, -0.1, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0, -3.5, -4.0, -4.5, -5.0, -5.5, -6.0, -6.28318530718, -100.0, -1000.0, -100000.0]
ret_test = [r for v in test_vals for r in (cp.sin(variable(v)), cp.cos(variable(v)), cp.tan(variable(v)))]
ret_test = [r for v in test_vals for r in (cp.sin(value(v)), cp.cos(value(v)), cp.tan(value(v)))]
ret_refe = [r for v in test_vals for r in (ma.sin(v), ma.cos(v), ma.tan(v))]
tg = Target()
@ -72,7 +72,7 @@ def test_trig_precision():
for i, (v, test, ref) in enumerate(zip(test_vals, ret_test, ret_refe)):
func_name = ['sin', 'cos', 'tan'][i % 3]
assert isinstance(test, cp.variable)
assert isinstance(test, cp.value)
val = tg.read_value(test)
print(f"+ Result of {func_name}: {val}; reference: {ref}")
assert val == pytest.approx(ref, abs=1e-3), f"Result of {func_name} for input {test_vals[i // 3]} does not match: {val} and reference: {ref} (value: {v})" # pyright: ignore[reportUnknownMemberType]
@ -85,11 +85,11 @@ def test_arcus_trig_precision():
test_vals = [0.0, 0.01, 0.1, 0.5, 0.7, 0.9, 0.95,
-0.01, -0.1, -0.5, -0.7, -0.9, 0.95]
ret_test = [r for v in test_vals for r in (cp.asin(variable(v)),
cp.acos(variable(v)),
cp.atan(variable(v)),
cp.atan2(variable(v), variable(3)),
cp.atan2(variable(v), variable(-3)),)]
ret_test = [r for v in test_vals for r in (cp.asin(value(v)),
cp.acos(value(v)),
cp.atan(value(v)),
cp.atan2(value(v), value(3)),
cp.atan2(value(v), value(-3)),)]
ret_refe = [r for v in test_vals for r in (ma.asin(v),
ma.acos(v),
ma.atan(v),
@ -102,7 +102,7 @@ def test_arcus_trig_precision():
for i, (test, ref) in enumerate(zip(ret_test, ret_refe)):
func_name = ['asin', 'acos', 'atan', 'atan2[1]', 'atan2[2]'][i % 5]
assert isinstance(test, cp.variable)
assert isinstance(test, cp.value)
val = tg.read_value(test)
print(f"+ Result of {func_name}: {val}; reference: {ref}")
#assert val == pytest.approx(ref, abs=1e-5), f"Result of {func_name} for input {test_vals[i // 5]} does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
@ -113,7 +113,7 @@ def test_arcus_trig_precision():
def test_sqrt_precision():
test_vals = [0.0, 0.0001, 0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.28318530718, 100.0, 1000.0, 100000.0]
ret_test = [r for v in test_vals for r in (cp.sqrt(variable(v)),)]
ret_test = [r for v in test_vals for r in (cp.sqrt(value(v)),)]
ret_refe = [r for v in test_vals for r in (cp.sqrt(v),)]
tg = Target()
@ -122,7 +122,7 @@ def test_sqrt_precision():
for i, (test, ref) in enumerate(zip(ret_test, ret_refe)):
func_name = 'sqrt'
assert isinstance(test, cp.variable)
assert isinstance(test, cp.value)
val = tg.read_value(test)
print(f"+ Result of {func_name}: {val}; reference: {ref}")
assert val == pytest.approx(ref, rel=1e-5), f"Result of {func_name} for input {test_vals[i]} does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
@ -135,8 +135,8 @@ def test_log_exp_precision():
test_vals = [0.1, 0.5, 0.9, 0.999, 1.0, 2.5,
-0.1, -0.5, -0.9, -0.999, -1.0, 2.5]
ret_test = [r for v in test_vals for r in (cp.log(variable(abs(v))),
cp.exp(variable(v)))]
ret_test = [r for v in test_vals for r in (cp.log(value(abs(v))),
cp.exp(value(v)))]
ret_refe = [r for v in test_vals for r in (ma.log(abs(v)),
ma.exp(v))]
@ -146,7 +146,7 @@ def test_log_exp_precision():
for i, (test, ref) in enumerate(zip(ret_test, ret_refe)):
func_name = ['log', 'exp'][i % 2]
assert isinstance(test, cp.variable)
assert isinstance(test, cp.value)
val = tg.read_value(test)
print(f"+ Result of {func_name}: {val}; reference: {ref}")
assert val == pytest.approx(ref, abs=1e-5), f"Result of {func_name} for input {test_vals[i // 2]} does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]

View File

@ -13,11 +13,11 @@ def test_matrix_init():
def test_matrix_with_variables():
"""Test matrix initialization with variables"""
m1 = cp.matrix([[cp.variable(1), 2], [3, cp.variable(4)]])
m1 = cp.matrix([[cp.value(1), 2], [3, cp.value(4)]])
assert m1.rows == 2
assert m1.cols == 2
assert isinstance(m1[0][0], cp.variable)
assert isinstance(m1[1][1], cp.variable)
assert isinstance(m1[0][0], cp.value)
assert isinstance(m1[1][1], cp.value)
def test_matrix_addition():
@ -201,12 +201,12 @@ def test_matrix_map():
def test_matrix_homogenize():
"""Test homogenizing matrix (converting to all variables)"""
m = cp.matrix([[1, cp.variable(2)], [3, 4]])
m = cp.matrix([[1, cp.value(2)], [3, 4]])
m_homo = m.homogenize()
for row in m_homo:
for elem in row:
assert isinstance(elem, cp.variable)
assert isinstance(elem, cp.value)
def test_identity_matrix():
@ -254,8 +254,8 @@ def test_diagonal_matrix():
def test_matrix_with_variables_compiled():
"""Test matrix operations with variables in compilation"""
m = cp.matrix([[cp.variable(1), 2], [3, cp.variable(4)]])
v = cp.vector([cp.variable(5), 6])
m = cp.matrix([[cp.value(1), 2], [3, cp.value(4)]])
v = cp.vector([cp.value(5), 6])
result = m @ v
# result[0] = 1*5 + 2*6 = 17

View File

@ -1,4 +1,4 @@
from copapy import variable, Target, NumLike, iif
from copapy import value, Target, NumLike, iif
import pytest
import copapy
@ -42,11 +42,11 @@ def iiftests(c1: NumLike) -> list[NumLike]:
def test_compile():
c_i = variable(9)
c_f = variable(1.111)
c_b = variable(True)
c_i = value(9)
c_f = value(1.111)
c_b = value(True)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [variable(9) % 2] + iiftests(c_i) + iiftests(c_f)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f)
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111)
tg = Target()
@ -57,7 +57,7 @@ def test_compile():
print('* finished')
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, copapy.variable)
assert isinstance(test, copapy.value)
val = tg.read_value(test)
print('+', val, ref, test.dtype)
for t in (int, float, bool):

View File

@ -1,4 +1,4 @@
from copapy import NumLike, iif, variable
from copapy import NumLike, iif, value
from copapy.backend import Write, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
@ -84,11 +84,11 @@ def iiftests(c1: NumLike) -> list[NumLike]:
@pytest.mark.runner
def test_compile():
c_i = variable(9)
c_f = variable(1.111)
c_b = variable(True)
c_i = value(9)
c_f = value(1.111)
c_b = value(True)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [variable(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
out = [Write(r) for r in ret_test]
@ -145,7 +145,7 @@ def test_compile():
result_data = parse_results(result)
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, variable)
assert isinstance(test, value)
address = variables[test][0]
data = result_data[address]
if test.dtype == 'int':

View File

@ -1,4 +1,4 @@
from copapy import NumLike, iif, variable
from copapy import NumLike, iif, value
from copapy.backend import Write, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
@ -86,11 +86,11 @@ def iiftests(c1: NumLike) -> list[NumLike]:
@pytest.mark.runner
def test_compile():
c_i = variable(9)
c_f = variable(1.111)
c_b = variable(True)
c_i = value(9)
c_f = value(1.111)
c_b = value(True)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [variable(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
#ret_test = (c_i * 100 // 5, c_f * 10 // 5)
@ -147,7 +147,7 @@ def test_compile():
result_data = parse_results(result)
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, variable)
assert isinstance(test, value)
address = variables[test][0]
data = result_data[address]
if test.dtype == 'int':

View File

@ -1,4 +1,4 @@
from copapy import NumLike, iif, variable
from copapy import NumLike, iif, value
from copapy.backend import Write, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
@ -77,7 +77,7 @@ def test_compile():
#t4 = ((t3 * t1) * 2).sum()
#t5 = ((t3 * t1) * 2).magnitude()
c_i = variable(9)
c_i = value(9)
#c_f = variable(1.111)
#c_b = variable(True)
@ -87,7 +87,7 @@ def test_compile():
#ret_test = [cp.sin(c_i), cp.asin(variable(0.0))]
#ret_ref = [cp.sin(9), cp.asin(0.0)]
ret_test: list[variable[float]] = []
ret_test: list[value[float]] = []
ret_ref: list[float] = []
#sval = variable(8.0)
#tval = 8.0
@ -155,7 +155,7 @@ def test_compile():
result_data = parse_results(result)
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, variable)
assert isinstance(test, value)
address = variables[test][0]
data = result_data[address]
if test.dtype == 'int':

View File

@ -1,11 +1,11 @@
from copapy import variable, Target, iif
from copapy import value, Target, iif
import pytest
import copapy
def test_compile():
c_i = variable(9)
c_f = variable(2.5)
c_i = value(9)
c_f = value(2.5)
# c_b = variable(True)
ret_test = (iif(c_f > 5, c_f, -1), iif(c_i > 5, c_f, 8.8), iif(c_i > 2, c_i, 1))
@ -19,7 +19,7 @@ def test_compile():
print('* finished')
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, copapy.variable)
assert isinstance(test, copapy.value)
val = tg.read_value(test)
print('+', val, ref, type(val), test.dtype)
#for t in (int, float, bool):

View File

@ -3,8 +3,8 @@ import pytest
def test_readme_example():
# Define variables
a = cp.variable(0.25)
b = cp.variable(0.87)
a = cp.value(0.25)
b = cp.value(0.87)
# Define computations
c = a + b * 2.0

View File

@ -10,7 +10,7 @@ target = cp.vector([0.7, 0.7])
alpha = 0.1
def forward_kinematics(theta1: cp.variable[float] | float, theta2: cp.variable[float] | float) -> tuple[cp.vector[float], cp.vector[float]]:
def forward_kinematics(theta1: cp.value[float] | float, theta2: cp.value[float] | float) -> tuple[cp.vector[float], cp.vector[float]]:
"""Return positions of joint and end-effector."""
joint = cp.vector([l1 * cp.cos(theta1), l1 * cp.sin(theta1)])
end_effector = joint + cp.vector([l2 * cp.cos(theta1 + theta2),
@ -20,7 +20,7 @@ def forward_kinematics(theta1: cp.variable[float] | float, theta2: cp.variable[f
def test_two_arms():
target_vec = cp.vector(target)
theta = cp.vector([cp.variable(0.0), cp.variable(0.0)])
theta = cp.vector([cp.value(0.0), cp.value(0.0)])
joint = cp.vector([0.0, 0.0])
effector = cp.vector([0.0, 0.0])

View File

@ -5,38 +5,38 @@ from copapy import filters
def test_vectors_init():
tt1 = cp.vector(range(3)) + cp.vector([1.1, 2.2, 3.3])
tt2 = cp.vector([1.1, 2, cp.variable(5)]) + cp.vector(range(3))
tt2 = cp.vector([1.1, 2, cp.value(5)]) + cp.vector(range(3))
tt3 = (cp.vector(range(3)) + 5.6)
tt4 = cp.vector([1.1, 2, 3]) + cp.vector(cp.variable(v) for v in range(3))
tt4 = cp.vector([1.1, 2, 3]) + cp.vector(cp.value(v) for v in range(3))
tt5 = cp.vector([1, 2, 3]).dot(tt4)
print(tt1, tt2, tt3, tt4, tt5)
def test_compiled_vectors():
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.variable(v) for v in range(3))
t1 = cp.vector([10, 11, 12]) + cp.vector(cp.value(v) for v in range(3))
t2 = t1.sum()
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(3))
t3 = cp.vector(cp.value(1 / (v + 1)) for v in range(3))
t4 = ((t3 * t1) * 2).sum()
t5 = ((t3 * t1) * 2).magnitude()
t6 = cp.angle_between(cp.vector([cp.variable(5.0), 0.0, 0.0]), cp.vector([5.0, 5.0, 0.0]))
t6 = cp.angle_between(cp.vector([cp.value(5.0), 0.0, 0.0]), cp.vector([5.0, 5.0, 0.0]))
tg = cp.Target()
tg.compile(t2, t4, t5, t6)
tg.run()
assert isinstance(t2, cp.variable)
assert isinstance(t2, cp.value)
assert tg.read_value(t2) == 10 + 11 + 12 + 0 + 1 + 2
assert isinstance(t4, cp.variable)
assert isinstance(t4, cp.value)
assert tg.read_value(t4) == pytest.approx(((10/1*2) + (12/2*2) + (14/3*2)), 0.001) # pyright: ignore[reportUnknownMemberType]
assert isinstance(t5, cp.variable)
assert isinstance(t5, cp.value)
assert tg.read_value(t5) == pytest.approx(((10/1*2)**2 + (12/2*2)**2 + (14/3*2)**2) ** 0.5, 0.001) # pyright: ignore[reportUnknownMemberType]
assert isinstance(t6, cp.variable)
assert isinstance(t6, cp.value)
assert tg.read_value(t6) == pytest.approx(math.pi / 4, 0.001), tg.read_value(t6) # pyright: ignore[reportUnknownMemberType]
@ -97,7 +97,7 @@ def test_non_compiled_vector_operations():
def test_sort_vector():
vlist = [50, 21, 20, 10, 22, 1, 80, 70, 90]
t1 = cp.vector(cp.variable(v) for v in vlist)
t1 = cp.vector(cp.value(v) for v in vlist)
#t1 = cp.vector(v for v in vlist)
t2 = filters.median(t1)

View File

@ -1,4 +1,4 @@
from copapy import variable
from copapy import value
from copapy.backend import Write, compile_to_dag, stencil_db_from_package
from copapy._binwrite import Command
import copapy as cp
@ -6,7 +6,7 @@ import copapy as cp
def compile_to_x86_64() -> None:
"""Test compilation of a simple program for x86_64."""
c1 = variable(9.0)
c1 = value(9.0)
#ret = [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4]
ret = [c1 // 3.3 + 5]
@ -29,14 +29,14 @@ def compile_to_x86_64() -> None:
def compile_to_x86() -> None:
"""Test compilation of a simple program for x86 32 bit."""
c1 = variable(9.0)
c1 = value(9.0)
#ret = [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4]
ret = [c1 // 3.3 + 5]
#ret = [cp.sqrt(c1)]
#c2 = cp._math.get_42()
#ret = [c2]
ret = [cp.sin(variable(2.5))]
ret = [cp.sin(value(2.5))]
out = [Write(r) for r in ret]
@ -53,7 +53,7 @@ def compile_to_x86() -> None:
def compile_to_aarch64() -> None:
"""Test compilation of a simple program for arm64."""
c1 = variable(9.0)
c1 = value(9.0)
#ret = [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4]
#ret = [cp.sin(c1), cp.sqrt(c1) + 5]