mirror of https://github.com/Nonannet/copapy.git
Merge pull request #16 from Nonannet/dev
- small improvements - readme extended
This commit is contained in:
commit
d95c3e3627
|
|
@ -44,6 +44,12 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Vendor pelfy
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 https://github.com/Nonannet/pelfy.git /tmp/pelfy
|
||||||
|
mkdir -p src/${{ github.event.repository.name }}/_vendor
|
||||||
|
cp -r /tmp/pelfy/src/pelfy src/${{ github.event.repository.name }}/_vendor/
|
||||||
|
|
||||||
# Only needed for Linux ARM builds
|
# Only needed for Linux ARM builds
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
@ -69,6 +75,11 @@ jobs:
|
||||||
if: contains(github.ref, '-beta') == false
|
if: contains(github.ref, '-beta') == false
|
||||||
needs: [build_wheels]
|
needs: [build_wheels]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: pypi
|
||||||
|
url: https://pypi.org/project/${{ github.event.repository.name }}/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install Twine
|
- name: Install Twine
|
||||||
run: pip install --force-reinstall twine==6.2.0
|
run: pip install --force-reinstall twine==6.2.0
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ name: CI Pipeline
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, dev]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, dev]
|
branches: [main, dev]
|
||||||
|
|
||||||
|
|
@ -30,6 +30,42 @@ jobs:
|
||||||
name: musl-object-files
|
name: musl-object-files
|
||||||
path: /object_files/musl_objects_*.*o
|
path: /object_files/musl_objects_*.*o
|
||||||
|
|
||||||
|
build-package-test:
|
||||||
|
needs: [build_stencils]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.12"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: stencil-object-files
|
||||||
|
path: src/copapy/obj
|
||||||
|
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install Python dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install -e .
|
||||||
|
python -m pip install pytest
|
||||||
|
|
||||||
|
- name: Vendor pelfy
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 https://github.com/Nonannet/pelfy.git /tmp/pelfy
|
||||||
|
mkdir -p src/${{ github.event.repository.name }}/_vendor
|
||||||
|
cp -r /tmp/pelfy/src/pelfy src/${{ github.event.repository.name }}/_vendor/
|
||||||
|
|
||||||
|
- name: Run tests with pytest
|
||||||
|
run: pytest -m "not runner"
|
||||||
|
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
needs: [build_stencils]
|
needs: [build_stencils]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ build/*
|
||||||
/*.obj
|
/*.obj
|
||||||
/src/*.pyd
|
/src/*.pyd
|
||||||
vc140.pdb
|
vc140.pdb
|
||||||
benchmark_results*
|
benchmark_results*.json
|
||||||
|
benchmark_results*.png
|
||||||
docs/build
|
docs/build
|
||||||
docs/source/api
|
docs/source/api
|
||||||
/libs/
|
/libs/
|
||||||
|
|
|
||||||
145
README.md
145
README.md
|
|
@ -1,39 +1,60 @@
|
||||||
# Copapy
|
# Copapy
|
||||||
Copapy is a python framework for deterministic low latency realtime computations, targeting hardware applications - for example in the field of robotics, aerospace, embedded systems and control systems in general.
|
|
||||||
|
|
||||||
GPU frameworks like PyTorch, JAX and TensorFlow jump started the development in the field of AI. With the right balance of flexibility and performance they allow for fast iterations of new ideas while being performant enough to test them or even use them in production.
|
Copapy is a Python framework for deterministic, low-latency realtime computation, targeting hardware applications - for example in the fields of robotics, aerospace, embedded systems and control systems in general.
|
||||||
|
|
||||||
This is exactly what Copapy is aiming for - but in the field of embedded realtime computation. While making use of the ergonomics of Python, the tooling and the general Python ecosystem, Copapy runs seamlessly optimized machine code. Despite being highly portable, the **copy and patch** compiler allows for effortless and fast deployment, without any dependencies beyond Python. It's designed to feel like writing python scripts, with a flat learning curve. But under the hood it produces high performance static typed and memory save code with a minimized set of possible runtime errors[^1]. To maximize productivity the framework provides detailed type hints to catch most errors even before compilation.
|
GPU frameworks like PyTorch, JAX and TensorFlow jump-started the development in the field of AI. With the right balance of flexibility and performance, they allow for fast iteration of new ideas while still being performant enough to test or even use them in production.
|
||||||
|
|
||||||
Embedded systems comes with a variety of CPU architectures. The **copy and patch** compiler already supports the most common ones [^3] and porting it to new architectures is effortless if a C compiler for the target architecture is available [^2]. The generated code depends only on the CPU architecture. The actual generated code does neither do system calls nor calling external libraries like libc. This allows Copapy for one to be highly deterministic and for the other it makes targeting different realtime operating systems or bare metal straight forward.
|
This is exactly what Copapy aims for - but in the field of embedded realtime computation. While making use of the ergonomics of Python, the tooling, and the general Python ecosystem, Copapy runs seamlessly optimized machine code. Despite being highly portable, the **copy-and-patch** compiler allows for effortless and fast deployment without any dependencies beyond Python. It's designed to feel like writing Python scripts with a shallow learning curve, but under the hood it produces high-performance, statically typed and memory-safe code with a minimized set of possible runtime errors[^1]. To maximize productivity, the framework provides detailed type hints to catch most errors even before compilation.
|
||||||
|
|
||||||
The summarized main features are:
|
Embedded systems come with a variety of CPU architectures. The **copy-and-patch** compiler already supports the most common ones[^3], and porting it to new architectures is straightforward if a C compiler for the target architecture is available[^2]. The generated code depends only on the CPU architecture. The generated binaries neither perform system calls nor rely on external libraries like libc. This makes Copapy both highly deterministic and easy to deploy on different realtime operating systems (RTOS) or bare metal.
|
||||||
|
|
||||||
|
The main features can be summarized as:
|
||||||
- Fast to write & easy to read
|
- Fast to write & easy to read
|
||||||
- Memory and type safety, minimal set of runtime errors
|
- Memory and type safety with a minimal set of runtime errors
|
||||||
- deterministic execution
|
- Deterministic execution
|
||||||
- Auto grad for efficient realtime optimizations
|
- Autograd for efficient realtime optimization
|
||||||
- Optimized machine code for the target architectures x68_64, Aarch64 and ARMv7
|
- Optimized machine code for x86_64, AArch64 and ARMv7
|
||||||
- Very portable to new architectures
|
- Highly portable to new architectures
|
||||||
- Small python package, minimal dependencies, no cross compile toolchain required
|
- Small Python package with minimal dependencies and no cross-compile toolchain required
|
||||||
|
|
||||||
|
Execution of the compiled code is managed by a runner application. The runner is implemented in C and handles I/O and communication with the Copapy framework. The overall design emphasizes minimal complexity of the runner to simplify portability, since this part must be adapted for the individual hardware/application. Because patching of memory addresses is done by the runner, the different architecture-specific relocation types are unified to an architecture-independent format by Copapy before sending the patch instructions to the runner. This keeps the runner implementation as minimal as possible.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The design targets either an architecture with a realtime-patched Linux kernel - where the runner uses the same CPU and memory as Linux but executes in a realtime thread - or a setup where even higher determinism is required. In such cases, the runner can be executed on a separate crossover MCU running on bare metal or a RTOS.
|
||||||
|
|
||||||
|
The Copapy framework also includes a runner as Python module build from the same C code. This allows frictionless testing of code and might be valuable for using Copapy in conventional application development.
|
||||||
|
|
||||||
## Current state
|
## Current state
|
||||||
While obviously hardware IO is a core aspect, this is not yet available. Therefore this package is at the moment a proof of concept with limited direct use. However the computation part is fully working and available for testing and playing with it by simply installing the package. At this point the project is quite close to being ready for integration into the first demonstration hardware platform.
|
|
||||||
|
|
||||||
Currently worked on:
|
While hardware I/O is obviously a core aspect of the project, it is not yet available. Therefore, this package is currently a proof of concept with limited direct use. However, the computation engine is fully functional and available for testing and experimentation simply by installing the package. The project is now close to being ready for integration into its first demonstration hardware platform.
|
||||||
- Array stencils for handling very large arrays and generate SIMD optimized code - e.g. for machine vision and neural network applications.
|
|
||||||
- For targeting Crossover‑MCUs, support for Thumb instructions required by ARM*-M is on the way.
|
Currently in development:
|
||||||
- Constant-regrouping for symbolic optimization of the computation graph.
|
- Array stencils for handling very large arrays and generating SIMD-optimized code - e.g., for machine vision and neural network applications
|
||||||
|
- Support for Thumb instructions required by ARM*-M targets (for MCUs)
|
||||||
|
- Constant regrouping for further symbolic optimization of the computation graph
|
||||||
|
|
||||||
|
Despite missing SIMD-optimization, benchmark performance shows promising numbers. The following chart plots the results in comparison to NumPy 2.3.5:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
For the benchmark (`tests/benchmark.py`) the timing of 30000 iterations for calculating the therm `sum((v1 + i) @ v2 for i in range(10))` where measured on an Ryzen 5 3400G. Where the vectors `v1` and `v2` both have a lengths of `v_size` which was varied according to the chart from 10 to 600. For the NumPy case the "i in range(10)" loop was vectorized like this: `np.sum((v1 + i) @ v2)` with i being here a `NDArray` with a dimension of `[10, 1]`. The number of calculated scalar operations is the same for both contenders. Obviously copapy profits from less overheat by calling a single function from python per iteration, where the NumPy variant requires 3. Interestingly there is no indication visible in the chart that for increasing `v_size` the calling overhead for NumPy will be compensated by using faster SIMD instructions.
|
||||||
|
|
||||||
|
Furthermore for many applications copypy will benefit by reducing the actual number of operations significantly compared to a NumPy implementation, by precompute constant values know at compile time and benefiting from sparcity. Multiplying by zero (e.g. in a diagonal matrix) eliminate a hole branch in the computation graph. Operations without effect, like multiplications by 1 oder additions with zero gets eliminated at compile time.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
To install copapy, you can use pip. Precompiled wheels are available for Linux (x86_64, Aarch64 and ARMv7), Windows (x86_64) and Mac OS (x86_64, Aarch64):
|
|
||||||
|
To install Copapy, you can use pip. Precompiled wheels are available for Linux (x86_64, AArch64, ARMv7), Windows (x86_64) and macOS (x86_64, AArch64):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install copapy
|
pip install copapy
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Basic example
|
### Basic example
|
||||||
A very simple example program using copapy can look like this:
|
|
||||||
|
A very simple example program using Copapy can look like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import copapy as cp
|
import copapy as cp
|
||||||
|
|
@ -59,9 +80,8 @@ print("Result e:", tg.read_value(e))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inverse kinematics
|
### Inverse kinematics
|
||||||
An other example using autograd in copapy. Here for for implementing
|
|
||||||
gradient descent to solve a reverse kinematic problem for
|
Another example using autograd in Copapy, here implementing gradient descent to solve an inverse kinematics problem for a two-joint 2D arm:
|
||||||
a two joint 2D arm:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import copapy as cp
|
import copapy as cp
|
||||||
|
|
@ -79,13 +99,13 @@ def forward_kinematics(theta1, theta2):
|
||||||
"""Return positions of joint and end-effector."""
|
"""Return positions of joint and end-effector."""
|
||||||
joint = cp.vector([l1 * cp.cos(theta1), l1 * cp.sin(theta1)])
|
joint = cp.vector([l1 * cp.cos(theta1), l1 * cp.sin(theta1)])
|
||||||
end_effector = joint + cp.vector([l2 * cp.cos(theta1 + theta2),
|
end_effector = joint + cp.vector([l2 * cp.cos(theta1 + theta2),
|
||||||
l2 * cp.sin(theta1 + theta2)])
|
l2 * cp.sin(theta1 + theta2)])
|
||||||
return joint, end_effector
|
return joint, end_effector
|
||||||
|
|
||||||
# Start values
|
# Start values
|
||||||
theta = cp.vector([cp.value(0.0), cp.value(0.0)])
|
theta = cp.vector([cp.value(0.0), cp.value(0.0)])
|
||||||
|
|
||||||
# Iterative inverse kinematic
|
# Iterative inverse kinematics
|
||||||
for _ in range(48):
|
for _ in range(48):
|
||||||
joint, effector = forward_kinematics(theta[0], theta[1])
|
joint, effector = forward_kinematics(theta[0], theta[1])
|
||||||
error = ((target - effector) ** 2).sum()
|
error = ((target - effector) ** 2).sum()
|
||||||
|
|
@ -101,31 +121,75 @@ print(f"Joint position: {tg.read_value(joint)}")
|
||||||
print(f"End-effector position: {tg.read_value(effector)}")
|
print(f"End-effector position: {tg.read_value(effector)}")
|
||||||
print(f"quadratic error = {tg.read_value(error)}")
|
print(f"quadratic error = {tg.read_value(error)}")
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Joint angles: [-0.7221821546554565, 2.6245293617248535]
|
Joint angles: [-0.7221821546554565, 2.6245293617248535]
|
||||||
Joint position: [1.3509329557418823, -1.189529299736023]
|
Joint position: [1.3509329557418823, -1.189529299736023]
|
||||||
End-effector position: [0.6995794177055359, 0.7014330625534058]
|
End-effector position: [0.6995794177055359, 0.7014330625534058]
|
||||||
quadratic error = 2.2305819129542215e-06
|
quadratic error = 2.2305819129542215e-06
|
||||||
```
|
```
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
The **Compilation** step starts with tracing the python code to generate an acyclic directed graph (DAG) of variables and operations. The DAG can be optimized and gets than linearized to a sequence of operations. Each operation gets mapped to a pre-compiled stencil, which is a piece of machine code with placeholders for memory addresses. The compiler generates patch instructions to fill the placeholders with the correct memory addresses. The binary code build from the stencils, data for constants and the patch instructions are than passed to the runner for execution. The runner allocates memory for the code and data, applies the patch instructions to correct memory addresses and finally executes the code.
|
|
||||||
|
The compilation step starts with tracing the Python code to generate an acyclic directed graph (DAG) of variables and operations. The code can contain functions, closures, branching, and so on, but conditional branching is only allowed when the condition is known at tracing time (a `cp.iif` function exists to work around this). In the next step, this DAG is optimized and linearized into a sequence of operations. Each operation is mapped to a precompiled stencil or a combination of several stencils. A stencil is a piece of machine code with placeholders for memory addresses pointing to other code or data. The compiler generates patch instructions that fill these placeholders with the correct memory addresses.
|
||||||
|
|
||||||
|
After compilation, the binary code built from the stencils, the constant data, and the patch instructions is handed to the runner for execution. The runner allocates memory for code and data, copies both into place, applies the patch instructions, and finally executes the code.
|
||||||
|
|
||||||
|
The C code for a very simple stencil can look like this:
|
||||||
|
|
||||||
|
```c
|
||||||
|
add_float_float(float arg1, float arg2) {
|
||||||
|
result_float_float(arg1 + arg2, arg2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The call to the dummy function `result_float_float` ensures that the compiler keeps the result and the second operand in registers for later use. The dummy function acts as a placeholder for the next stencil. Copapy uses two virtual registers, which map on most relevant architectures to actual hardware registers. Data that cannot be kept in a register is stored in statically allocated heap memory. Stack memory may be used inside some stencils, but its usage is essentially fixed and independent of the Copapy program, so total memory requirements are known at compile time.
|
||||||
|
|
||||||
|
The machine code for the function above, compiled for x86_64, looks like this:
|
||||||
|
|
||||||
|
```assembly
|
||||||
|
0000000000000000 <add_float_float>:
|
||||||
|
0: f3 0f 58 c1 addss %xmm1,%xmm0
|
||||||
|
4: e9 00 00 00 00 jmp 9 <.LC1+0x1>
|
||||||
|
5: R_X86_64_PLT32 result_float_float-0x4
|
||||||
|
```
|
||||||
|
|
||||||
|
Based on the relocation entry for the `jmp` to the symbol `result_float_float`, the `jmp` instruction is stripped when it is the last instruction in a stencil. Thus, a Copapy addition operation results in a single instruction. For stencils containing multiple branch exits, only the final `jmp` is removed; the others are patched to jump to the next stencil.
|
||||||
|
|
||||||
|
For more complex operations - where inlining is less useful - stencils call a non-stencil function, such as in this example:
|
||||||
|
|
||||||
|
```assembly
|
||||||
|
0000000000000000 <sin_float>:
|
||||||
|
0: 48 83 ec 08 sub $0x8,%rsp
|
||||||
|
4: e8 00 00 00 00 call 9 <sin_float+0x9>
|
||||||
|
5: R_X86_64_PLT32 sinf-0x4
|
||||||
|
9: 48 83 c4 08 add $0x8,%rsp
|
||||||
|
d: e9 00 00 00 00 jmp 12 <.LC0+0x2>
|
||||||
|
e: R_X86_64_PLT32 result_float-0x4
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike stencils, non-stencil functions are not stripped and do not need to be tail-call-optimizable.
|
||||||
|
|
||||||
|
Non-stencil functions and constants are stored together with the stencils in an ELF object file for each supported CPU architecture. The required non-stencil functions and constants are bundled during compilation. The compiler includes only the data and code required for the specific program.
|
||||||
|
|
||||||
|
The whole compilation process is independent of the actual instruction set. It relies purely on relocation entries and symbol metadata from the ELF file generated by the C compiler.
|
||||||
|
|
||||||
## Developer Guide
|
## Developer Guide
|
||||||
Contributions are welcome, please open an issue or submit a pull request on GitHub.
|
|
||||||
|
|
||||||
To get started with developing the package, first clone the repository using Git:
|
Feedback and contributions are welcome - please open an issue or submit a pull request on GitHub.
|
||||||
|
|
||||||
|
To get started with development, first clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Nonannet/copapy.git
|
git clone https://github.com/Nonannet/copapy.git
|
||||||
cd copapy
|
cd copapy
|
||||||
```
|
```
|
||||||
|
|
||||||
You may setup a venv:
|
You may set up a virtual environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate # On Windows `.venv\Scripts\activate`
|
source .venv/bin/activate # On Windows: `.venv\Scripts\activate`
|
||||||
```
|
```
|
||||||
|
|
||||||
Build and install the package and dev dependencies:
|
Build and install the package and dev dependencies:
|
||||||
|
|
@ -134,37 +198,40 @@ Build and install the package and dev dependencies:
|
||||||
pip install -e .[dev]
|
pip install -e .[dev]
|
||||||
```
|
```
|
||||||
|
|
||||||
If the build fails because you have no suitable c compiler installed, you can either install a compiler (obviously) or use the binary from pypi:
|
If the build fails because no suitable C compiler is installed, you can either install one or use the binary package from PyPI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install copapy[dev]
|
pip install copapy[dev]
|
||||||
```
|
```
|
||||||
|
|
||||||
When running pytest it will use the binary part from pypi but all the python code gets executed from the local repo.
|
When running pytest, it will use the binary components from PyPI, but all Python code is executed from the local repository.
|
||||||
|
|
||||||
For running all tests you need the stencil object files and the compiled runner. You can download the stencils and binary runner from GitHub or build them with gcc yourself.
|
To run all tests, you need the stencil object files and the compiled runner. You can download them from GitHub or build them yourself with gcc.
|
||||||
|
|
||||||
For downloading the latest binaries from GitHub run:
|
Download the latest binaries from GitHub:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python tools/get_binaries.py
|
python tools/get_binaries.py
|
||||||
```
|
```
|
||||||
|
|
||||||
To build the binaries from source on Linux run:
|
Build the binaries from source on Linux:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash tools/build.sh
|
bash tools/build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure that everything is set up correctly by running the tests:
|
Run the tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest
|
pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This project is licensed under GPL - see the [LICENSE](LICENSE) file for details.
|
|
||||||
|
|
||||||
[^1]: Currently errors like divide by zero are possible. The feasibility of tacking value ranges in the type system is under investigation to be able to do checks at compile time.
|
This project is licensed under the MIT license - see the [LICENSE](LICENSE) file for details.
|
||||||
[^2]: The compiler must support TCO (tail call optimization). Currently gcc as C compiler is supported. Porting to a new architecture requires to implement a subset of relocation types used by the architecture.
|
|
||||||
[^3]: Supported are x68_64, Aarch64, ARMv7 (non-Thumb); ARMv6/7-M (Thumb) is under development; code for x68 32 Bit is present but has open issues (low priority).
|
[^1]: Errors like divide-by-zero are currently still possible. The feasibility of tracking value ranges in the type system is under investigation to enable compile-time checks.
|
||||||
|
|
||||||
|
[^2]: The compiler must support tail-call optimization (TCO). Currently, GCC is supported. Porting to a new architecture requires implementing a subset of relocation types used by that architecture.
|
||||||
|
|
||||||
|
[^3]: Supported architectures: x86_64, AArch64, ARMv7 (non-Thumb). ARMv6/7-M (Thumb) support is in development. Code for x86 32-bit exists but has unresolved issues and a low priority.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,306 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="432pt" height="288pt" viewBox="0 0 432 288" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path {
|
||||||
|
stroke: #EEEEEE !important;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
fill: #EEEEEE !important;
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
fill: #444444 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
path {
|
||||||
|
stroke: #444444 !important;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
fill: #444444 !important;
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
fill: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
stroke: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<cc:Work>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
|
<dc:date>2025-12-16T11:37:29.841711</dc:date>
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Matplotlib v3.10.7, https://matplotlib.org/</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs>
|
||||||
|
<style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>
|
||||||
|
</defs>
|
||||||
|
<g id="figure_1">
|
||||||
|
<g id="patch_1">
|
||||||
|
<path d="M 0 288
|
||||||
|
L 432 288
|
||||||
|
L 432 0
|
||||||
|
L 0 0
|
||||||
|
L 0 288
|
||||||
|
z
|
||||||
|
" style="fill: none"/>
|
||||||
|
</g>
|
||||||
|
<g id="axes_1">
|
||||||
|
<g id="patch_2">
|
||||||
|
<path d="M 47.72 245.93875
|
||||||
|
L 421.2 245.93875
|
||||||
|
L 421.2 10.8
|
||||||
|
L 47.72 10.8
|
||||||
|
L 47.72 245.93875
|
||||||
|
z
|
||||||
|
" style="fill: none"/>
|
||||||
|
</g>
|
||||||
|
<g id="matplotlib.axis_1">
|
||||||
|
<g id="xtick_1">
|
||||||
|
<g id="line2d_1">
|
||||||
|
<defs>
|
||||||
|
<path id="m259558fe05" d="M 0 0
|
||||||
|
L 0 3.5
|
||||||
|
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="57.767236" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_1">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="57.767236" y="260.537188" transform="rotate(-0 57.767236 260.537188)">0</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_2">
|
||||||
|
<g id="line2d_2">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="127.058516" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_2">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="127.058516" y="260.537188" transform="rotate(-0 127.058516 260.537188)">100</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_3">
|
||||||
|
<g id="line2d_3">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="196.349796" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_3">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="196.349796" y="260.537188" transform="rotate(-0 196.349796 260.537188)">200</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_4">
|
||||||
|
<g id="line2d_4">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="265.641076" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_4">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="265.641076" y="260.537188" transform="rotate(-0 265.641076 260.537188)">300</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_5">
|
||||||
|
<g id="line2d_5">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="334.932356" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_5">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="334.932356" y="260.537188" transform="rotate(-0 334.932356 260.537188)">400</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_6">
|
||||||
|
<g id="line2d_6">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m259558fe05" x="404.223636" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_6">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="404.223636" y="260.537188" transform="rotate(-0 404.223636 260.537188)">500</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_7">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="234.46" y="274.215312" transform="rotate(-0 234.46 274.215312)">Vector Size (v_size)</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="matplotlib.axis_2">
|
||||||
|
<g id="ytick_1">
|
||||||
|
<g id="line2d_7">
|
||||||
|
<defs>
|
||||||
|
<path id="mc468514af5" d="M 0 0
|
||||||
|
L -3.5 0
|
||||||
|
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="245.93875" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_8">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="249.737969" transform="rotate(-0 40.72 249.737969)">0.0</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_2">
|
||||||
|
<g id="line2d_8">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="205.286366" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_9">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="209.085585" transform="rotate(-0 40.72 209.085585)">0.1</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_3">
|
||||||
|
<g id="line2d_9">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="164.633982" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_10">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="168.4332" transform="rotate(-0 40.72 168.4332)">0.2</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_4">
|
||||||
|
<g id="line2d_10">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="123.981597" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_11">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="127.780816" transform="rotate(-0 40.72 127.780816)">0.3</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_5">
|
||||||
|
<g id="line2d_11">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="83.329213" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_12">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="87.128432" transform="rotate(-0 40.72 87.128432)">0.4</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_6">
|
||||||
|
<g id="line2d_12">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#mc468514af5" x="47.72" y="42.676829" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_13">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: end" x="40.72" y="46.476048" transform="rotate(-0 40.72 46.476048)">0.5</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_14">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: middle" x="18.737188" y="128.369375" transform="rotate(-90 18.737188 128.369375)">Elapsed Time (seconds)</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="line2d_13">
|
||||||
|
<defs>
|
||||||
|
<path id="m6b2427ccd4" d="M 0 2.5
|
||||||
|
C 0.663008 2.5 1.29895 2.236584 1.767767 1.767767
|
||||||
|
C 2.236584 1.29895 2.5 0.663008 2.5 0
|
||||||
|
C 2.5 -0.663008 2.236584 -1.29895 1.767767 -1.767767
|
||||||
|
C 1.29895 -2.236584 0.663008 -2.5 0 -2.5
|
||||||
|
C -0.663008 -2.5 -1.29895 -2.236584 -1.767767 -1.767767
|
||||||
|
C -2.236584 -1.29895 -2.5 -0.663008 -2.5 0
|
||||||
|
C -2.5 0.663008 -2.236584 1.29895 -1.767767 1.767767
|
||||||
|
C -1.29895 2.236584 -0.663008 2.5 0 2.5
|
||||||
|
z
|
||||||
|
" style="stroke: #1f77b4"/>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#p2582994344)">
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="64.696364" y="213.714987" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="78.55462" y="209.105698" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="99.342004" y="202.871817" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="127.058516" y="195.087496" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="196.349796" y="175.187625" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="265.641076" y="156.027506" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="334.932356" y="132.602586" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="404.223636" y="115.587124" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="line2d_14">
|
||||||
|
<defs>
|
||||||
|
<path id="m1b160ac523" d="M 0 2.5
|
||||||
|
C 0.663008 2.5 1.29895 2.236584 1.767767 1.767767
|
||||||
|
C 2.236584 1.29895 2.5 0.663008 2.5 0
|
||||||
|
C 2.5 -0.663008 2.236584 -1.29895 1.767767 -1.767767
|
||||||
|
C 1.29895 -2.236584 0.663008 -2.5 0 -2.5
|
||||||
|
C -0.663008 -2.5 -1.29895 -2.236584 -1.767767 -1.767767
|
||||||
|
C -2.236584 -1.29895 -2.5 -0.663008 -2.5 0
|
||||||
|
C -2.5 0.663008 -2.236584 1.29895 -1.767767 1.767767
|
||||||
|
C -1.29895 2.236584 -0.663008 2.5 0 2.5
|
||||||
|
z
|
||||||
|
" style="stroke: #ff7f0e"/>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#p2582994344)">
|
||||||
|
<use xlink:href="#m1b160ac523" x="64.696364" y="120.890675" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="78.55462" y="115.992103" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="99.342004" y="109.475323" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="127.058516" y="103.160463" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="196.349796" y="84.651839" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="265.641076" y="61.452417" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="334.932356" y="46.159112" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
<use xlink:href="#m1b160ac523" x="404.223636" y="20.462618" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="patch_3">
|
||||||
|
<path d="M 47.72 245.93875
|
||||||
|
L 47.72 10.8
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_4">
|
||||||
|
<path d="M 421.2 245.93875
|
||||||
|
L 421.2 10.8
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_5">
|
||||||
|
<path d="M 47.72 245.93875
|
||||||
|
L 421.2 245.93875
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_6">
|
||||||
|
<path d="M 47.72 10.8
|
||||||
|
L 421.2 10.8
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="legend_1">
|
||||||
|
<g id="line2d_15">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m6b2427ccd4" x="66.72" y="23.898438" style="fill: #1f77b4; stroke: #1f77b4"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_15">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: start" x="84.72" y="27.398438" transform="rotate(-0 84.72 27.398438)">Copapy</text>
|
||||||
|
</g>
|
||||||
|
<g id="line2d_16">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m1b160ac523" x="66.72" y="38.576562" style="fill: #ff7f0e; stroke: #ff7f0e"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_16">
|
||||||
|
<text style="font-size: 10px; font-family: 'DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', sans-serif; text-anchor: start" x="84.72" y="42.076562" transform="rotate(-0 84.72 42.076562)">NumPy</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="p2582994344">
|
||||||
|
<rect x="47.72" y="10.8" width="373.48" height="235.13875"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="140mm"
|
||||||
|
height="33mm"
|
||||||
|
viewBox="0 0 140 33"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<style>
|
||||||
|
/* Light mode */
|
||||||
|
:root {
|
||||||
|
--currentColor: black;
|
||||||
|
--bg: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode override */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--currentColor: #f5f5f5;
|
||||||
|
--bg: #101010;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.currentColor { fill: var(--fg); }
|
||||||
|
text { font-family: sans-serif; }
|
||||||
|
</style>
|
||||||
|
<defs id="defs1">
|
||||||
|
<marker style="overflow:visible" id="marker13" refX="0" refY="0" orient="auto-start-reverse" markerWidth="1" markerHeight="1" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid">
|
||||||
|
<path transform="scale(0.7)" d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z" style="fill:context-stroke;fill-rule:evenodd;stroke:none" id="path13" />
|
||||||
|
</marker>
|
||||||
|
<marker style="overflow:visible" id="ConcaveTriangle" refX="0" refY="0" orient="auto-start-reverse" markerWidth="1" markerHeight="1" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid">
|
||||||
|
<path transform="scale(0.7)" d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z" style="fill:context-stroke;fill-rule:evenodd;stroke:none" id="path7" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<!--<rect style="fill:var(-\-bg);stroke:none;fill-opacity:1;" id="bground" width="140" height="33" x="0" y="0" />-->
|
||||||
|
<g id="layer1" transform="translate(-22.201859,-83.671639)">
|
||||||
|
<rect style="fill:none;stroke:var(--currentColor);stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;" id="rect1" width="57.569527" height="29.707123" x="103.93349" y="86.473648" />
|
||||||
|
<rect style="fill:none;stroke:var(--currentColor);stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;" id="rect1-8" width="38.838821" height="17.486176" x="23.394859" y="98.501587" />
|
||||||
|
<text xml:space="preserve" style="font-size:4.23333px;fill:var(--currentColor);stroke:none;" x="34.96566" y="108.76154" id="text1">Runner</text>
|
||||||
|
<path style="fill:none;stroke:var(--currentColor);stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;marker-start:url(#ConcaveTriangle)" d="M 66.608701,102.12517 H 98.133736" id="path1" />
|
||||||
|
<path style="fill:none;stroke:var(--currentColor);stroke-width:0.392;stroke-linecap:square;stroke-miterlimit:2.5;marker-start:url(#ConcaveTriangle);marker-end:url(#marker13)" d="M 99.35918,111.92104 H 66.646501" id="path1-1" />
|
||||||
|
<text style="font-size:3.52778px;fill:var(--currentColor);" x="67.950348" y="94.343025" id="text7">Compiled code &</text>
|
||||||
|
<text style="font-size:3.52778px;fill:var(--currentColor);" x="67.803932" y="98.791328" id="text7-6">Patch instructions</text>
|
||||||
|
<text style="font-size:3.52778px;fill:var(--currentColor);" x="78.810158" y="110.24088" id="text7-6-2">Data</text>
|
||||||
|
<path style="fill:none;stroke:var(--currentColor);stroke-width:0.420363;stroke-linecap:square;stroke-miterlimit:2.5;marker-start:url(#ConcaveTriangle);marker-end:url(#marker13)" d="m 42.81427,91.377955 v 3.753178" id="path1-1-2" />
|
||||||
|
<text style="font-size:4.23333px;fill:var(--currentColor);" x="32.480026" y="87.887978" id="text1-4">Hardware</text>
|
||||||
|
<text style="font-size:4.23333px;fill:var(--currentColor);" x="45.371216" y="94.793465" id="text1-4-4">IO</text>
|
||||||
|
<text style="font-size:4.23333px;fill:var(--currentColor);" x="110.51713" y="97.619591" id="text1-1">Copapy framework &</text>
|
||||||
|
<text style="font-size:4.23333px;fill:var(--currentColor);" x="110.39734" y="102.7154" id="text1-1-2">copy & patch</text>
|
||||||
|
<text style="font-size:4.23333px;fill:var(--currentColor);" x="110.39734" y="107.8417" id="text15">cross compiler</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,272 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="138.49416mm"
|
||||||
|
height="31.702131mm"
|
||||||
|
viewBox="0 0 138.49416 31.702131"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="base_concept.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.0167041"
|
||||||
|
inkscape:cx="245.89259"
|
||||||
|
inkscape:cy="156.87947"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1009"
|
||||||
|
inkscape:window-x="3832"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="marker13"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto-start-reverse"
|
||||||
|
inkscape:stockid="Concave triangle arrow"
|
||||||
|
markerWidth="1"
|
||||||
|
markerHeight="1"
|
||||||
|
viewBox="0 0 1 1"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always"
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path
|
||||||
|
transform="scale(0.7)"
|
||||||
|
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||||
|
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||||
|
id="path13" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="ConcaveTriangle"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto-start-reverse"
|
||||||
|
inkscape:stockid="Concave triangle arrow"
|
||||||
|
markerWidth="1"
|
||||||
|
markerHeight="1"
|
||||||
|
viewBox="0 0 1 1"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always"
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path
|
||||||
|
transform="scale(0.7)"
|
||||||
|
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||||
|
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||||
|
id="path7" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="ConcaveTriangle-5"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto-start-reverse"
|
||||||
|
inkscape:stockid="Concave triangle arrow"
|
||||||
|
markerWidth="1"
|
||||||
|
markerHeight="1"
|
||||||
|
viewBox="0 0 1 1"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always"
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path
|
||||||
|
transform="scale(0.7)"
|
||||||
|
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||||
|
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||||
|
id="path7-5" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="ConcaveTriangle-5-5"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto-start-reverse"
|
||||||
|
inkscape:stockid="Concave triangle arrow"
|
||||||
|
markerWidth="1"
|
||||||
|
markerHeight="1"
|
||||||
|
viewBox="0 0 1 1"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always"
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path
|
||||||
|
transform="scale(0.7)"
|
||||||
|
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||||
|
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||||
|
id="path7-5-7" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="marker13-6"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto-start-reverse"
|
||||||
|
inkscape:stockid="Concave triangle arrow"
|
||||||
|
markerWidth="1"
|
||||||
|
markerHeight="1"
|
||||||
|
viewBox="0 0 1 1"
|
||||||
|
inkscape:isstock="true"
|
||||||
|
inkscape:collect="always"
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path
|
||||||
|
transform="scale(0.7)"
|
||||||
|
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||||
|
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||||
|
id="path13-1" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Ebene 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-23.201859,-84.671639)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
id="rect1"
|
||||||
|
width="57.569527"
|
||||||
|
height="29.707123"
|
||||||
|
x="103.93349"
|
||||||
|
y="86.473648" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
id="rect1-8"
|
||||||
|
width="38.838821"
|
||||||
|
height="17.486176"
|
||||||
|
x="23.394859"
|
||||||
|
y="98.501587" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
x="34.96566"
|
||||||
|
y="108.76154"
|
||||||
|
id="text1"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="34.96566"
|
||||||
|
y="108.76154">Runner</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.386;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#ConcaveTriangle)"
|
||||||
|
d="M 66.608701,102.12517 H 98.133736"
|
||||||
|
id="path1"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.392;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#ConcaveTriangle-5);marker-end:url(#marker13)"
|
||||||
|
d="M 99.35918,111.92104 H 66.646501"
|
||||||
|
id="path1-1"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:3.52778px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.4;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="67.950348"
|
||||||
|
y="94.343025"
|
||||||
|
id="text7"><tspan
|
||||||
|
style="font-size:3.52778px;stroke-width:0.4"
|
||||||
|
x="67.950348"
|
||||||
|
y="94.343025"
|
||||||
|
id="tspan12"
|
||||||
|
sodipodi:role="line">Compiled code &</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:3.52778px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.4;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="67.803932"
|
||||||
|
y="98.791328"
|
||||||
|
id="text7-6"><tspan
|
||||||
|
style="font-size:3.52778px;stroke-width:0.4"
|
||||||
|
x="67.803932"
|
||||||
|
y="98.791328"
|
||||||
|
id="tspan12-1"
|
||||||
|
sodipodi:role="line">Patch instructions</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:3.52778px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.4;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="78.810158"
|
||||||
|
y="110.24088"
|
||||||
|
id="text7-6-2"><tspan
|
||||||
|
style="font-size:3.52778px;stroke-width:0.4"
|
||||||
|
x="78.810158"
|
||||||
|
y="110.24088"
|
||||||
|
id="tspan12-1-1"
|
||||||
|
sodipodi:role="line">Data</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.420363;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#ConcaveTriangle-5-5);marker-end:url(#marker13-6)"
|
||||||
|
d="m 42.81427,91.377955 v 3.753178"
|
||||||
|
id="path1-1-2"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
x="32.480026"
|
||||||
|
y="87.887978"
|
||||||
|
id="text1-4"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1-1"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="32.480026"
|
||||||
|
y="87.887978">Hardware</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
x="45.371216"
|
||||||
|
y="94.793465"
|
||||||
|
id="text1-4-4"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1-1-2"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="45.371216"
|
||||||
|
y="94.793465" /><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="45.371216"
|
||||||
|
y="94.793465"
|
||||||
|
id="tspan13">IO</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
x="110.51713"
|
||||||
|
y="97.619591"
|
||||||
|
id="text1-1"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="110.51713"
|
||||||
|
y="97.619591"
|
||||||
|
id="tspan14">Copapy framework &</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none"
|
||||||
|
x="110.39734"
|
||||||
|
y="102.7154"
|
||||||
|
id="text1-1-2"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
|
||||||
|
x="110.39734"
|
||||||
|
y="102.7154"
|
||||||
|
id="tspan14-8">copy & patch</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:4.23333px;line-height:0;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.386001;stroke-linecap:square;stroke-miterlimit:2.5;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="110.39734"
|
||||||
|
y="107.8417"
|
||||||
|
id="text15"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan15"
|
||||||
|
style="font-size:4.23333px;stroke-width:0.386"
|
||||||
|
x="110.39734"
|
||||||
|
y="107.8417">cross compiler</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -13,7 +13,6 @@ classifiers = [
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pelfy>=1.0.7"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|
@ -34,13 +33,15 @@ copapy = ["obj/*.o", "py.typed"]
|
||||||
dev = [
|
dev = [
|
||||||
"ruff",
|
"ruff",
|
||||||
"mypy",
|
"mypy",
|
||||||
"pytest"
|
"pytest",
|
||||||
|
"pelfy>=1.0.7"
|
||||||
]
|
]
|
||||||
doc_build = [
|
doc_build = [
|
||||||
"sphinx",
|
"sphinx",
|
||||||
"pydata_sphinx_theme",
|
"pydata_sphinx_theme",
|
||||||
"sphinx-autodoc-typehints",
|
"sphinx-autodoc-typehints",
|
||||||
"myst-parser"
|
"myst-parser",
|
||||||
|
"pelfy>=1.0.7"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@ class Node:
|
||||||
return hash(self.name) ^ hash(frozenset(a.source.node_hash for a in self.args))
|
return hash(self.name) ^ hash(frozenset(a.source.node_hash for a in self.args))
|
||||||
return hash(self.name) ^ hash(tuple(a.source.node_hash for a in self.args))
|
return hash(self.name) ^ hash(tuple(a.source.node_hash for a in self.args))
|
||||||
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return self.node_hash
|
return self.node_hash
|
||||||
|
|
||||||
|
|
@ -77,6 +76,7 @@ class Net:
|
||||||
def __init__(self, dtype: str, source: Node):
|
def __init__(self, dtype: str, source: Node):
|
||||||
self.dtype = dtype
|
self.dtype = dtype
|
||||||
self.source = source
|
self.source = source
|
||||||
|
self.volatile = False
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
names = get_var_name(self)
|
names = get_var_name(self)
|
||||||
|
|
@ -93,7 +93,7 @@ class value(Generic[TNum], Net):
|
||||||
Attributes:
|
Attributes:
|
||||||
dtype (str): Data type of this value.
|
dtype (str): Data type of this value.
|
||||||
"""
|
"""
|
||||||
def __init__(self, source: TNum | Node, dtype: str | None = None):
|
def __init__(self, source: TNum | Node, dtype: str | None = None, volatile: bool = True):
|
||||||
"""Instance a value.
|
"""Instance a value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -113,6 +113,7 @@ class value(Generic[TNum], Net):
|
||||||
else:
|
else:
|
||||||
self.source = CPConstant(source)
|
self.source = CPConstant(source)
|
||||||
self.dtype = 'int'
|
self.dtype = 'int'
|
||||||
|
self.volatile = volatile
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __add__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
def __add__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
||||||
|
|
@ -220,33 +221,33 @@ class value(Generic[TNum], Net):
|
||||||
return add_op('floordiv', [other, self])
|
return add_op('floordiv', [other, self])
|
||||||
|
|
||||||
def __neg__(self: TCPNum) -> TCPNum:
|
def __neg__(self: TCPNum) -> TCPNum:
|
||||||
if self.dtype == 'int':
|
if self.dtype == 'float':
|
||||||
return cast(TCPNum, add_op('sub', [value(0), self]))
|
return cast(TCPNum, add_op('sub', [value(0.0, volatile=False), self]))
|
||||||
return cast(TCPNum, add_op('sub', [value(0.0), self]))
|
return cast(TCPNum, add_op('sub', [value(0, volatile=False), self]))
|
||||||
|
|
||||||
def __gt__(self, other: TVarNumb) -> 'value[int]':
|
def __gt__(self, other: TVarNumb) -> 'value[int]':
|
||||||
ret = add_op('gt', [self, other])
|
ret = add_op('gt', [self, other])
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
def __lt__(self, other: TVarNumb) -> 'value[int]':
|
def __lt__(self, other: TVarNumb) -> 'value[int]':
|
||||||
ret = add_op('gt', [other, self])
|
ret = add_op('gt', [other, self])
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
def __ge__(self, other: TVarNumb) -> 'value[int]':
|
def __ge__(self, other: TVarNumb) -> 'value[int]':
|
||||||
ret = add_op('ge', [self, other])
|
ret = add_op('ge', [self, other])
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
def __le__(self, other: TVarNumb) -> 'value[int]':
|
def __le__(self, other: TVarNumb) -> 'value[int]':
|
||||||
ret = add_op('ge', [other, self])
|
ret = add_op('ge', [other, self])
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
def __eq__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
def __eq__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
||||||
ret = add_op('eq', [self, other], True)
|
ret = add_op('eq', [self, other], True)
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
def __ne__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
def __ne__(self, other: TVarNumb) -> 'value[int]': # type: ignore
|
||||||
ret = add_op('ne', [self, other], True)
|
ret = add_op('ne', [self, other], True)
|
||||||
return value(ret.source, dtype='bool')
|
return value(ret.source, dtype='bool', volatile=False)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __mod__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
def __mod__(self: 'value[TNum]', other: 'value[TNum] | TNum') -> 'value[TNum]': ...
|
||||||
|
|
@ -358,7 +359,7 @@ class Op(Node):
|
||||||
|
|
||||||
def net_from_value(val: Any) -> value[Any]:
|
def net_from_value(val: Any) -> value[Any]:
|
||||||
vi = CPConstant(val)
|
vi = CPConstant(val)
|
||||||
return value(vi, vi.dtype)
|
return value(vi, vi.dtype, False)
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,17 @@ def get_aux_func_layout(function_names: Iterable[str], sdb: stencil_database, of
|
||||||
return section_list, function_lookup, offset
|
return section_list, function_lookup, offset
|
||||||
|
|
||||||
|
|
||||||
|
def get_dag_stats(node_list: Iterable[Node | Net]) -> dict[str, int]:
|
||||||
|
edges = get_all_dag_edges(n.source if isinstance(n, Net) else n for n in node_list)
|
||||||
|
ops = {node for node, _ in edges}
|
||||||
|
|
||||||
|
op_stat: dict[str, int] = {}
|
||||||
|
for op in ops:
|
||||||
|
op_stat[op.name] = op_stat.get(op.name, 0) + 1
|
||||||
|
|
||||||
|
return op_stat
|
||||||
|
|
||||||
|
|
||||||
def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
||||||
"""Compiles a DAG identified by provided end nodes to binary code
|
"""Compiles a DAG identified by provided end nodes to binary code
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(a + b for a, b in zip(row1, row2))
|
tuple(a + b for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(a + other for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(a + other for a in row)
|
tuple(a + o if isinstance(a, value) else a + other for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -106,8 +112,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(a - b for a, b in zip(row1, row2))
|
tuple(a - b for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(a - other for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(a - other for a in row)
|
tuple(a - o if isinstance(a, value) else a - other for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,8 +135,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(b - a for a, b in zip(row1, row2))
|
tuple(b - a for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(other - a for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(other - a for a in row)
|
tuple(o - a if isinstance(a, value) else other - a for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -145,8 +163,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(a * b for a, b in zip(row1, row2))
|
tuple(a * b for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(a * other for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(a * other for a in row)
|
tuple(a * o if isinstance(a, value) else a * other for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -166,8 +190,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(a / b for a, b in zip(row1, row2))
|
tuple(a / b for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(a / other for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(a / other for a in row)
|
tuple(a / o if isinstance(a, value) else a / other for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -179,8 +209,14 @@ class matrix(Generic[TNum]):
|
||||||
tuple(b / a for a, b in zip(row1, row2))
|
tuple(b / a for a, b in zip(row1, row2))
|
||||||
for row1, row2 in zip(self.values, other.values)
|
for row1, row2 in zip(self.values, other.values)
|
||||||
)
|
)
|
||||||
|
if isinstance(other, value):
|
||||||
|
return matrix(
|
||||||
|
tuple(other / a for a in row)
|
||||||
|
for row in self.values
|
||||||
|
)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(other / a for a in row)
|
tuple(o / a if isinstance(a, value) else other / a for a in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -269,7 +305,7 @@ class matrix(Generic[TNum]):
|
||||||
"""Convert all elements to copapy values if any element is a copapy value."""
|
"""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):
|
if any(isinstance(val, value) for row in self.values for val in row):
|
||||||
return matrix(
|
return matrix(
|
||||||
tuple(value(val) if not isinstance(val, value) else val for val in row)
|
tuple(value(val, volatile=False) if not isinstance(val, value) else val for val in row)
|
||||||
for row in self.values
|
for row in self.values
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,6 @@ def mixed_sum(scalars: Iterable[int | float | value[Any]]) -> Any:
|
||||||
|
|
||||||
def mixed_homogenize(scalars: Iterable[T | value[T]]) -> Iterable[T] | Iterable[value[T]]:
|
def mixed_homogenize(scalars: Iterable[T | value[T]]) -> Iterable[T] | Iterable[value[T]]:
|
||||||
if any(isinstance(val, value) for val in scalars):
|
if any(isinstance(val, value) for val in scalars):
|
||||||
return (value(val) if not isinstance(val, value) else val for val in scalars)
|
return (value(val, volatile=False) if not isinstance(val, value) else val for val in scalars)
|
||||||
else:
|
else:
|
||||||
return (val for val in scalars if not isinstance(val, value))
|
return (val for val in scalars if not isinstance(val, value))
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pelfy import open_elf_file, elf_file, elf_symbol
|
from typing import Generator, Literal, Iterable, TYPE_CHECKING
|
||||||
from typing import Generator, Literal, Iterable
|
|
||||||
import pelfy
|
|
||||||
import struct
|
import struct
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import pelfy
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
from ._vendor import pelfy
|
||||||
|
except ImportError:
|
||||||
|
import pelfy
|
||||||
|
|
||||||
|
|
||||||
ByteOrder = Literal['little', 'big']
|
ByteOrder = Literal['little', 'big']
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -62,7 +69,7 @@ def detect_process_arch() -> str:
|
||||||
return arch_family
|
return arch_family
|
||||||
|
|
||||||
|
|
||||||
def get_return_function_type(symbol: elf_symbol) -> str:
|
def get_return_function_type(symbol: pelfy.elf_symbol) -> str:
|
||||||
if symbol.relocations:
|
if symbol.relocations:
|
||||||
for reloc in reversed(symbol.relocations):
|
for reloc in reversed(symbol.relocations):
|
||||||
func_name = reloc.symbol.name
|
func_name = reloc.symbol.name
|
||||||
|
|
@ -71,7 +78,7 @@ def get_return_function_type(symbol: elf_symbol) -> str:
|
||||||
return 'void'
|
return 'void'
|
||||||
|
|
||||||
|
|
||||||
def get_stencil_position(func: elf_symbol) -> tuple[int, int]:
|
def get_stencil_position(func: pelfy.elf_symbol) -> tuple[int, int]:
|
||||||
start_index = 0 # There must be no prolog
|
start_index = 0 # There must be no prolog
|
||||||
# Find last relocation in function
|
# Find last relocation in function
|
||||||
last_instr = get_last_call_in_function(func)
|
last_instr = get_last_call_in_function(func)
|
||||||
|
|
@ -84,7 +91,7 @@ def get_stencil_position(func: elf_symbol) -> tuple[int, int]:
|
||||||
return start_index, end_index
|
return start_index, end_index
|
||||||
|
|
||||||
|
|
||||||
def get_last_call_in_function(func: elf_symbol) -> int:
|
def get_last_call_in_function(func: pelfy.elf_symbol) -> int:
|
||||||
# Find last relocation in function
|
# Find last relocation in function
|
||||||
assert func.relocations, f'No call function in stencil function {func.name}.'
|
assert func.relocations, f'No call function in stencil function {func.name}.'
|
||||||
reloc = func.relocations[-1]
|
reloc = func.relocations[-1]
|
||||||
|
|
@ -95,7 +102,7 @@ def get_last_call_in_function(func: elf_symbol) -> int:
|
||||||
return reloc.fields['r_offset'] - func.fields['st_value'] + address_field_length - instruction_lengths
|
return reloc.fields['r_offset'] - func.fields['st_value'] + address_field_length - instruction_lengths
|
||||||
|
|
||||||
|
|
||||||
def get_op_after_last_call_in_function(func: elf_symbol) -> int:
|
def get_op_after_last_call_in_function(func: pelfy.elf_symbol) -> int:
|
||||||
# Find last relocation in function
|
# Find last relocation in function
|
||||||
assert func.relocations, f'No call function in stencil function {func.name}.'
|
assert func.relocations, f'No call function in stencil function {func.name}.'
|
||||||
reloc = func.relocations[-1]
|
reloc = func.relocations[-1]
|
||||||
|
|
@ -120,9 +127,9 @@ class stencil_database():
|
||||||
obj_file: path to the ELF object file or bytes of the ELF object file
|
obj_file: path to the ELF object file or bytes of the ELF object file
|
||||||
"""
|
"""
|
||||||
if isinstance(obj_file, str):
|
if isinstance(obj_file, str):
|
||||||
self.elf = open_elf_file(obj_file)
|
self.elf = pelfy.open_elf_file(obj_file)
|
||||||
else:
|
else:
|
||||||
self.elf = elf_file(obj_file)
|
self.elf = pelfy.elf_file(obj_file)
|
||||||
|
|
||||||
self.stencil_definitions = {s.name: get_return_function_type(s)
|
self.stencil_definitions = {s.name: get_return_function_type(s)
|
||||||
for s in self.elf.symbols
|
for s in self.elf.symbols
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from . import value
|
from . import value
|
||||||
from ._mixed import mixed_sum, mixed_homogenize
|
from ._mixed import mixed_sum, mixed_homogenize
|
||||||
from typing import TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
|
from typing import Sequence, TypeVar, Iterable, Any, overload, TypeAlias, Callable, Iterator, Generic
|
||||||
import copapy as cp
|
import copapy as cp
|
||||||
from ._helper_types import TNum
|
from ._helper_types import TNum
|
||||||
|
|
||||||
|
|
@ -57,7 +57,10 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a + b for a, b in zip(self.values, other.values))
|
return vector(a + b for a, b in zip(self.values, other.values))
|
||||||
return vector(a + other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a + other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a + o if isinstance(a, value) else a + other for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __radd__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
def __radd__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||||
|
|
@ -80,7 +83,10 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a - b for a, b in zip(self.values, other.values))
|
return vector(a - b for a, b in zip(self.values, other.values))
|
||||||
return vector(a - other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a - other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a - o if isinstance(a, value) else a - other for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __rsub__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
def __rsub__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||||
|
|
@ -92,7 +98,10 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(b - a for a, b in zip(self.values, other.values))
|
return vector(b - a for a, b in zip(self.values, other.values))
|
||||||
return vector(other - a for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(other - a for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(o - a if isinstance(a, value) else other - a for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __mul__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
def __mul__(self: 'vector[int]', other: VecFloatLike) -> 'vector[float]': ...
|
||||||
|
|
@ -106,7 +115,10 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a * b for a, b in zip(self.values, other.values))
|
return vector(a * b for a, b in zip(self.values, other.values))
|
||||||
return vector(a * other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a * other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a * o if isinstance(a, value) else a * other for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __rmul__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
def __rmul__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||||
|
|
@ -129,7 +141,10 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a ** b for a, b in zip(self.values, other.values))
|
return vector(a ** b for a, b in zip(self.values, other.values))
|
||||||
return vector(a ** other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a ** other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a ** o if isinstance(a, value) else a ** other for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __rpow__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
def __rpow__(self: 'vector[float]', other: VecNumLike) -> 'vector[float]': ...
|
||||||
|
|
@ -138,19 +153,31 @@ class vector(Generic[TNum]):
|
||||||
@overload
|
@overload
|
||||||
def __rpow__(self, other: VecNumLike) -> 'vector[Any]': ...
|
def __rpow__(self, other: VecNumLike) -> 'vector[Any]': ...
|
||||||
def __rpow__(self, other: VecNumLike) -> Any:
|
def __rpow__(self, other: VecNumLike) -> Any:
|
||||||
return self ** other
|
if isinstance(other, vector):
|
||||||
|
assert len(self.values) == len(other.values)
|
||||||
|
return vector(b ** a for a, b in zip(self.values, other.values))
|
||||||
|
if isinstance(other, value):
|
||||||
|
return vector(other ** a for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(o ** a if isinstance(a, value) else other ** a for a in self.values)
|
||||||
|
|
||||||
def __truediv__(self, other: VecNumLike) -> 'vector[float]':
|
def __truediv__(self, other: VecNumLike) -> 'vector[float]':
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a / b for a, b in zip(self.values, other.values))
|
return vector(a / b for a, b in zip(self.values, other.values))
|
||||||
return vector(a / other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a / other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a / o if isinstance(a, value) else a / other for a in self.values)
|
||||||
|
|
||||||
def __rtruediv__(self, other: VecNumLike) -> 'vector[float]':
|
def __rtruediv__(self, other: VecNumLike) -> 'vector[float]':
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(b / a for a, b in zip(self.values, other.values))
|
return vector(b / a for a, b in zip(self.values, other.values))
|
||||||
return vector(other / a for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(other / a for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(o / a if isinstance(a, value) else other / a for a in self.values)
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def dot(self: 'vector[int]', other: 'vector[int]') -> int | value[int]: ...
|
def dot(self: 'vector[int]', other: 'vector[int]') -> int | value[int]: ...
|
||||||
|
|
@ -191,37 +218,55 @@ class vector(Generic[TNum]):
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a > b for a, b in zip(self.values, other.values))
|
return vector(a > b for a, b in zip(self.values, other.values))
|
||||||
return vector(a > other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a > other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a > o if isinstance(a, value) else a > other for a in self.values)
|
||||||
|
|
||||||
def __lt__(self, other: VecNumLike) -> 'vector[int]':
|
def __lt__(self, other: VecNumLike) -> 'vector[int]':
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a < b for a, b in zip(self.values, other.values))
|
return vector(a < b for a, b in zip(self.values, other.values))
|
||||||
return vector(a < other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a < other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a < o if isinstance(a, value) else a < other for a in self.values)
|
||||||
|
|
||||||
def __ge__(self, other: VecNumLike) -> 'vector[int]':
|
def __ge__(self, other: VecNumLike) -> 'vector[int]':
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a >= b for a, b in zip(self.values, other.values))
|
return vector(a >= b for a, b in zip(self.values, other.values))
|
||||||
return vector(a >= other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a >= other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a >= o if isinstance(a, value) else a >= other for a in self.values)
|
||||||
|
|
||||||
def __le__(self, other: VecNumLike) -> 'vector[int]':
|
def __le__(self, other: VecNumLike) -> 'vector[int]':
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a <= b for a, b in zip(self.values, other.values))
|
return vector(a <= b for a, b in zip(self.values, other.values))
|
||||||
return vector(a <= other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a <= other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a <= o if isinstance(a, value) else a <= other for a in self.values)
|
||||||
|
|
||||||
def __eq__(self, other: VecNumLike) -> 'vector[int]': # type: ignore
|
def __eq__(self, other: VecNumLike | Sequence[int | float]) -> 'vector[int]': # type: ignore
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector | Sequence):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self) == len(other)
|
||||||
return vector(a == b for a, b in zip(self.values, other.values))
|
return vector(a == b for a, b in zip(self.values, other))
|
||||||
return vector(a == other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a == other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a == o if isinstance(a, value) else a == other for a in self.values)
|
||||||
|
|
||||||
def __ne__(self, other: VecNumLike) -> 'vector[int]': # type: ignore
|
def __ne__(self, other: VecNumLike) -> 'vector[int]': # type: ignore
|
||||||
if isinstance(other, vector):
|
if isinstance(other, vector):
|
||||||
assert len(self.values) == len(other.values)
|
assert len(self.values) == len(other.values)
|
||||||
return vector(a != b for a, b in zip(self.values, other.values))
|
return vector(a != b for a, b in zip(self.values, other.values))
|
||||||
return vector(a != other for a in self.values)
|
if isinstance(other, value):
|
||||||
|
return vector(a != other for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(a != o if isinstance(a, value) else a != other for a in self.values)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shape(self) -> tuple[int]:
|
def shape(self) -> tuple[int]:
|
||||||
|
|
@ -255,6 +300,15 @@ class vector(Generic[TNum]):
|
||||||
def map(self, func: Callable[[Any], value[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."""
|
"""Applies a function to each element of the vector and returns a new vector."""
|
||||||
return vector(func(x) for x in self.values)
|
return vector(func(x) for x in self.values)
|
||||||
|
|
||||||
|
def _map2(self, other: VecNumLike, func: Callable[[Any, Any], value[int] | value[float]]) -> 'vector[Any]':
|
||||||
|
if isinstance(other, vector):
|
||||||
|
assert len(self.values) == len(other.values)
|
||||||
|
return vector(func(a, b) for a, b in zip(self.values, other.values))
|
||||||
|
if isinstance(other, value):
|
||||||
|
return vector(func(a, other) for a in self.values)
|
||||||
|
o = value(other, volatile=False) # Make sure a single constant is allocated
|
||||||
|
return vector(func(a, o) if isinstance(a, value) else a + other for a in self.values)
|
||||||
|
|
||||||
|
|
||||||
def cross_product(v1: vector[float], v2: vector[float]) -> vector[float]:
|
def cross_product(v1: vector[float], v2: vector[float]) -> vector[float]:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from ._target import add_read_command
|
||||||
from ._basic_types import Net, Op, Node, CPConstant, Write, stencil_db_from_package
|
from ._basic_types import Net, Op, Node, CPConstant, Write, stencil_db_from_package
|
||||||
from ._compiler import compile_to_dag, \
|
from ._compiler import compile_to_dag, \
|
||||||
stable_toposort, get_const_nets, get_all_dag_edges, add_read_ops, get_all_dag_edges_between, \
|
stable_toposort, get_const_nets, get_all_dag_edges, add_read_ops, get_all_dag_edges_between, \
|
||||||
add_write_ops
|
add_write_ops, get_dag_stats
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"add_read_command",
|
"add_read_command",
|
||||||
|
|
@ -18,5 +18,6 @@ __all__ = [
|
||||||
"get_all_dag_edges_between",
|
"get_all_dag_edges_between",
|
||||||
"add_read_ops",
|
"add_read_ops",
|
||||||
"add_write_ops",
|
"add_write_ops",
|
||||||
"stencil_db_from_package"
|
"stencil_db_from_package",
|
||||||
|
"get_dag_stats"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,6 @@ def cp_vs_python_sparse(path: str = 'benchmark_results_001_sparse.json'):
|
||||||
results.append({'benchmark': 'Copapy', 'iter_size': iter_size, 'elapsed_time': elapsed_cp, 'sum_size': sum_size, 'v_size': v_size})
|
results.append({'benchmark': 'Copapy', 'iter_size': iter_size, 'elapsed_time': elapsed_cp, 'sum_size': sum_size, 'v_size': v_size})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
v1 = cp.vector(float(v) for v in range(v_size))
|
v1 = cp.vector(float(v) for v in range(v_size))
|
||||||
v2 = cp.vector(float(v) for v in [5]*v_size)
|
v2 = cp.vector(float(v) for v in [5]*v_size)
|
||||||
|
|
||||||
|
|
@ -158,6 +157,7 @@ def plot_results(path: str):
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import matplotlib as mpl
|
||||||
|
|
||||||
# Load the benchmark results
|
# Load the benchmark results
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r') as f:
|
||||||
|
|
@ -185,30 +185,97 @@ def plot_results(path: str):
|
||||||
v_sizes_set = sorted(set(v for benchmark_data in medians_by_benchmark.values() for v in benchmark_data.keys()))
|
v_sizes_set = sorted(set(v for benchmark_data in medians_by_benchmark.values() for v in benchmark_data.keys()))
|
||||||
|
|
||||||
# Create the plot
|
# Create the plot
|
||||||
plt.figure(figsize=(10, 6))
|
plt.figure(figsize=(6, 4))
|
||||||
|
|
||||||
for benchmark in benchmarks:
|
for benchmark in benchmarks:
|
||||||
if benchmark != 'Python':
|
if benchmark != 'Python':
|
||||||
v_sizes = sorted(medians_by_benchmark[benchmark].keys())
|
v_sizes = sorted(medians_by_benchmark[benchmark].keys())
|
||||||
elapsed_times = [medians_by_benchmark[benchmark][v] for v in v_sizes]
|
elapsed_times = [medians_by_benchmark[benchmark][v] for v in v_sizes]
|
||||||
plt.plot(v_sizes, elapsed_times, '.', label=benchmark)
|
plt.plot(v_sizes, elapsed_times, '.', label=benchmark, markersize=10)
|
||||||
|
|
||||||
plt.xlabel('Vector Size (v_size)')
|
plt.xlabel('Vector Size (v_size)')
|
||||||
plt.ylabel('Elapsed Time (seconds)')
|
plt.ylabel('Elapsed Time (seconds)')
|
||||||
#plt.title('Benchmark Results: Elapsed Time vs Vector Size')
|
#plt.title('Benchmark Results: Elapsed Time vs Vector Size')
|
||||||
plt.legend()
|
plt.legend(frameon=False)
|
||||||
#plt.grid(True, alpha=0.3)
|
#plt.grid(True, alpha=0.3)
|
||||||
plt.ylim(bottom=0)
|
plt.ylim(bottom=0)
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
|
|
||||||
# Save to PNG
|
# Save to PNG
|
||||||
plt.savefig(path.replace('.json', '') + '.png', dpi=300)
|
mpl.rcParams['svg.fonttype'] = 'none'
|
||||||
|
save_svg_with_theme_styles(plt, path.replace('.json', '') + '.svg')
|
||||||
print("Plot saved")
|
print("Plot saved")
|
||||||
|
|
||||||
|
|
||||||
|
def save_svg_with_theme_styles(pyplot_obj, path):
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
"""
|
||||||
|
Takes a pyplot object (typically `plt`) or a figure, captures its SVG output,
|
||||||
|
injects theme-based CSS, and writes to disk.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# --- Step 1: Capture SVG to memory ---
|
||||||
|
buf = io.StringIO()
|
||||||
|
|
||||||
|
# pyplot_obj can be a module (plt) or a Figure instance
|
||||||
|
if hasattr(pyplot_obj, "gcf"):
|
||||||
|
fig = pyplot_obj.gcf()
|
||||||
|
else:
|
||||||
|
fig = pyplot_obj
|
||||||
|
|
||||||
|
fig.savefig(buf, format="svg", dpi=150, transparent=True)
|
||||||
|
svg_data = buf.getvalue()
|
||||||
|
buf.close()
|
||||||
|
|
||||||
|
# --- Step 2: Theme CSS to inject ---
|
||||||
|
theme_css = """
|
||||||
|
<style type="text/css">
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path {
|
||||||
|
stroke: #EEEEEE !important;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
fill: #EEEEEE !important;
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
fill: #444444 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
path {
|
||||||
|
stroke: #444444 !important;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
fill: #444444 !important;
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
fill: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#patch_1 path {
|
||||||
|
stroke: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# --- Step 3: Inject CSS right after <svg ...> tag ---
|
||||||
|
# Find the first > after the <svg ...> opening tag
|
||||||
|
modified_svg = re.sub(
|
||||||
|
r"(<svg[^>]*>)",
|
||||||
|
r"\1\n" + theme_css,
|
||||||
|
svg_data,
|
||||||
|
count=1
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Step 4: Write final output to disk ---
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(modified_svg)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
path1 = 'benchmark_results_001.json'
|
path1 = 'docs/source/media/benchmark_results_001.json'
|
||||||
path2 = 'benchmark_results_001_sparse.json'
|
path2 = 'docs/source/media/benchmark_results_001_sparse.json'
|
||||||
|
|
||||||
if 'no_simd' in sys.argv[1:]:
|
if 'no_simd' in sys.argv[1:]:
|
||||||
os.environ["NPY_DISABLE_CPU_FEATURES"] = CPU_SIMD_FEATURES
|
os.environ["NPY_DISABLE_CPU_FEATURES"] = CPU_SIMD_FEATURES
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import copapy as cp
|
||||||
|
from copapy._basic_types import value
|
||||||
|
from copapy.backend import get_dag_stats
|
||||||
|
|
||||||
|
def test_get_dag_stats():
|
||||||
|
|
||||||
|
sum_size = 10
|
||||||
|
v_size = 200
|
||||||
|
|
||||||
|
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 + 7) @ v2 for i in range(sum_size))
|
||||||
|
|
||||||
|
assert isinstance(v3, value)
|
||||||
|
stat = get_dag_stats([v3])
|
||||||
|
print(stat)
|
||||||
|
|
||||||
|
assert stat['const_float'] == 2 * v_size
|
||||||
|
assert stat['add_float_float'] == sum_size * v_size - 2
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_get_dag_stats()
|
||||||
|
|
@ -103,8 +103,8 @@ def test_matrix_scalar_division():
|
||||||
m1 = cp.matrix([[6.0, 8.0], [12.0, 16.0]])
|
m1 = cp.matrix([[6.0, 8.0], [12.0, 16.0]])
|
||||||
m2 = m1 / 2.0
|
m2 = m1 / 2.0
|
||||||
|
|
||||||
assert m2[0] == pytest.approx((3.0, 4.0)) # pyright: ignore[reportUnknownMemberType]
|
assert list(m2[0]) == pytest.approx((3.0, 4.0)) # pyright: ignore[reportUnknownMemberType]
|
||||||
assert m2[1] == pytest.approx((6.0, 8.0)) # pyright: ignore[reportUnknownMemberType]
|
assert list(m2[1]) == pytest.approx((6.0, 8.0)) # pyright: ignore[reportUnknownMemberType]
|
||||||
|
|
||||||
|
|
||||||
def test_matrix_vector_multiplication():
|
def test_matrix_vector_multiplication():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue