mirror of https://github.com/Nonannet/copapy.git
commit
eb82afface
24
.flake8
24
.flake8
|
|
@ -1,24 +0,0 @@
|
|||
[flake8]
|
||||
# Specify the maximum allowed line length
|
||||
max-line-length = 88
|
||||
|
||||
# Ignore specific rules
|
||||
# For example, E501: Line too long, W503: Line break before binary operator
|
||||
ignore = E501, W503, W504, E226, E265
|
||||
|
||||
# Exclude specific files or directories
|
||||
exclude =
|
||||
.git,
|
||||
__pycache__,
|
||||
build,
|
||||
dist,
|
||||
.conda,
|
||||
.venv
|
||||
|
||||
# Enable specific plugins or options
|
||||
# Example: Enabling flake8-docstrings
|
||||
select = C,E,F,W,D
|
||||
|
||||
# Specify custom error codes to ignore or enable
|
||||
per-file-ignores =
|
||||
tests/*: D, E712
|
||||
|
|
@ -14,6 +14,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build & test aux functions
|
||||
run: bash tools/test_stencil_aux.sh
|
||||
|
||||
- name: Build object files
|
||||
run: bash tools/crosscompile.sh
|
||||
|
||||
|
|
@ -24,7 +27,7 @@ jobs:
|
|||
|
||||
build-ubuntu:
|
||||
needs: [build_stencils]
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -53,7 +56,10 @@ jobs:
|
|||
gcc -DENABLE_BASIC_LOGGING -O3 -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wstrict-overflow -Werror -g src/coparun/runmem.c src/coparun/coparun.c src/coparun/mem_man.c -o bin/coparun
|
||||
|
||||
- name: Install ARM binutils
|
||||
run: sudo apt-get update && sudo apt-get install -y binutils-aarch64-linux-gnu
|
||||
run: |
|
||||
which aarch64-linux-gnu-objdump || echo "Not found: aarch64-linux-gnu-objdump"
|
||||
sudo apt-get install --no-install-recommends -y binutils-aarch64-linux-gnu
|
||||
|
||||
|
||||
- name: Generate debug asm files
|
||||
if: strategy.job-index == 0
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ copapy = ["obj/*.o", "py.typed"]
|
|||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"flake8",
|
||||
"ruff",
|
||||
"mypy",
|
||||
"pytest"
|
||||
]
|
||||
|
|
@ -51,3 +51,22 @@ minversion = "6.0"
|
|||
addopts = "-ra -q"
|
||||
testpaths = ["tests"]
|
||||
pythonpath = ["src"]
|
||||
|
||||
[tool.ruff]
|
||||
lint.ignore = ["E501", "E226", "E265"]
|
||||
|
||||
# Equivalent to Flake8's "exclude"
|
||||
exclude = [
|
||||
".git",
|
||||
"__pycache__",
|
||||
"build",
|
||||
"dist",
|
||||
".conda",
|
||||
".venv",
|
||||
]
|
||||
|
||||
# "D" for dockstrings
|
||||
lint.select = ["C", "E", "F", "W"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = ["D", "E712"]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
from ._target import Target
|
||||
from ._basic_types import NumLike, variable, \
|
||||
CPNumber, cpvector, generic_sdb, iif
|
||||
generic_sdb, iif
|
||||
from ._vectors import vector
|
||||
from ._math import sqrt, abs
|
||||
|
||||
__all__ = [
|
||||
"Target",
|
||||
"NumLike",
|
||||
"variable",
|
||||
"CPNumber",
|
||||
"cpvector",
|
||||
"generic_sdb",
|
||||
"iif",
|
||||
"vector",
|
||||
"sqrt",
|
||||
"abs",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,15 +4,12 @@ from ._stencils import stencil_database
|
|||
import platform
|
||||
|
||||
NumLike: TypeAlias = 'variable[int] | variable[float] | variable[bool] | int | float | bool'
|
||||
NumLikeAndNet: TypeAlias = 'variable[int] | variable[float] | variable[bool] | int | float | bool | Net'
|
||||
NetAndNum: TypeAlias = 'Net | int | float'
|
||||
|
||||
unifloat: TypeAlias = 'variable[float] | float'
|
||||
uniint: TypeAlias = 'variable[int] | int'
|
||||
unibool: TypeAlias = 'variable[bool] | bool'
|
||||
|
||||
TNumber = TypeVar("TNumber", bound='CPNumber')
|
||||
T = TypeVar("T")
|
||||
TCPNum = TypeVar("TCPNum", bound='variable[Any]')
|
||||
TNum = TypeVar("TNum", int, bool, float)
|
||||
|
||||
|
||||
def get_var_name(var: Any, scope: dict[str, Any] = globals()) -> list[str]:
|
||||
|
|
@ -60,231 +57,201 @@ class Net:
|
|||
return id(self)
|
||||
|
||||
|
||||
class CPNumber(Net):
|
||||
def __init__(self, dtype: str, source: Node):
|
||||
self.dtype = dtype
|
||||
self.source = source
|
||||
|
||||
@overload
|
||||
def __mul__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __mul__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __mul__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('mul', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __rmul__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __rmul__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __rmul__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('mul', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __add__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __add__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __add__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('add', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __radd__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __radd__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __radd__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('add', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __sub__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __sub__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __sub__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('sub', [self, other])
|
||||
|
||||
@overload
|
||||
def __rsub__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __rsub__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __rsub__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('sub', [other, self])
|
||||
|
||||
def __truediv__(self, other: NumLike) -> 'variable[float]':
|
||||
return _add_op('div', [self, other])
|
||||
|
||||
def __rtruediv__(self, other: NumLike) -> 'variable[float]':
|
||||
return _add_op('div', [other, self])
|
||||
|
||||
@overload
|
||||
def __floordiv__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __floordiv__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __floordiv__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('floordiv', [self, other])
|
||||
|
||||
@overload
|
||||
def __rfloordiv__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __rfloordiv__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __rfloordiv__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('floordiv', [other, self])
|
||||
|
||||
def __neg__(self: TNumber) -> TNumber:
|
||||
assert isinstance(T, variable)
|
||||
return cast(TNumber, _add_op('sub', [variable(0), self]))
|
||||
|
||||
def __gt__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = _add_op('gt', [self, other])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __lt__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = _add_op('gt', [other, self])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __eq__(self, other: NumLike) -> 'variable[bool]': # type: ignore
|
||||
ret = _add_op('eq', [self, other], True)
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __ne__(self, other: NumLike) -> 'variable[bool]': # type: ignore
|
||||
ret = _add_op('ne', [self, other], True)
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
@overload
|
||||
def __mod__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __mod__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __mod__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('mod', [self, other])
|
||||
|
||||
@overload
|
||||
def __rmod__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __rmod__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __rmod__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('mod', [other, self])
|
||||
|
||||
@overload
|
||||
def __pow__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __pow__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __pow__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('pow', [other, self])
|
||||
|
||||
@overload
|
||||
def __rpow__(self: TNumber, other: uniint) -> TNumber:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __rpow__(self, other: unifloat) -> 'variable[float]':
|
||||
...
|
||||
|
||||
def __rpow__(self, other: NumLike) -> 'CPNumber':
|
||||
return _add_op('rpow', [self, other])
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return super().__hash__()
|
||||
|
||||
|
||||
class variable(Generic[T], CPNumber):
|
||||
def __init__(self, source: T | Node, dtype: str | None = None):
|
||||
class variable(Generic[TNum], Net):
|
||||
def __init__(self, source: TNum | Node, dtype: str | None = None):
|
||||
if isinstance(source, Node):
|
||||
self.source = source
|
||||
assert dtype, 'For source type Node a dtype argument is required.'
|
||||
self.dtype = dtype
|
||||
elif isinstance(source, bool):
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'bool'
|
||||
elif isinstance(source, int):
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'int'
|
||||
elif isinstance(source, float):
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'float'
|
||||
elif isinstance(source, bool):
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'bool'
|
||||
else:
|
||||
raise ValueError(f'Non supported data type: {type(source).__name__}')
|
||||
self.source = CPConstant(source)
|
||||
self.dtype = 'int'
|
||||
|
||||
@overload
|
||||
def __add__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __add__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __add__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __add__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __add__(self, other: NumLike) -> Any:
|
||||
return add_op('add', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __radd__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __radd__(self, other: float) -> 'variable[float]': ...
|
||||
def __radd__(self, other: NumLike) -> Any:
|
||||
return add_op('add', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __sub__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __sub__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __sub__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __sub__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __sub__(self, other: NumLike) -> Any:
|
||||
return add_op('sub', [self, other])
|
||||
|
||||
@overload
|
||||
def __rsub__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __rsub__(self, other: float) -> 'variable[float]': ...
|
||||
def __rsub__(self, other: NumLike) -> Any:
|
||||
return add_op('sub', [other, self])
|
||||
|
||||
@overload
|
||||
def __mul__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __mul__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __mul__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __mul__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __mul__(self, other: NumLike) -> Any:
|
||||
return add_op('mul', [self, other], True)
|
||||
|
||||
@overload
|
||||
def __rmul__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __rmul__(self, other: float) -> 'variable[float]': ...
|
||||
def __rmul__(self, other: NumLike) -> Any:
|
||||
return add_op('mul', [self, other], True)
|
||||
|
||||
def __truediv__(self, other: NumLike) -> 'variable[float]':
|
||||
return add_op('div', [self, other])
|
||||
|
||||
def __rtruediv__(self, other: NumLike) -> 'variable[float]':
|
||||
return add_op('div', [other, self])
|
||||
|
||||
@overload
|
||||
def __floordiv__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __floordiv__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __floordiv__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __floordiv__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __floordiv__(self, other: NumLike) -> Any:
|
||||
return add_op('floordiv', [self, other])
|
||||
|
||||
@overload
|
||||
def __rfloordiv__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __rfloordiv__(self, other: float) -> 'variable[float]': ...
|
||||
def __rfloordiv__(self, other: NumLike) -> Any:
|
||||
return add_op('floordiv', [other, self])
|
||||
|
||||
def __neg__(self: TCPNum) -> TCPNum:
|
||||
return cast(TCPNum, add_op('sub', [variable(0), self]))
|
||||
|
||||
def __gt__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = add_op('gt', [self, other])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __lt__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = add_op('gt', [other, self])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __ge__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = add_op('ge', [self, other])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __le__(self, other: NumLike) -> 'variable[bool]':
|
||||
ret = add_op('ge', [other, self])
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __eq__(self, other: NumLike) -> 'variable[bool]': # type: ignore
|
||||
ret = add_op('eq', [self, other], True)
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
def __ne__(self, other: NumLike) -> 'variable[bool]': # type: ignore
|
||||
ret = add_op('ne', [self, other], True)
|
||||
return variable(ret.source, dtype='bool')
|
||||
|
||||
@overload
|
||||
def __mod__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __mod__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __mod__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __mod__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __mod__(self, other: NumLike) -> Any:
|
||||
return add_op('mod', [self, other])
|
||||
|
||||
@overload
|
||||
def __rmod__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __rmod__(self, other: float) -> 'variable[float]': ...
|
||||
def __rmod__(self, other: NumLike) -> Any:
|
||||
return add_op('mod', [other, self])
|
||||
|
||||
@overload
|
||||
def __pow__(self, other: TCPNum) -> TCPNum: ...
|
||||
@overload
|
||||
def __pow__(self: TCPNum, other: uniint) -> TCPNum: ...
|
||||
@overload
|
||||
def __pow__(self, other: unifloat) -> 'variable[float]': ...
|
||||
@overload
|
||||
def __pow__(self, other: NumLike) -> 'variable[float] | variable[int]': ...
|
||||
def __pow__(self, other: NumLike) -> Any:
|
||||
if not isinstance(other, variable):
|
||||
if other == 2:
|
||||
return self * self
|
||||
if other == -1:
|
||||
return 1 / self
|
||||
return add_op('pow', [self, other])
|
||||
|
||||
@overload
|
||||
def __rpow__(self: TCPNum, other: int) -> TCPNum: ...
|
||||
@overload
|
||||
def __rpow__(self, other: float) -> 'variable[float]': ...
|
||||
def __rpow__(self, other: NumLike) -> Any:
|
||||
return add_op('rpow', [other, self])
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return super().__hash__()
|
||||
|
||||
# Bitwise and shift operations for cp[int]
|
||||
def __lshift__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('lshift', [self, other])
|
||||
return add_op('lshift', [self, other])
|
||||
|
||||
def __rlshift__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('lshift', [other, self])
|
||||
return add_op('lshift', [other, self])
|
||||
|
||||
def __rshift__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('rshift', [self, other])
|
||||
return add_op('rshift', [self, other])
|
||||
|
||||
def __rrshift__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('rshift', [other, self])
|
||||
return add_op('rshift', [other, self])
|
||||
|
||||
def __and__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('bwand', [self, other], True)
|
||||
return add_op('bwand', [self, other], True)
|
||||
|
||||
def __rand__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('rwand', [other, self], True)
|
||||
return add_op('rwand', [other, self], True)
|
||||
|
||||
def __or__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('bwor', [self, other], True)
|
||||
return add_op('bwor', [self, other], True)
|
||||
|
||||
def __ror__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('bwor', [other, self], True)
|
||||
return add_op('bwor', [other, self], True)
|
||||
|
||||
def __xor__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('bwxor', [self, other], True)
|
||||
return add_op('bwxor', [self, other], True)
|
||||
|
||||
def __rxor__(self, other: uniint) -> 'variable[int]':
|
||||
return _add_op('bwxor', [other, self], True)
|
||||
|
||||
|
||||
class cpvector:
|
||||
def __init__(self, *value: NumLike):
|
||||
self.value = value
|
||||
|
||||
def __add__(self, other: 'cpvector') -> 'cpvector':
|
||||
assert len(self.value) == len(other.value)
|
||||
tup = (a + b for a, b in zip(self.value, other.value))
|
||||
return cpvector(*(v for v in tup if isinstance(v, CPNumber)))
|
||||
return add_op('bwxor', [other, self], True)
|
||||
|
||||
|
||||
class CPConstant(Node):
|
||||
|
|
@ -295,7 +262,7 @@ class CPConstant(Node):
|
|||
|
||||
|
||||
class Write(Node):
|
||||
def __init__(self, input: NetAndNum):
|
||||
def __init__(self, input: Net | int | float):
|
||||
if isinstance(input, Net):
|
||||
net = input
|
||||
else:
|
||||
|
|
@ -319,35 +286,24 @@ def net_from_value(value: Any) -> Net:
|
|||
|
||||
|
||||
@overload
|
||||
def iif(expression: CPNumber, true_result: unibool, false_result: unibool) -> variable[bool]: # pyright: ignore[reportOverlappingOverload]
|
||||
...
|
||||
|
||||
|
||||
def iif(expression: variable[Any], true_result: unibool, false_result: unibool) -> variable[bool]: ... # pyright: ignore[reportOverlappingOverload]
|
||||
@overload
|
||||
def iif(expression: CPNumber, true_result: uniint, false_result: uniint) -> variable[int]:
|
||||
...
|
||||
|
||||
|
||||
def iif(expression: variable[Any], true_result: uniint, false_result: uniint) -> variable[int]: ...
|
||||
@overload
|
||||
def iif(expression: CPNumber, true_result: unifloat, false_result: unifloat) -> variable[float]:
|
||||
...
|
||||
|
||||
|
||||
def iif(expression: variable[Any], true_result: unifloat, false_result: unifloat) -> variable[float]: ...
|
||||
@overload
|
||||
def iif(expression: NumLike, true_result: T, false_result: T) -> T:
|
||||
...
|
||||
|
||||
|
||||
def iif(expression: float | int, true_result: TNum, false_result: TNum) -> TNum: ...
|
||||
@overload
|
||||
def iif(expression: float | int, true_result: TNum, false_result: variable[TNum]) -> variable[TNum]: ...
|
||||
@overload
|
||||
def iif(expression: float | int, true_result: variable[TNum], false_result: TNum | variable[TNum]) -> variable[TNum]: ...
|
||||
def iif(expression: Any, true_result: Any, false_result: Any) -> Any:
|
||||
allowed_type = (variable, int, float, bool)
|
||||
assert isinstance(true_result, allowed_type) and isinstance(false_result, allowed_type), "Result type not supported"
|
||||
if isinstance(expression, CPNumber):
|
||||
return (expression != 0) * true_result + (expression == 0) * false_result
|
||||
else:
|
||||
return true_result if expression else false_result
|
||||
|
||||
|
||||
def _add_op(op: str, args: list[CPNumber | int | float], commutative: bool = False) -> variable[Any]:
|
||||
def add_op(op: str, args: list[variable[Any] | int | float], commutative: bool = False) -> variable[Any]:
|
||||
arg_nets = [a if isinstance(a, Net) else net_from_value(a) for a in args]
|
||||
|
||||
if commutative:
|
||||
|
|
@ -360,10 +316,10 @@ def _add_op(op: str, args: list[CPNumber | int | float], commutative: bool = Fal
|
|||
|
||||
result_type = generic_sdb.stencil_definitions[typed_op].split('_')[0]
|
||||
|
||||
if result_type == 'int':
|
||||
return variable[int](Op(typed_op, arg_nets), result_type)
|
||||
else:
|
||||
if result_type == 'float':
|
||||
return variable[float](Op(typed_op, arg_nets), result_type)
|
||||
else:
|
||||
return variable[int](Op(typed_op, arg_nets), result_type)
|
||||
|
||||
|
||||
def _get_data_and_dtype(value: Any) -> tuple[str, float | int]:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ COMMAND_SIZE = 4
|
|||
|
||||
class data_writer():
|
||||
def __init__(self, byteorder: ByteOrder):
|
||||
self._data: list[tuple[str, bytes, int]] = list()
|
||||
self._data: list[tuple[str, bytes, int]] = []
|
||||
self.byteorder: ByteOrder = byteorder
|
||||
|
||||
def write_int(self, value: int, num_bytes: int = 4, signed: bool = False) -> None:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Generator, Iterable, Any
|
||||
from . import _binwrite as binw
|
||||
from ._stencils import stencil_database, patch_entry
|
||||
from ._stencils import stencil_database
|
||||
from collections import defaultdict, deque
|
||||
from ._basic_types import Net, Node, Write, CPConstant, Op, transl_type
|
||||
|
||||
|
|
@ -166,8 +166,8 @@ def get_data_layout(variable_list: Iterable[Net], sdb: stencil_database, offset:
|
|||
return object_list, offset
|
||||
|
||||
|
||||
def get_target_sym_lookup(function_names: Iterable[str], sdb: stencil_database) -> dict[str, patch_entry]:
|
||||
return {patch.target_symbol_name: patch for name in set(function_names) for patch in sdb.get_patch_positions(name)}
|
||||
#def get_target_sym_lookup(function_names: Iterable[str], sdb: stencil_database) -> dict[str, patch_entry]:
|
||||
# return {patch.target_symbol_name: patch for name in set(function_names) for patch in sdb.get_patch_positions(name)}
|
||||
|
||||
|
||||
def get_section_layout(section_indexes: Iterable[int], sdb: stencil_database, offset: int = 0) -> tuple[list[tuple[int, int, int]], int]:
|
||||
|
|
@ -192,8 +192,8 @@ def get_aux_function_mem_layout(function_names: Iterable[str], sdb: stencil_data
|
|||
return function_list, offset
|
||||
|
||||
|
||||
def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
||||
variables: dict[Net, tuple[int, int, str]] = dict()
|
||||
def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
||||
variables: dict[Net, tuple[int, int, str]] = {}
|
||||
data_list: list[bytes] = []
|
||||
patch_list: list[tuple[int, int, int, binw.Command]] = []
|
||||
|
||||
|
|
@ -221,18 +221,18 @@ def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database
|
|||
dw.write_int(variables_data_lengths)
|
||||
|
||||
# Heap constants
|
||||
for section_id, out_offs, lengths in section_mem_layout:
|
||||
for section_id, start, lengths in section_mem_layout:
|
||||
dw.write_com(binw.Command.COPY_DATA)
|
||||
dw.write_int(out_offs)
|
||||
dw.write_int(start)
|
||||
dw.write_int(lengths)
|
||||
dw.write_bytes(sdb.get_section_data(section_id))
|
||||
|
||||
# Heap variables
|
||||
for net, out_offs, lengths in variable_mem_layout:
|
||||
variables[net] = (out_offs, lengths, net.dtype)
|
||||
for net, start, lengths in variable_mem_layout:
|
||||
variables[net] = (start, lengths, net.dtype)
|
||||
if isinstance(net.source, CPConstant):
|
||||
dw.write_com(binw.Command.COPY_DATA)
|
||||
dw.write_int(out_offs)
|
||||
dw.write_int(start)
|
||||
dw.write_int(lengths)
|
||||
dw.write_value(net.source.value, lengths)
|
||||
# print(f'+ {net.dtype} {net.source.value}')
|
||||
|
|
@ -244,12 +244,11 @@ def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database
|
|||
# Prepare program code and relocations
|
||||
object_addr_lookup = {net: offs for net, offs, _ in variable_mem_layout}
|
||||
section_addr_lookup = {id: offs for id, offs, _ in section_mem_layout}
|
||||
offset = aux_function_lengths # offset in generated code chunk
|
||||
|
||||
# assemble stencils to main program
|
||||
# assemble stencils to main program and patch stencils
|
||||
data = sdb.get_function_code('entry_function_shell', 'start')
|
||||
data_list.append(data)
|
||||
offset += len(data)
|
||||
offset = aux_function_lengths + len(data)
|
||||
|
||||
for associated_net, node in extended_output_ops:
|
||||
assert node.name in sdb.stencil_definitions, f"- Warning: {node.name} stencil not found"
|
||||
|
|
@ -257,24 +256,30 @@ def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database
|
|||
data_list.append(data)
|
||||
#print(f"* {node.name} ({offset}) " + ' '.join(f'{d:02X}' for d in data))
|
||||
|
||||
for patch in sdb.get_patch_positions(node.name):
|
||||
for patch in sdb.get_patch_positions(node.name, stencil=True):
|
||||
if patch.target_symbol_info in {'STT_OBJECT', 'STT_NOTYPE'}:
|
||||
if patch.target_symbol_name.startswith('dummy_'):
|
||||
# Patch for write and read addresses to/from heap variables
|
||||
assert associated_net, f"Relocation found but no net defined for operation {node.name}"
|
||||
#print(f"Patch for write and read addresses to/from heap variables: {node.name} {patch.target_symbol_info} {patch.target_symbol_name}")
|
||||
addr = object_addr_lookup[associated_net]
|
||||
patch_value = addr + patch.addend - (offset + patch.addr)
|
||||
patch_value = addr + patch.addend - (offset + patch.patch_address)
|
||||
elif patch.target_symbol_name.startswith('result_'):
|
||||
raise Exception(f"Stencil {node.name} seams to branch to multiple result_* calls.")
|
||||
else:
|
||||
# Patch constants addresses on heap
|
||||
addr = section_addr_lookup[patch.target_symbol_section_index]
|
||||
patch_value = addr + patch.addend - (offset + patch.addr)
|
||||
patch_list.append((patch.type.value, offset + patch.addr, patch_value, binw.Command.PATCH_OBJECT))
|
||||
section_addr = section_addr_lookup[patch.target_symbol_section_index]
|
||||
obj_addr = section_addr + patch.target_symbol_address
|
||||
patch_value = obj_addr + patch.addend - (offset + patch.patch_address)
|
||||
#print('* constants stancils', patch.type, patch.patch_address, binw.Command.PATCH_OBJECT, node.name)
|
||||
patch_list.append((patch.type.value, offset + patch.patch_address, patch_value, binw.Command.PATCH_OBJECT))
|
||||
#print(patch.type, patch.addr, binw.Command.PATCH_OBJECT, node.name)
|
||||
|
||||
elif patch.target_symbol_info == 'STT_FUNC':
|
||||
addr = aux_func_addr_lookup[patch.target_symbol_name]
|
||||
patch_value = addr + patch.addend - (offset + patch.addr)
|
||||
patch_list.append((patch.type.value, offset + patch.addr, patch_value, binw.Command.PATCH_FUNC))
|
||||
patch_value = addr + patch.addend - (offset + patch.patch_address)
|
||||
patch_list.append((patch.type.value, offset + patch.patch_address, patch_value, binw.Command.PATCH_FUNC))
|
||||
#print(patch.type, patch.addr, binw.Command.PATCH_FUNC, node.name, '->', patch.target_symbol_name)
|
||||
else:
|
||||
raise ValueError(f"Unsupported: {node.name} {patch.target_symbol_info} {patch.target_symbol_name}")
|
||||
|
||||
|
|
@ -288,13 +293,34 @@ def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database
|
|||
dw.write_com(binw.Command.ALLOCATE_CODE)
|
||||
dw.write_int(offset)
|
||||
|
||||
# write aux functions
|
||||
for name, out_offs, lengths in aux_function_mem_layout:
|
||||
# write aux functions code
|
||||
for name, start, lengths in aux_function_mem_layout:
|
||||
dw.write_com(binw.Command.COPY_CODE)
|
||||
dw.write_int(out_offs)
|
||||
dw.write_int(start)
|
||||
dw.write_int(lengths)
|
||||
dw.write_bytes(sdb.get_function_code(name))
|
||||
|
||||
# Patch aux functions
|
||||
for name, start, lengths in aux_function_mem_layout:
|
||||
for patch in sdb.get_patch_positions(name):
|
||||
if patch.target_symbol_info in {'STT_OBJECT', 'STT_NOTYPE'}:
|
||||
# Patch constants/variable addresses on heap
|
||||
section_addr = section_addr_lookup[patch.target_symbol_section_index]
|
||||
obj_addr = section_addr + patch.target_symbol_address
|
||||
patch_value = obj_addr + patch.addend - (start + patch.patch_address)
|
||||
patch_list.append((patch.type.value, start + patch.patch_address, patch_value, binw.Command.PATCH_OBJECT))
|
||||
#print('* constants aux', patch.type, patch.patch_address, obj_addr, binw.Command.PATCH_OBJECT, name)
|
||||
|
||||
elif patch.target_symbol_info == 'STT_FUNC':
|
||||
aux_func_addr = aux_func_addr_lookup[patch.target_symbol_name]
|
||||
patch_value = aux_func_addr + patch.addend - (start + patch.patch_address)
|
||||
patch_list.append((patch.type.value, start + patch.patch_address, patch_value, binw.Command.PATCH_FUNC))
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported: {name} {patch.target_symbol_info} {patch.target_symbol_name}")
|
||||
|
||||
#assert False, aux_function_mem_layout
|
||||
|
||||
# write entry function code
|
||||
dw.write_com(binw.Command.COPY_CODE)
|
||||
dw.write_int(aux_function_lengths)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
from . import variable, NumLike
|
||||
from typing import TypeVar, Any, overload
|
||||
from ._basic_types import add_op
|
||||
|
||||
T = TypeVar("T", int, float, variable[int], variable[float])
|
||||
|
||||
|
||||
@overload
|
||||
def sqrt(x: float | int) -> float: ...
|
||||
@overload
|
||||
def sqrt(x: variable[Any]) -> variable[float]: ...
|
||||
def sqrt(x: NumLike) -> variable[float] | float:
|
||||
"""Square root function"""
|
||||
if isinstance(x, variable):
|
||||
return add_op('sqrt', [x, x]) # TODO: fix 2. dummy argument
|
||||
return float(x ** 0.5)
|
||||
|
||||
|
||||
@overload
|
||||
def sqrt2(x: float | int) -> float: ...
|
||||
@overload
|
||||
def sqrt2(x: variable[Any]) -> variable[float]: ...
|
||||
def sqrt2(x: NumLike) -> variable[float] | float:
|
||||
"""Square root function"""
|
||||
if isinstance(x, variable):
|
||||
return add_op('sqrt2', [x, x]) # TODO: fix 2. dummy argument
|
||||
return float(x ** 0.5)
|
||||
|
||||
|
||||
def get_42() -> variable[float]:
|
||||
"""Returns the variable representing the constant 42"""
|
||||
return add_op('get_42', [0.0, 0.0])
|
||||
|
||||
|
||||
def abs(x: T) -> T:
|
||||
"""Absolute value function"""
|
||||
ret = (x < 0) * -x + (x >= 0) * x
|
||||
return ret # pyright: ignore[reportReturnType]
|
||||
|
||||
|
|
@ -21,11 +21,12 @@ class patch_entry:
|
|||
type (RelocationType): relocation type"""
|
||||
|
||||
type: RelocationType
|
||||
addr: int
|
||||
patch_address: int
|
||||
addend: int
|
||||
target_symbol_name: str
|
||||
target_symbol_info: str
|
||||
target_symbol_section_index: int
|
||||
target_symbol_address: int
|
||||
|
||||
|
||||
def translate_relocation(relocation_addr: int, reloc_type: str, bits: int, r_addend: int) -> RelocationType:
|
||||
|
|
@ -119,7 +120,7 @@ class stencil_database():
|
|||
ret.add(sym.section.index)
|
||||
return list(ret)
|
||||
|
||||
def get_patch_positions(self, symbol_name: str) -> Generator[patch_entry, None, None]:
|
||||
def get_patch_positions(self, symbol_name: str, stencil: bool = False) -> Generator[patch_entry, None, None]:
|
||||
"""Return patch positions for a provided symbol (function or object)
|
||||
|
||||
Args:
|
||||
|
|
@ -129,7 +130,11 @@ class stencil_database():
|
|||
patch_entry: every relocation for the symbol
|
||||
"""
|
||||
symbol = self.elf.symbols[symbol_name]
|
||||
if stencil:
|
||||
start_index, end_index = get_stencil_position(symbol)
|
||||
else:
|
||||
start_index = 0
|
||||
end_index = symbol.fields['st_size']
|
||||
|
||||
for reloc in symbol.relocations:
|
||||
|
||||
|
|
@ -146,10 +151,11 @@ class stencil_database():
|
|||
reloc.fields['r_addend'],
|
||||
reloc.symbol.name,
|
||||
reloc.symbol.info,
|
||||
reloc.symbol.fields['st_shndx'])
|
||||
reloc.symbol.fields['st_shndx'],
|
||||
reloc.symbol.fields['st_value'])
|
||||
|
||||
# Exclude the call to the result_* function
|
||||
if patch.addr < end_index - start_index:
|
||||
if patch.patch_address < end_index - start_index:
|
||||
yield patch
|
||||
|
||||
def get_stencil_code(self, name: str) -> bytes:
|
||||
|
|
@ -185,7 +191,7 @@ class stencil_database():
|
|||
return self.elf.sections[id].data
|
||||
|
||||
def get_function_code(self, name: str, part: Literal['full', 'start', 'end'] = 'full') -> bytes:
|
||||
"""Returns machine code for a specified function name"""
|
||||
"""Returns machine code for a specified function name."""
|
||||
func = self.elf.symbols[name]
|
||||
assert func.info == 'STT_FUNC', f"{name} is not a function"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ 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 ._compiler import compile_to_instruction_list
|
||||
from ._compiler import compile_to_dag
|
||||
|
||||
|
||||
def add_read_command(dw: binw.data_writer, variables: dict[Net, tuple[int, int, str]], net: Net) -> None:
|
||||
|
|
@ -18,7 +18,7 @@ def add_read_command(dw: binw.data_writer, variables: dict[Net, tuple[int, int,
|
|||
class Target():
|
||||
def __init__(self, arch: str = 'native', optimization: str = 'O3') -> None:
|
||||
self.sdb = stencil_db_from_package(arch, optimization)
|
||||
self._variables: dict[Net, tuple[int, int, str]] = dict()
|
||||
self._variables: dict[Net, tuple[int, int, str]] = {}
|
||||
|
||||
def compile(self, *variables: int | float | variable[int] | variable[float] | variable[bool] | Iterable[int | float | variable[int] | variable[float] | variable[bool]]) -> None:
|
||||
nodes: list[Node] = []
|
||||
|
|
@ -30,7 +30,7 @@ class Target():
|
|||
else:
|
||||
nodes.append(Write(s))
|
||||
|
||||
dw, self._variables = compile_to_instruction_list(nodes, self.sdb)
|
||||
dw, self._variables = compile_to_dag(nodes, self.sdb)
|
||||
dw.write_com(binw.Command.END_COM)
|
||||
assert coparun(dw.get_data()) > 0
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
from . import variable
|
||||
from typing import Generic, TypeVar, Iterable, Any, overload, TypeAlias
|
||||
from ._math import sqrt
|
||||
|
||||
VecNumLike: TypeAlias = 'vector[int] | vector[float] | variable[int] | variable[float] | int | float'
|
||||
VecIntLike: TypeAlias = 'vector[int] | variable[int] | int'
|
||||
VecFloatLike: TypeAlias = 'vector[float] | variable[float] | float'
|
||||
T = TypeVar("T", int, float)
|
||||
|
||||
epsilon = 1e-20
|
||||
|
||||
|
||||
class vector(Generic[T]):
|
||||
"""Type-safe vector supporting numeric promotion between vector types."""
|
||||
def __init__(self, values: Iterable[T | variable[T]]):
|
||||
self.values: tuple[variable[T] | T, ...] = tuple(values)
|
||||
|
||||
# ---------- Basic dunder methods ----------
|
||||
def __repr__(self) -> str:
|
||||
return f"vector({self.values})"
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.values)
|
||||
|
||||
def __getitem__(self, index: int) -> variable[T] | T:
|
||||
return self.values[index]
|
||||
|
||||
@overload
|
||||
def __add__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
||||
@overload
|
||||
def __add__(self: 'vector[int]', other: VecIntLike) -> 'vector[int]': ...
|
||||
@overload
|
||||
def __add__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
@overload
|
||||
def __add__(self, other: VecNumLike) -> 'vector[int] | vector[float]': ...
|
||||
def __add__(self, other: VecNumLike) -> Any:
|
||||
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)
|
||||
|
||||
@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, other: Any) -> Any:
|
||||
return self + other
|
||||
|
||||
@overload
|
||||
def __sub__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
||||
@overload
|
||||
def __sub__(self: 'vector[int]', other: VecIntLike) -> 'vector[int]': ...
|
||||
@overload
|
||||
def __sub__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||
@overload
|
||||
def __sub__(self, other: VecNumLike) -> 'vector[int] | vector[float]': ...
|
||||
def __sub__(self, other: VecNumLike) -> Any:
|
||||
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)
|
||||
|
||||
@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, other: VecNumLike) -> Any:
|
||||
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)
|
||||
|
||||
@overload
|
||||
def __mul__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
||||
@overload
|
||||
def __mul__(self: 'vector[int]', other: VecIntLike) -> 'vector[int]': ...
|
||||
@overload
|
||||
def __mul__(self: 'vector[float]', other: 'vector[int] | float | int | variable[int]') -> 'vector[float]': ...
|
||||
@overload
|
||||
def __mul__(self, other: VecNumLike) -> 'vector[int] | vector[float]': ...
|
||||
def __mul__(self, other: VecNumLike) -> Any:
|
||||
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)
|
||||
|
||||
@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, other: VecNumLike) -> Any:
|
||||
return self * other
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
@overload
|
||||
def dot(self: 'vector[int]', other: 'vector[int]') -> int | variable[int]: ...
|
||||
@overload
|
||||
def dot(self, other: 'vector[float]') -> float | variable[float]: ...
|
||||
@overload
|
||||
def dot(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | variable[float]: ...
|
||||
@overload
|
||||
def dot(self, other: 'vector[int] | vector[float]') -> float | int | variable[float] | variable[int]: ...
|
||||
def dot(self, other: 'vector[int] | vector[float]') -> Any:
|
||||
assert len(self.values) == len(other.values)
|
||||
return 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]: ...
|
||||
@overload
|
||||
def __matmul__(self, other: 'vector[float]') -> float | variable[float]: ...
|
||||
@overload
|
||||
def __matmul__(self: 'vector[float]', other: 'vector[int] | vector[float]') -> float | variable[float]: ...
|
||||
@overload
|
||||
def __matmul__(self, other: 'vector[int] | vector[float]') -> float | int | variable[float] | variable[int]: ...
|
||||
def __matmul__(self, other: 'vector[int] | vector[float]') -> Any:
|
||||
return self.dot(other)
|
||||
|
||||
def cross(self: 'vector[float]', other: 'vector[float]') -> 'vector[float]':
|
||||
"""3D cross product"""
|
||||
assert len(self.values) == 3 and len(other.values) == 3
|
||||
a1, a2, a3 = self.values
|
||||
b1, b2, b3 = other.values
|
||||
return vector([
|
||||
a2 * b3 - a3 * b2,
|
||||
a3 * b1 - a1 * b3,
|
||||
a1 * b2 - a2 * b1
|
||||
])
|
||||
|
||||
@overload
|
||||
def sum(self: 'vector[int]') -> int | variable[int]: ...
|
||||
@overload
|
||||
def sum(self: 'vector[float]') -> float | variable[float]: ...
|
||||
def sum(self) -> Any:
|
||||
return sum(a for a in self.values if isinstance(a, variable)) +\
|
||||
sum(a for a in self.values if not isinstance(a, variable))
|
||||
|
||||
def magnitude(self) -> 'float | variable[float]':
|
||||
s = sum(a * a for a in self.values)
|
||||
return sqrt(s) if isinstance(s, variable) else sqrt(s)
|
||||
|
||||
def normalize(self) -> 'vector[float]':
|
||||
mag = self.magnitude() + epsilon
|
||||
return self / mag
|
||||
|
||||
def __iter__(self) -> Iterable[variable[T] | T]:
|
||||
return iter(self.values)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from ._target import add_read_command
|
||||
from ._basic_types import Net, Op, Node, CPConstant, Write
|
||||
from ._compiler import compile_to_instruction_list, \
|
||||
from ._compiler import compile_to_dag, \
|
||||
stable_toposort, get_const_nets, get_all_dag_edges, add_read_ops, \
|
||||
add_write_ops
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ __all__ = [
|
|||
"Node",
|
||||
"CPConstant",
|
||||
"Write",
|
||||
"compile_to_instruction_list",
|
||||
"compile_to_dag",
|
||||
"stable_toposort",
|
||||
"get_const_nets",
|
||||
"get_all_dag_edges",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,27 @@ __attribute__((noinline)) int floor_div(float arg1, float arg2) {
|
|||
return i;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) float aux_sqrt(float n) {
|
||||
if (n < 0) return -1;
|
||||
|
||||
float x = n; // initial guess
|
||||
float epsilon = 0.00001; // desired accuracy
|
||||
|
||||
while ((x - n / x) > epsilon || (x - n / x) < -epsilon) {
|
||||
x = 0.5 * (x + n / x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) float aux_sqrt2(float n) {
|
||||
return n * 20.5 + 4.5;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) float aux_get_42(float n) {
|
||||
return n + 42.0;
|
||||
}
|
||||
|
||||
float fast_pow_float(float base, float exponent) {
|
||||
union {
|
||||
float f;
|
||||
|
|
@ -24,3 +45,13 @@ float fast_pow_float(float base, float exponent) {
|
|||
u.i = (uint32_t)y;
|
||||
return u.f;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Test aux functions
|
||||
float a = 16.0f;
|
||||
float sqrt_a = aux_sqrt(a);
|
||||
float pow_a = fast_pow_float(a, 0.5f);
|
||||
float sqrt2_a = aux_sqrt2(a);
|
||||
float g42 = aux_get_42(0.0f);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ from pathlib import Path
|
|||
import os
|
||||
|
||||
op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/', 'pow': '**',
|
||||
'gt': '>', 'eq': '==', 'ne': '!=', 'mod': '%'}
|
||||
'gt': '>', 'eq': '==', 'ge': '>=', 'ne': '!=', 'mod': '%'}
|
||||
|
||||
entry_func_prefix = ''
|
||||
stencil_func_prefix = '__attribute__((naked)) ' # Remove callee prolog
|
||||
|
|
@ -78,6 +78,15 @@ def get_cast(type1: str, type2: str, type_out: str) -> str:
|
|||
"""
|
||||
|
||||
|
||||
@norm_indent
|
||||
def get_func2(func_name: str, type1: str, type2: str) -> str:
|
||||
return f"""
|
||||
{stencil_func_prefix}void {func_name}_{type1}_{type2}({type1} arg1, {type2} arg2) {{
|
||||
result_float_{type2}(aux_{func_name}((float)arg1), arg2);
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
@norm_indent
|
||||
def get_conv_code(type1: str, type2: str, type_out: str) -> str:
|
||||
return f"""
|
||||
|
|
@ -189,7 +198,7 @@ if __name__ == "__main__":
|
|||
|
||||
# Scalar arithmetic:
|
||||
types = ['int', 'float']
|
||||
ops = ['add', 'sub', 'mul', 'div', 'floordiv', 'gt', 'eq', 'ne', 'pow']
|
||||
ops = ['add', 'sub', 'mul', 'div', 'floordiv', 'gt', 'ge', 'eq', 'ne', 'pow']
|
||||
|
||||
for t1 in types:
|
||||
code += get_result_stubs1(t1)
|
||||
|
|
@ -203,6 +212,11 @@ if __name__ == "__main__":
|
|||
t_out = 'int' if t1 == 'float' else 'float'
|
||||
code += get_cast(t1, t2, t_out)
|
||||
|
||||
for t1, t2 in permutate(types, types):
|
||||
code += get_func2('sqrt', t1, t2)
|
||||
code += get_func2('sqrt2', t1, t2)
|
||||
code += get_func2('get_42', t1, t2)
|
||||
|
||||
for op, t1, t2 in permutate(ops, types, types):
|
||||
t_out = t1 if t1 == t2 else 'float'
|
||||
if op == 'floordiv':
|
||||
|
|
@ -211,7 +225,7 @@ if __name__ == "__main__":
|
|||
code += get_op_code_float(op, t1, t2)
|
||||
elif op == 'pow':
|
||||
code += get_pow(t1, t2)
|
||||
elif op == 'gt' or op == 'eq' or op == 'ne':
|
||||
elif op in {'gt', 'eq', 'ge', 'ne'}:
|
||||
code += get_op_code(op, t1, t2, 'int')
|
||||
else:
|
||||
code += get_op_code(op, t1, t2, t_out)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from copapy import variable, NumLike
|
||||
from copapy.backend import Write, compile_to_instruction_list, add_read_command
|
||||
from copapy.backend import Write, compile_to_dag, add_read_command
|
||||
import copapy
|
||||
import subprocess
|
||||
import struct
|
||||
|
|
@ -49,7 +49,7 @@ def test_compile():
|
|||
|
||||
out = [Write(r) for r in ret]
|
||||
|
||||
il, variables = compile_to_instruction_list(out, copapy.generic_sdb)
|
||||
il, variables = compile_to_dag(out, copapy.generic_sdb)
|
||||
|
||||
# run program command
|
||||
il.write_com(_binwrite.Command.RUN_PROG)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from copapy import variable, NumLike
|
||||
from copapy.backend import Write, compile_to_instruction_list
|
||||
from copapy.backend import Write, compile_to_dag
|
||||
import copapy
|
||||
import subprocess
|
||||
from copapy import _binwrite
|
||||
|
|
@ -26,7 +26,7 @@ def test_compile():
|
|||
|
||||
out = [Write(r) for r in ret]
|
||||
|
||||
il, _ = compile_to_instruction_list(out, copapy.generic_sdb)
|
||||
il, _ = compile_to_dag(out, copapy.generic_sdb)
|
||||
|
||||
# run program command
|
||||
il.write_com(_binwrite.Command.RUN_PROG)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from coparun_module import coparun
|
||||
from copapy import variable
|
||||
from copapy.backend import Write, compile_to_instruction_list, add_read_command
|
||||
from copapy.backend import Write, compile_to_dag, add_read_command
|
||||
import copapy
|
||||
from copapy import _binwrite
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ def test_compile():
|
|||
r2 = i1 + 9
|
||||
out = [Write(r1), Write(r2), Write(c2)]
|
||||
|
||||
il, variables = compile_to_instruction_list(out, copapy.generic_sdb)
|
||||
il, variables = compile_to_dag(out, copapy.generic_sdb)
|
||||
|
||||
# run program command
|
||||
il.write_com(_binwrite.Command.RUN_PROG)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from copapy import NumLike, variable
|
||||
from copapy.backend import Write, Net, compile_to_instruction_list, add_read_command
|
||||
from copapy.backend import Write, Net, compile_to_dag, add_read_command
|
||||
import copapy
|
||||
import subprocess
|
||||
from copapy import _binwrite
|
||||
|
|
@ -29,7 +29,7 @@ def test_compile():
|
|||
|
||||
ret = function(c1, c2)
|
||||
|
||||
dw, variable_list = compile_to_instruction_list([Write(net) for net in ret], copapy.generic_sdb)
|
||||
dw, variable_list = compile_to_dag([Write(net) for net in ret], copapy.generic_sdb)
|
||||
|
||||
# run program command
|
||||
dw.write_com(_binwrite.Command.RUN_PROG)
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
from copapy import variable, Target
|
||||
import pytest
|
||||
import copapy
|
||||
|
||||
|
||||
def test_compile():
|
||||
c_i = variable(9)
|
||||
c_f = variable(2.5)
|
||||
# c_b = variable(True)
|
||||
|
||||
ret_test = (c_f ** c_f, c_i ** c_i)
|
||||
ret_ref = (2.5 ** 2.5, 9 ** 9)
|
||||
|
||||
tg = Target()
|
||||
print('* compile and copy ...')
|
||||
tg.compile(ret_test)
|
||||
print('* run and copy ...')
|
||||
tg.run()
|
||||
print('* finished')
|
||||
|
||||
for test, ref in zip(ret_test, ret_ref):
|
||||
assert isinstance(test, copapy.CPNumber)
|
||||
val = tg.read_value(test)
|
||||
print('+', val, ref, type(val), test.dtype)
|
||||
#for t in (int, float, bool):
|
||||
# assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
|
||||
assert val == pytest.approx(ref, 2), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_compile()
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from copapy import variable, Target
|
||||
import pytest
|
||||
import copapy as cp
|
||||
|
||||
|
||||
def test_corse():
|
||||
a_i = 9
|
||||
a_f = 2.5
|
||||
c_i = variable(a_i)
|
||||
c_f = variable(a_f)
|
||||
# c_b = variable(True)
|
||||
|
||||
ret_test = (c_f ** c_f, c_i ** c_i) # , c_i & 3)
|
||||
ret_refe = (a_f ** a_f, a_i ** a_i) # , a_i & 3)
|
||||
|
||||
tg = Target()
|
||||
print('* compile and copy ...')
|
||||
tg.compile(ret_test)
|
||||
print('* run and copy ...')
|
||||
tg.run()
|
||||
print('* finished')
|
||||
|
||||
for test, ref in zip(ret_test, ret_refe):
|
||||
assert isinstance(test, cp.variable)
|
||||
val = tg.read_value(test)
|
||||
print('+', val, ref, type(val), test.dtype)
|
||||
#for t in (int, float, bool):
|
||||
# assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
|
||||
assert val == pytest.approx(ref, 2), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
|
||||
def test_fine():
|
||||
a_i = 9
|
||||
a_f = 2.5
|
||||
c_i = variable(a_i)
|
||||
c_f = variable(a_f)
|
||||
# c_b = variable(True)
|
||||
|
||||
ret_test = (c_f ** 2, c_i ** -1, cp.sqrt(c_i), cp.sqrt(c_f)) # , c_i & 3)
|
||||
ret_refe = (a_f ** 2, a_i ** -1, cp.sqrt(a_i), cp.sqrt(a_f)) # , a_i & 3)
|
||||
|
||||
tg = Target()
|
||||
print('* compile and copy ...')
|
||||
tg.compile(ret_test)
|
||||
print('* run and copy ...')
|
||||
tg.run()
|
||||
print('* finished')
|
||||
|
||||
for test, ref in zip(ret_test, ret_refe):
|
||||
assert isinstance(test, cp.variable)
|
||||
val = tg.read_value(test)
|
||||
print('+', val, ref, type(val), test.dtype)
|
||||
#for t in (int, float, bool):
|
||||
# assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
|
||||
assert val == pytest.approx(ref, 0.001), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_corse()
|
||||
test_fine()
|
||||
|
|
@ -57,7 +57,7 @@ def test_compile():
|
|||
print('* finished')
|
||||
|
||||
for test, ref in zip(ret_test, ret_ref):
|
||||
assert isinstance(test, copapy.CPNumber)
|
||||
assert isinstance(test, copapy.variable)
|
||||
val = tg.read_value(test)
|
||||
print('+', val, ref, test.dtype)
|
||||
for t in (int, float, bool):
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ def test_compile():
|
|||
print('* finished')
|
||||
|
||||
for test, ref in zip(ret_test, ret_ref):
|
||||
assert isinstance(test, copapy.CPNumber)
|
||||
assert isinstance(test, copapy.variable)
|
||||
val = tg.read_value(test)
|
||||
print('+', val, ref, type(val), test.dtype)
|
||||
#for t in (int, float, bool):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import copapy as cp
|
||||
import pytest
|
||||
|
||||
|
||||
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))
|
||||
tt3 = (cp.vector(range(3)) + 5.6)
|
||||
tt4 = cp.vector([1.1, 2, 3]) + cp.vector(cp.variable(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))
|
||||
t2 = t1.sum()
|
||||
|
||||
t3 = cp.vector(cp.variable(1 / (v + 1)) for v in range(3))
|
||||
t4 = ((t3 * t1) * 2).sum()
|
||||
t5 = ((t3 * t1) * 2).magnitude()
|
||||
|
||||
tg = cp.Target()
|
||||
tg.compile(t2, t4, t5)
|
||||
tg.run()
|
||||
|
||||
assert isinstance(t2, cp.variable)
|
||||
assert tg.read_value(t2) == 10 + 11 + 12 + 0 + 1 + 2
|
||||
|
||||
assert isinstance(t4, cp.variable)
|
||||
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 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]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_compiled_vectors()
|
||||
|
|
@ -47,6 +47,8 @@ if __name__ == "__main__":
|
|||
offs = dr.read_int()
|
||||
reloc_type = dr.read_int()
|
||||
value = dr.read_int(signed=True)
|
||||
assert reloc_type == RelocationType.RELOC_RELATIVE_32.value
|
||||
program_data[offs:offs + 4] = value.to_bytes(4, byteorder, signed=True)
|
||||
print(f"PATCH_FUNC patch_offs={offs} reloc_type={reloc_type} value={value}")
|
||||
elif com == Command.PATCH_OBJECT:
|
||||
offs = dr.read_int()
|
||||
|
|
|
|||
|
|
@ -1,32 +1,43 @@
|
|||
from copapy import _binwrite, variable
|
||||
from copapy.backend import Write, compile_to_instruction_list
|
||||
import copapy
|
||||
from copapy import variable
|
||||
from copapy.backend import Write, compile_to_dag
|
||||
import copapy as cp
|
||||
from copapy._binwrite import Command
|
||||
|
||||
|
||||
def test_compile() -> None:
|
||||
|
||||
c1 = variable(9)
|
||||
"""Test compilation of a simple program."""
|
||||
c1 = variable(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]
|
||||
|
||||
out = [Write(r) for r in ret]
|
||||
|
||||
il, _ = compile_to_instruction_list(out, copapy.generic_sdb)
|
||||
dw, vars = compile_to_dag(out, cp.generic_sdb)
|
||||
|
||||
# run program command
|
||||
il.write_com(_binwrite.Command.RUN_PROG)
|
||||
dw.write_com(Command.RUN_PROG)
|
||||
|
||||
il.write_com(_binwrite.Command.READ_DATA)
|
||||
il.write_int(0)
|
||||
il.write_int(36)
|
||||
# read first 32 byte
|
||||
dw.write_com(Command.READ_DATA)
|
||||
dw.write_int(0)
|
||||
dw.write_int(32)
|
||||
|
||||
il.write_com(_binwrite.Command.END_COM)
|
||||
# read variables
|
||||
for addr, lengths, _ in vars.values():
|
||||
dw.write_com(Command.READ_DATA)
|
||||
dw.write_int(addr)
|
||||
dw.write_int(lengths)
|
||||
|
||||
dw.write_com(Command.END_COM)
|
||||
|
||||
print('* Data to runner:')
|
||||
il.print()
|
||||
dw.print()
|
||||
|
||||
il.to_file('bin/test.copapy')
|
||||
dw.to_file('bin/test.copapy')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -v
|
||||
|
||||
mkdir -p bin
|
||||
FILE=aux_functions
|
||||
SRC=stencils/$FILE.c
|
||||
DEST=bin
|
||||
OPT=O3
|
||||
|
||||
mkdir -p $DEST
|
||||
|
||||
# Compile native x86_64
|
||||
gcc -g -$OPT $SRC -o $DEST/$FILE
|
||||
chmod +x $DEST/$FILE
|
||||
|
||||
# Run
|
||||
$DEST/$FILE
|
||||
Loading…
Reference in New Issue