{"id":18816304,"url":"https://github.com/igorsusmelj/rustynum","last_synced_at":"2025-04-13T22:21:50.183Z","repository":{"id":239063613,"uuid":"798281481","full_name":"IgorSusmelj/rustynum","owner":"IgorSusmelj","description":"RustyNum: A NumPy Alternative powered by Rust’s Portable SIMD","archived":false,"fork":false,"pushed_at":"2025-04-05T17:36:40.000Z","size":1014,"stargazers_count":37,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-05T18:28:41.332Z","etag":null,"topics":["numerical-methods","python","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IgorSusmelj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-09T13:14:54.000Z","updated_at":"2025-04-05T17:36:44.000Z","dependencies_parsed_at":"2024-05-21T21:26:01.257Z","dependency_job_id":"b98901e3-4991-40d0-aeaf-e17e1c238869","html_url":"https://github.com/IgorSusmelj/rustynum","commit_stats":null,"previous_names":["igorsusmelj/rustynum"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IgorSusmelj%2Frustynum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IgorSusmelj%2Frustynum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IgorSusmelj%2Frustynum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IgorSusmelj%2Frustynum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IgorSusmelj","download_url":"https://codeload.github.com/IgorSusmelj/rustynum/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248789756,"owners_count":21161888,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["numerical-methods","python","rust"],"created_at":"2024-11-07T23:54:28.060Z","updated_at":"2025-04-13T22:21:50.166Z","avatar_url":"https://github.com/IgorSusmelj.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"![RustyNum Banner](docs/assets/rustynum-banner.png?raw=true \"RustyNum\")\n\n[![PyPI python](https://img.shields.io/pypi/pyversions/rustynum)](https://pypi.org/project/rustynum)\n![PyPI Version](https://badge.fury.io/py/rustynum.svg)\n![License](https://img.shields.io/badge/license-MIT-blue.svg)\n![Test Python Bindings](https://github.com/IgorSusmelj/rustynum/actions/workflows/test_python_bindings.yml/badge.svg)\n[![Documentation](https://img.shields.io/badge/docs-rustynum.com-blue)](https://rustynum.com)\n\n---\n\n⚠️ **Disclaimer:** _RustyNum is currently a work in progress and is not recommended for production use. Features may be unstable and subject to change._\n\n# RustyNum\n\nRustyNum is a high-performance numerical computation library written in Rust, created to demonstrate the potential of Rust's SIMD (Single Instruction, Multiple Data) capabilities using the nightly `portable_simd` feature, and serving as a fast alternative to Numpy.\n\n## Key Features\n\n- **High Performance:** Utilizes Rust's `portable_simd` for accelerated numerical operations across various hardware platforms, achieving up to 2.86x faster computations for certain operations compared to Numpy.\n- **Python Bindings:** Seamless integration with Python, providing a familiar Numpy-like interface.\n- **Lightweight:** Minimal dependencies (no external crates are used), ensuring a small footprint and easy deployment. RustyNum Python wheels are only 300kBytes (50x smaller than Numpy wheels).\n\n## Installation\n\nSupported Python versions: `3.8`, `3.9`, `3.10`, `3.11`, `3.12`, `3.13`\n\nSupported operating systems: `Windows x86`, `Linux x86`, `MacOS x86 \u0026 ARM`\n\nFor comprehensive documentation, tutorials, and API reference, visit [rustynum.com](https://rustynum.com).\n\n### For Python\n\nYou can install RustyNum directly from PyPI:\n\n```bash\npip install rustynum\n```\n\n\u003e If that does not work for you please create an issue with the operating system and Python version you're using!\n\n## Quick Start Guide (Python)\n\nIf you're familiar with Numpy, you'll quickly get used to RustyNum!\n\n```Python\nimport numpy as np\nimport rustynum as rnp\n\n# Using Numpy\na = np.array([1.0, 2.0, 3.0, 4.0], dtype=\"float32\")\na = a + 2\nprint(a.mean())  # 4.5\n\n# Using RustyNum\nb = rnp.NumArray([1.0, 2.0, 3.0, 4.0], dtype=\"float32\")\nb = b + 2\nprint(b.mean().item())  # 4.5\n```\n\n### Advanced Usage\n\nYou can perform advanced operations such as matrix-vector and matrix-matrix multiplications:\n\n```Python\n# Matrix-vector dot product using Numpy\nimport numpy as np\nimport rustynum as rnp\n\na = np.random.rand(4 * 4).astype(np.float32)\nb = np.random.rand(4).astype(np.float32)\nresult_numpy = np.dot(a.reshape((4, 4)), b)\n\n# Matrix-vector dot product using RustyNum\na_rnp = rnp.NumArray(a.tolist())\nb_rnp = rnp.NumArray(b.tolist())\nresult_rust = a_rnp.reshape([4, 4]).dot(b_rnp).tolist()\n\nprint(result_numpy)  # Example Output: [0.8383043, 1.678406, 1.4153088, 0.7959367]\nprint(result_rust)   # Example Output: [0.8383043, 1.678406, 1.4153088, 0.7959367]\n```\n\n## Features\n\nRustyNum offers a variety of numerical operations and data types, with more features planned for the future.\n\n### Supported Data Types\n\n- float64\n- float32\n- uint8 (experimental)\n- int32 (Planned)\n- int64 (Planned)\n\n### Supported Operations\n\n| Operation        | NumPy Equivalent                | RustyNum Equivalent              |\n| ---------------- | ------------------------------- | -------------------------------- |\n| Zeros Array      | `np.zeros((2, 3))`              | `rnp.zeros((2, 3))`              |\n| Ones Array       | `np.ones((2, 3))`               | `rnp.ones((2, 3))`               |\n| Arange           | `np.arange(start, stop, step)`  | `rnp.arange(start, stop, step)`  |\n| Linspace         | `np.linspace(start, stop, num)` | `rnp.linspace(start, stop, num)` |\n| Mean             | `np.mean(a)`                    | `rnp.mean(a)`                    |\n| Median           | `np.median(a)`                  | `rnp.median(a)`                  |\n| Min              | `np.min(a)`                     | `rnp.min(a)`                     |\n| Max              | `np.max(a)`                     | `rnp.max(a)`                     |\n| Exp              | `np.exp(a)`                     | `rnp.exp(a)`                     |\n| Log              | `np.log(a)`                     | `rnp.log(a)`                     |\n| Sigmoid          | `1 / (1 + np.exp(-a))`          | `rnp.sigmoid(a)`                 |\n| Dot Product      | `np.dot(a, b)`                  | `rnp.dot(a, b)`                  |\n| Reshape          | `a.reshape((2, 3))`             | `a.reshape([2, 3])`              |\n| Concatenate      | `np.concatenate([a,b], axis=0)` | `rnp.concatenate([a,b], axis=0)` |\n| Element-wise Add | `a + b`                         | `a + b`                          |\n| Element-wise Sub | `a - b`                         | `a - b`                          |\n| Element-wise Mul | `a * b`                         | `a * b`                          |\n| Element-wise Div | `a / b`                         | `a / b`                          |\n| Fancy indexing   | `np.ones((2,3))[0, :]`          | `rnp.ones((2,3))[0, :]`          |\n| Fancy flipping   | `np.array([1,2,3])[::-1]`       | `rnp.array([1,2,3])[::-1]`       |\n\n### NumArray Class\n\nInitialization\n\n```Python\nfrom rustynum import NumArray\n\n# From a list\na = NumArray([1.0, 2.0, 3.0], dtype=\"float32\")\n\n# From another NumArray\nb = NumArray(a)\n\n# From nested lists (2D array)\nc = NumArray([[1.0, 2.0], [3.0, 4.0]], dtype=\"float64\")\n```\n\nMethods\n\n`reshape(shape: List[int]) -\u003e NumArray`\n\nReshapes the array to the specified shape.\n\n```Python\nreshaped = a.reshape([3, 1])\n```\n\n`matmul(other: NumArray) -\u003e NumArray`\n\nPerforms matrix multiplication with another NumArray.\n\n```Python\nresult = a.matmul(b)\n# or\nresult = a @ b\n```\n\n`dot(other: NumArray) -\u003e NumArray`\n\nComputes the dot product with another NumArray.\n\n```Python\ndot_product = a.dot(b)\n```\n\n`mean(axis: Union[None, int, Sequence[int]] = None) -\u003e Union[NumArray, float]`\n\nComputes the mean along specified axis.\n\n```Python\naverage = a.mean()\naverage_axis0 = a.mean(axis=0)\n```\n\n`median(axis: Union[None, int, Sequence[int]] = None) -\u003e Union[NumArray, float]`\n\nComputes the median along specified axis.\n\n```Python\nmedian = a.median()\nmedian_axis0 = a.median(axis=0)\n```\n\n`min(axis: Union[None, int, Sequence[int]] = None) -\u003e Union[NumArray, float]`\n\nReturns the minimum value in the array.\n\n```Python\nminimum = a.min()\n```\n\n`max(axis: Union[None, int, Sequence[int]] = None) -\u003e Union[NumArray, float]`\n\nReturns the maximum value in the array.\n\n```Python\nmaximum = a.max()\n```\n\n`tolist() -\u003e Union[List[float], List[List[float]]]`\n\nConverts the NumArray to a Python list.\n\n```Python\nlist_representation = a.tolist()\n```\n\n### Multi-Dimensional Arrays\n\n- Matrix-vector dot product\n- Matrix-matrix dot product\n\n## Roadmap\n\nPlanned Features:\n\n- N-dimensional arrays\n  - Useful for filters, image processing, and machine learning\n- Additional operations: argmin, argmax, sort, std, var, zeros, cumsum, interp\n- Integer support\n- Extended shaping and reshaping capabilities\n- C++ and WASM bindings\n\nNot Planned:\n\n- Random number generation (use the rand crate)\n\n## Design Principles\n\nRustyNum is built on four core principles:\n\n1. **No 3rd Party Dependencies:** Ensuring transparency and control over the codebase.\n2. **Leverage Portable SIMD:** Utilizing Rust's nightly SIMD feature for high-performance operations across platforms.\n3. **First-Class Language Bindings:** Providing robust support for Python, with plans for WebAssembly and C++.\n4. **Numpy-like Interface:** Offering a familiar and intuitive user experience for those coming from Python.\n\n## Performance\n\n### Python\n\nRustyNum leverages Rust's `portable_simd` feature to achieve significant performance improvements in numerical computations. On a MacBook Pro M1 Pro, RustyNum outperforms Numpy in several key operations. Below are benchmark results comparing `RustyNum 0.1.4` with `Numpy 1.24.4`:\n\n#### Benchmark Results (float32)\n\n| Operation                   | RustyNum (us)  | Numpy (us)     | Speedup Factor |\n| --------------------------- | -------------- | -------------- | -------------- |\n| Mean (1000 elements)        | 8.8993         | 22.6300        | 2.54x          |\n| Median (1000 elements)      | 23.6040        | 39.8451        | 1.68x          |\n| Min (1000 elements)         | 10.1423        | 28.9693        | 2.86x          |\n| Sigmoid (1000 elems)        | 10.6899        | 23.2486        | 2.17x          |\n| Dot Product (1000 elems)    | 17.0640        | 38.2958        | 2.24x          |\n| Matrix-Vector (1000x1000)   | 10,041.6093    | 24,990.2646    | 2.49x          |\n| Matrix-Vector (10000x10000) | 2,731,092.0332 | 2,103,920.4830 | 0.77x          |\n| Matrix-Matrix (500x500)     | 7,010.6638     | 14,878.9556    | 2.12x          |\n| Matrix-Matrix (2000x2000)   | 225,595.8832   | 257,832.6334   | 1.14x          |\n\n#### Benchmark Results (float64)\n\n| Operation                   | RustyNum (us)  | Numpy (us)     | Speedup Factor |\n| --------------------------- | -------------- | -------------- | -------------- |\n| Mean (1000 elements)        | 9.1026         | 24.0636        | 2.64x          |\n| Median (1000 elements)      | 24.9010        | 38.4760        | 1.54x          |\n| Min (1000 elements)         | 18.2651        | 24.8170        | 1.36x          |\n| Dot Product (1000 elems)    | 16.6583        | 38.8000        | 2.33x          |\n| Matrix-Vector (1000x1000)   | 9,941.3305     | 23,788.9570    | 2.39x          |\n| Matrix-Vector (10000x10000) | 3,635,297.4664 | 4,962,900.9084 | 1.37x          |\n| Matrix-Matrix (500x500)     | 9,683.3815     | 15,866.6376    | 1.64x          |\n| Matrix-Matrix (2000x2000)   | 412,333.8586   | 365,047.5000   | 0.89x          |\n\n#### Observations\n\n- RustyNum significantly outperforms Numpy in basic operations such as mean, median, min, and dot product, with speedup factors up to and over 2x.\n- For larger operations, especially matrix-vector and matrix-matrix multiplications, Numpy currently performs better, which highlights areas for potential optimization in RustyNum.\n\nThese results demonstrate RustyNum's potential for high-performance numerical computations, particularly in operations where SIMD instructions can be fully leveraged.\n\n### Rust\n\nIn addition to the Python bindings, RustyNum’s core library is implemented in Rust. Below is a comparison of RustyNum (rustynum_rs) with two popular Rust numerical libraries: `nalgebra 0.33.0` and `ndarray 0.16.1`. The benchmarks were conducted using the Criterion crate to measure performance across various basic operations.\n\n#### Benchmark Results (float32)\n\n| Input Size                                  | RustyNum  | nalgebra  | ndarray   |\n| ------------------------------------------- | --------- | --------- | --------- |\n| Addition (10k elements)                     | 760.53 ns | 695.73 ns | 664.29 ns |\n| Vector mean (10k elements)                  | 683.83 ns | 14.602 µs | 1.2370 µs |\n| Vector median (10k elements)                | 7.4175 µs | 6.8863 µs | 6.9970 µs |\n| Vector Dot Product (10k elements)           | 758.65 ns | 1.1843 µs | 1.1942 µs |\n| Matrix-Vector Multiplication (1k elements)  | 77.851 us | 403.39 µs | 115.75 µs |\n| Matrix-Matrix Multiplication (500 elements) | 2.5526 ms | 2.9038 ms | 2.7847 ms |\n| Matrix-Matrix Multiplication (1k elements)  | 17.836 ms | 21.895 ms | 22.423 ms |\n\n#### Observations\n\n- RustyNum is able to perform on par with nalgebra and ndarray in most operations and sometimes even outperforms them.\n- There seems a significant overhead in the Python bindings. We're getting 10ms in Python in RustyNum for the matrix-vector multiplication with 1k elements vs 77us in Rust.\n\n# Build\n\n## Rust Crate\n\n### Run tests\n\nRun using\n\n```\n\ncargo test\n\n```\n\n### Create Docs\n\n```\n\ncargo doc --open\n\n```\n\n### Run Benchmarks\n\nRun using\n\n```\n\ncargo bench -- \u003cbenchmark_name\u003e\n\n```\n\n## Python bindings\n\nDon't use maturin. But only setup.py\n\n```\n\ncd bindings/python/ \u0026\u0026 python setup.py install\n\n```\n\nor\n\n```\n\ncd bindings/python/ \u0026\u0026 python setup.py bdist_wheel\n\n```\n\nThen run tests using\n\n```\n\npytest tests\n\n```\n\nor benchmarks using\n\n```\n\npytest benchmarks\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorsusmelj%2Frustynum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorsusmelj%2Frustynum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorsusmelj%2Frustynum/lists"}