Compare commits

...

30 Commits

Author SHA1 Message Date
6596c2df99 typo fix
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 14s
2025-06-19 18:00:29 +00:00
24337cea48 Updated Project urls
Some checks failed
Run Python Tests / test (3.12) (pull_request) Successful in 10s
Run Python Tests / test (3.10) (pull_request) Successful in 14s
Run Python Tests / test (3.8) (pull_request) Successful in 10s
Publish Python Package to PyPI / deploy (push) Failing after 14s
Signed-off-by: Jonathan Rampersad <jonathan@jono-rams.work>
2025-06-19 17:58:50 +00:00
ee18cc9e59 Added Branding (#14)
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 19s
Reviewed-on: #14
2025-06-19 17:54:07 +00:00
ce464cffd4 FEAT: Added support for float coefficients (#13)
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 13s
Reviewed-on: #13
Co-authored-by: Jonathan Rampersad <rampersad.jonathan@gmail.com>
Co-committed-by: Jonathan Rampersad <rampersad.jonathan@gmail.com>
2025-06-18 13:20:18 +00:00
c94d08498d Edited README to advise of function * function multiplication being available (#12)
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 17s
Reviewed-on: #12
2025-06-18 12:55:37 +00:00
3aad9efb61 Merge pull request 'readme-patch' (#11) from readme-patch into main
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 17s
Reviewed-on: #11
2025-06-17 18:37:50 +00:00
32d6cfeeea Update pyproject.toml
All checks were successful
Run Python Tests / test (3.12) (pull_request) Successful in 9s
Run Python Tests / test (3.10) (pull_request) Successful in 13s
Run Python Tests / test (3.8) (pull_request) Successful in 9s
2025-06-17 18:37:33 +00:00
8d6fe7aca0 Update README.md
Signed-off-by: Jonathan Rampersad <jonathan@jono-rams.work>
2025-06-17 18:37:17 +00:00
7927845f17 Merge pull request 'v0.2.0' (#10) from v0.2.0-dev into main
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 13s
Reviewed-on: #10
2025-06-17 18:36:25 +00:00
ac591f49ec docs: Added documentation for nth_derivative function
All checks were successful
Run Python Tests / test (3.12) (pull_request) Successful in 10s
Run Python Tests / test (3.10) (pull_request) Successful in 13s
Run Python Tests / test (3.8) (pull_request) Successful in 10s
2025-06-17 14:35:03 -04:00
ec97aefee1 feat: Added nth derivative showcase in __main__ 2025-06-17 14:34:16 -04:00
d27497488f fix: differential in README.md renamed to derivative 2025-06-17 14:30:40 -04:00
41daf4f7e0 Remove CONTRIBUTORS.md 2025-06-17 14:30:11 -04:00
36f51ca67e fix: Typo in test
All checks were successful
Run Python Tests / test (3.12) (pull_request) Successful in 11s
Run Python Tests / test (3.10) (pull_request) Successful in 13s
Run Python Tests / test (3.8) (pull_request) Successful in 10s
2025-06-17 14:27:53 -04:00
25f20a4db2 v0.2.0
Some checks failed
Run Python Tests / test (3.12) (pull_request) Failing after 11s
Run Python Tests / test (3.10) (pull_request) Failing after 15s
Run Python Tests / test (3.8) (pull_request) Failing after 11s
2025-06-17 14:26:45 -04:00
ee414ea0dc feat: Added function * function multiplication 2025-06-17 14:26:26 -04:00
8656b558b4 feat: Added alternative degree property to return largest_exponent 2025-06-17 14:12:19 -04:00
30a5189928 fix: multiplying by 0 returns a function object representing 0 2025-06-17 14:08:36 -04:00
3d2c724ad4 feat: Add nth derivative function and fix: typo derivitive->derivative 2025-06-17 14:06:45 -04:00
a761efe28e fix: Renamed differential function to derivitive 2025-06-17 13:45:51 -04:00
1165c03955 Apply patch from GitHub PR #10 by jono-rams
Some checks failed
Run Python Tests / test (3.12) (pull_request) Successful in 9s
Run Python Tests / test (3.10) (pull_request) Successful in 14s
Run Python Tests / test (3.8) (pull_request) Successful in 9s
Publish Python Package to PyPI / deploy (push) Has been cancelled
2025-06-17 16:13:43 +00:00
0a36e955a1 Apply patch from GitHub PR #9 by allcontributors[bot]
Some checks failed
Run Python Tests / test (3.10) (pull_request) Successful in 10s
Run Python Tests / test (3.12) (pull_request) Successful in 12s
Run Python Tests / test (3.8) (pull_request) Successful in 9s
Publish Python Package to PyPI / deploy (push) Has been cancelled
2025-06-17 13:59:47 +00:00
c896ecaff8 Update .all-contributorsrc
Some checks failed
Run Python Tests / test (3.10) (pull_request) Successful in 10s
Run Python Tests / test (3.12) (pull_request) Successful in 12s
Publish Python Package to PyPI / deploy (push) Has been cancelled
Run Python Tests / test (3.8) (pull_request) Successful in 9s
2025-06-17 13:57:37 +00:00
b7073287e5 Update README.md 2025-06-17 13:53:34 +00:00
9c47db4e6a [GitHub PR #7 by jono-rams] All contributors/add jono rams (#6)
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 13s
**Mirrored from GitHub PR `#7` by `jono-rams`**

Original PR: https://github.com/jono-rams/PolySolve/pull/7

---

None

Co-authored-by: GitHub Bridge Bot <bot@yourdomain.com>
Reviewed-on: #6
Co-authored-by: gitea-bot <bot@jono-rams.work>
Co-committed-by: gitea-bot <bot@jono-rams.work>
2025-06-17 13:48:34 +00:00
97e4259bfa Merge pull request '[GitHub PR #6 by allcontributors[bot]] docs: add jono-rams as a contributor for code, doc, and infra' (#5) from github-pr-6 into main
Some checks failed
Publish Python Package to PyPI / deploy (push) Has been cancelled
Reviewed-on: #5
2025-06-17 13:29:43 +00:00
a4b947ee07 Apply patch from GitHub PR #6 by allcontributors[bot]
All checks were successful
Run Python Tests / test (3.10) (pull_request) Successful in 9s
Run Python Tests / test (3.12) (pull_request) Successful in 12s
Run Python Tests / test (3.8) (pull_request) Successful in 9s
2025-06-17 13:28:43 +00:00
8e8462d9d8 Add contribution badges
Some checks failed
Run Python Tests / test (3.10) (pull_request) Successful in 9s
Run Python Tests / test (3.8) (pull_request) Successful in 10s
Publish Python Package to PyPI / deploy (push) Has been cancelled
Run Python Tests / test (3.12) (pull_request) Successful in 12s
2025-06-17 09:23:26 -04:00
a4d9f657fe Merge pull request '[GitHub PR #4] Update Contributing and pyproject for GitHub' (#3) from github-pr-4 into main
All checks were successful
Publish Python Package to PyPI / deploy (push) Successful in 20s
Reviewed-on: #3
2025-06-17 13:06:56 +00:00
83f565c08c Apply patch from GitHub PR #4 by jono-rams
All checks were successful
Run Python Tests / test (3.10) (pull_request) Successful in 10s
Run Python Tests / test (3.12) (pull_request) Successful in 15s
Run Python Tests / test (3.8) (pull_request) Successful in 9s
2025-06-17 13:05:16 +00:00
6 changed files with 299 additions and 65 deletions

29
.all-contributorsrc Normal file
View File

@ -0,0 +1,29 @@
{
"projectName": "PolySolve",
"projectOwner": "jono-rams",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributorsPerLine": 7,
"linkToUsage": true,
"skipCi": true,
"contributors": [
{
"login": "jono-rams",
"name": "Jonathan Rampersad",
"avatar_url": "https://avatars.githubusercontent.com/u/29872001?v=4",
"profile": "https://jono-rams.work",
"contributions": [
"code",
"doc",
"infra"
]
}
],
"commitType": "docs"
}

View File

@ -1,6 +1,6 @@
# Contributing to polysolve # Contributing to polysolve
First off, thank you for considering contributing! We welcome any contributions, from fixing a typo to implementing a new feature. First off, thank you for considering contributing! We welcome any contributions, from fixing a typo to implementing a new feature. You can see a list of everyone who has contributed in our [Contributors Hall of Fame](/.github/CONTRIBUTORS.md).
## Table of Contents ## Table of Contents
@ -8,23 +8,26 @@ First off, thank you for considering contributing! We welcome any contributions,
* [Suggesting Enhancements](#suggesting-enhancements) * [Suggesting Enhancements](#suggesting-enhancements)
* [Setting Up the Development Environment](#setting-up-the-development-environment) * [Setting Up the Development Environment](#setting-up-the-development-environment)
* [Running Tests](#running-tests) * [Running Tests](#running-tests)
* [Our CI Process & The Gitea Bridge](#our-ci-process--the-gitea-bridge)
* [Pull Request Process](#pull-request-process) * [Pull Request Process](#pull-request-process)
## Reporting Bugs ## Reporting Bugs
If you find a bug, please open an issue on our Gitea issue tracker. Please include as many details as possible, such as your OS, Python version, steps to reproduce, and any error messages. If you find a bug, please open an issue on our GitHub issue tracker. Please include as many details as possible, such as your OS, Python version, steps to reproduce, and any error messages.
[Report a Bug](https://github.com/jono-rams/PolySolve/issues/new?assignees=&labels=bug&template=bug_report.md&title=)
## Suggesting Enhancements ## Suggesting Enhancements
If you have an idea for a new feature or an improvement, please open an issue to discuss it. This allows us to coordinate efforts and ensure the proposed change aligns with the project's goals. If you have an idea for a new feature or an improvement, please open an issue to discuss it. This allows us to coordinate efforts and ensure the proposed change aligns with the project's goals.
[Suggest an Enhancement](https://github.com/jono-rams/PolySolve/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)
## Setting Up the Development Environment ## Setting Up the Development Environment
1. **Fork the repository** on Gitea. 1. **Fork the repository** on GitHub.
2. **Clone your fork** locally: 2. **Clone your fork** locally:
```bash ```bash
git clone [https://gitea.example.com/YourUsername/PolySolve.git](https://gitea.example.com/YourUsername/PolySolve.git) git clone git clone https://github.com/YourUsername/PolySolve.git
cd PolySolve cd PolySolve
``` ```
@ -58,34 +61,33 @@ We use `pytest` for automated testing. After setting up the development environm
pytest pytest
``` ```
This will automatically discover and run all tests located in the `tests/` directory. This will automatically discover and run all tests located in the `tests/` directory. All tests should pass before you submit your changes.
If you are adding a new feature or fixing a bug, please add a corresponding test case to ensure the code is working correctly and to prevent future regressions.
All tests should pass before you submit your changes. If you are adding a new feature or fixing a bug, please add a corresponding test case to ensure the code is working correctly and to prevent future regressions. ## Our CI Process & The Gitea Bridge
## CI & Automated Testing Environment To ensure that all contributions are consistent and stable, our test suite is executed automatically in a controlled environment. Heres how it works:
1. Our canonical source of truth and CI/CD runners are managed on our private Gitea instance.
2. When you open a Pull Request on GitHub, our "Gitea Bridge" bot automatically mirrors your changes to a corresponding PR on our Gitea instance.
3. The tests are run using Gitea Actions within our specific, reproducible environment.
4. The results (success or failure) are then reported back to your GitHub Pull Request via a status check named "Gitea CI Bridge".
To ensure that all contributions are consistent and stable, our test suite is executed automatically via Gitea Actions. Your pull request must pass all these checks before it can be merged. Your pull request must pass all these checks before it can be merged.
Our CI environment is the ultimate source of truth. It is built using a custom Docker image to lock down dependencies and guarantee reproducibility.
### Reference Environment Specification ### Reference Environment Specification
You can replicate our CI environment exactly to minimize "it works on my machine" issues.
* **Base OS:** Ubuntu 24.04 * **Base OS:** Ubuntu 24.04
* **CUDA Toolkit:** 12.5.1 * **CUDA Toolkit:** 12.5.1
* **Base Docker Image:** `nvidia/cuda:12.5.1-devel-ubuntu24.04` * **Base Docker Image:** `nvidia/cuda:12.5.1-devel-ubuntu24.04`
* **Node.js Version:** 20.x * **Node.js Version:** 20.x
* **Python Versions Tested:** 3.8, 3.10, 3.12 * **Python Versions Tested:** 3.8, 3.10, 3.12
* **CI Docker Image:** You can pull the exact image used by our runners from Docker Hub:
### Reproducing the Environment
You can replicate our CI environment exactly by using the public Docker image we built for our runners. You can pull it from Docker Hub:
```bash ```bash
docker pull c1ph3rd3v/gitea-runner-cuda-node:12.5.1-ubuntu24.04 docker pull c1ph3rd3v/gitea-runner-cuda-node:12.5.1-ubuntu24.04
``` ```
Using this Docker image for your local development will ensure your code runs in the same environment as our automated tests, minimizing "it works on my machine" issues.
## Pull Request Process ## Pull Request Process
1. Create a new branch for your feature or bugfix from the `main` branch: 1. Create a new branch for your feature or bugfix from the `main` branch:
@ -99,7 +101,7 @@ Using this Docker image for your local development will ensure your code runs in
pytest pytest
``` ```
5. Commit your changes with a clear and descriptive commit message. 5. Commit your changes with a clear and descriptive commit message.
6. Push your branch to your fork on Gitea. 6. Push your branch to your fork on GitHub.
7. Open a pull request to the `main` branch of the original `PolySolve` repository. Please provide a clear title and description for your pull request. 7. Open a **Pull Request** to the `main` branch of the `jono-rams/PolySolve` repository on **GitHub**. Please provide a clear title and description for your pull request.
Once you submit your pull request, our automated CI tests will run. We will review your contribution and provide feedback as soon as possible. Thank you for your contribution! Once you submit your pull request, our automated CI tests will run. We will review your contribution and provide feedback as soon as possible. Thank you for your contribution!

View File

@ -1,4 +1,6 @@
# polysolve <p align="center">
<img src="https://i.ibb.co/N22Gx6xq/Poly-Solve-Logo.png" alt="polysolve Logo" width="256">
</p>
[![PyPI version](https://img.shields.io/pypi/v/polysolve.svg)](https://pypi.org/project/polysolve/) [![PyPI version](https://img.shields.io/pypi/v/polysolve.svg)](https://pypi.org/project/polysolve/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/polysolve.svg)](https://pypi.org/project/polysolve/) [![PyPI pyversions](https://img.shields.io/pypi/pyversions/polysolve.svg)](https://pypi.org/project/polysolve/)
@ -9,7 +11,7 @@ A Python library for representing, manipulating, and solving polynomial equation
## Key Features ## Key Features
* **Create and Manipulate Polynomials**: Easily define polynomials of any degree and perform arithmetic operations like addition, subtraction, and scaling. * **Create and Manipulate Polynomials**: Easily define polynomials of any degree using integer or float coefficients, and perform arithmetic operations like addition, subtraction, multiplication, and scaling.
* **Genetic Algorithm Solver**: Find approximate real roots for complex polynomials where analytical solutions are difficult or impossible. * **Genetic Algorithm Solver**: Find approximate real roots for complex polynomials where analytical solutions are difficult or impossible.
* **CUDA Accelerated**: Leverage NVIDIA GPUs for a massive performance boost when finding roots in large solution spaces. * **CUDA Accelerated**: Leverage NVIDIA GPUs for a massive performance boost when finding roots in large solution spaces.
* **Analytical Solvers**: Includes standard, exact solvers for simple cases (e.g., `quadratic_solve`). * **Analytical Solvers**: Includes standard, exact solvers for simple cases (e.g., `quadratic_solve`).
@ -44,6 +46,7 @@ Here is a simple example of how to define a quadratic function, find its propert
from polysolve import Function, GA_Options, quadratic_solve from polysolve import Function, GA_Options, quadratic_solve
# 1. Define the function f(x) = 2x^2 - 3x - 5 # 1. Define the function f(x) = 2x^2 - 3x - 5
# Coefficients can be integers or floats.
f1 = Function(largest_exponent=2) f1 = Function(largest_exponent=2)
f1.set_constants([2, -3, -5]) f1.set_constants([2, -3, -5])
@ -56,17 +59,22 @@ print(f"Value of f1 at x=5 is: {y_val}")
# > Value of f1 at x=5 is: 30.0 # > Value of f1 at x=5 is: 30.0
# 3. Get the derivative: 4x - 3 # 3. Get the derivative: 4x - 3
df1 = f1.differential() df1 = f1.derivative()
print(f"Derivative of f1: {df1}") print(f"Derivative of f1: {df1}")
# > Derivative of f1: 4x - 3 # > Derivative of f1: 4x - 3
# 4. Find roots analytically using the quadratic formula # 4. Get the 2nd derivative: 4
ddf1 = f1.nth_derivative(2)
print(f"2nd Derivative of f1: {ddf1}")
# > Derivative of f1: 4
# 5. Find roots analytically using the quadratic formula
# This is exact and fast for degree-2 polynomials. # This is exact and fast for degree-2 polynomials.
roots_analytic = quadratic_solve(f1) roots_analytic = quadratic_solve(f1)
print(f"Analytic roots: {sorted(roots_analytic)}") print(f"Analytic roots: {sorted(roots_analytic)}")
# > Analytic roots: [-1.0, 2.5] # > Analytic roots: [-1.0, 2.5]
# 5. Find roots with the genetic algorithm (CPU) # 6. Find roots with the genetic algorithm (CPU)
# This can solve polynomials of any degree. # This can solve polynomials of any degree.
ga_opts = GA_Options(num_of_generations=20) ga_opts = GA_Options(num_of_generations=20)
roots_ga = f1.get_real_roots(ga_opts, use_cuda=False) roots_ga = f1.get_real_roots(ga_opts, use_cuda=False)
@ -89,10 +97,41 @@ While the code may work on other configurations, all contributions must pass the
## Contributing ## Contributing
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![GitHub issues](https://img.shields.io/github/issues/jono-rams/PolySolve.svg?style=flat-square)](https://github.com/jono-rams/PolySolve/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/jono-rams/PolySolve.svg?style=flat-square)](https://github.com/jono-rams/PolySolve/pulls)
Contributions are welcome! Whether it's a bug report, a feature request, or a pull request, please feel free to get involved. Contributions are welcome! Whether it's a bug report, a feature request, or a pull request, please feel free to get involved.
Please read our `CONTRIBUTING.md` file for details on our code of conduct and the process for submitting pull requests. Please read our `CONTRIBUTING.md` file for details on our code of conduct and the process for submitting pull requests.
## Contributors
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://jono-rams.work"><img src="https://avatars.githubusercontent.com/u/29872001?v=4?s=100" width="100px;" alt="Jonathan Rampersad"/><br /><sub><b>Jonathan Rampersad</b></sub></a><br /><a href="https://github.com/jono-rams/PolySolve/commits?author=jono-rams" title="Code">💻</a> <a href="https://github.com/jono-rams/PolySolve/commits?author=jono-rams" title="Documentation">📖</a> <a href="#infra-jono-rams" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td align="center" size="13px" colspan="7">
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg">
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
</img>
</td>
</tr>
</tfoot>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
## License ## License
This project is licensed under the MIT License - see the `LICENSE` file for details. This project is licensed under the MIT License - see the `LICENSE` file for details.

View File

@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
# --- Core Metadata --- # --- Core Metadata ---
name = "polysolve" name = "polysolve"
version = "0.1.0" version = "0.3.2"
authors = [ authors = [
{ name="Jonathan Rampersad", email="jonathan@jono-rams.work" }, { name="Jonathan Rampersad", email="jonathan@jono-rams.work" },
] ]
@ -42,5 +42,7 @@ cuda12 = ["cupy-cuda12x"]
dev = ["pytest"] dev = ["pytest"]
[project.urls] [project.urls]
Homepage = "https://gitea.jono-rams.work/jono/PolySolve" Homepage = "https://polysolve.jono-rams.work"
"Bug Tracker" = "https://gitea.jono-rams.work/jono/PolySolve/issues" Documentation = "https://polysolve.jono-rams.work/docs"
Repository = "https://github.com/jono-rams/PolySolve"
"Bug Tracker" = "https://github.com/jono-rams/PolySolve/issues"

View File

@ -1,7 +1,7 @@
import math import math
import numpy as np import numpy as np
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Optional from typing import List, Optional, Union
import warnings import warnings
# Attempt to import CuPy for CUDA acceleration. # Attempt to import CuPy for CUDA acceleration.
@ -12,8 +12,32 @@ try:
except ImportError: except ImportError:
_CUPY_AVAILABLE = False _CUPY_AVAILABLE = False
# The CUDA kernel for the fitness function # The CUDA kernels for the fitness function
_FITNESS_KERNEL = """ _FITNESS_KERNEL_FLOAT = """
extern "C" __global__ void fitness_kernel(
const double* coefficients,
int num_coefficients,
const double* x_vals,
double* ranks,
int size,
double y_val)
{
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < size)
{
double ans = 0;
int lrgst_expo = num_coefficients - 1;
for (int i = 0; i < num_coefficients; ++i)
{
ans += coefficients[i] * pow(x_vals[idx], (double)(lrgst_expo - i));
}
ans -= y_val;
ranks[idx] = (ans == 0) ? 1.7976931348623157e+308 : fabs(1.0 / ans);
}
}
"""
_FITNESS_KERNEL_INT = """
extern "C" __global__ void fitness_kernel( extern "C" __global__ void fitness_kernel(
const long long* coefficients, const long long* coefficients,
int num_coefficients, int num_coefficients,
@ -38,6 +62,7 @@ extern "C" __global__ void fitness_kernel(
} }
""" """
@dataclass @dataclass
class GA_Options: class GA_Options:
""" """
@ -76,13 +101,14 @@ class Function:
self.coefficients: Optional[np.ndarray] = None self.coefficients: Optional[np.ndarray] = None
self._initialized = False self._initialized = False
def set_coeffs(self, coefficients: List[int]): def set_coeffs(self, coefficients: List[Union[int, float]]):
""" """
Sets the coefficients of the polynomial. Sets the coefficients of the polynomial.
Args: Args:
coefficients (List[int]): A list of integer coefficients. The list size coefficients (List[Union[int, float]]): A list of integer or float
must be largest_exponent + 1. coefficients. The list size
must be largest_exponent + 1.
Raises: Raises:
ValueError: If the input is invalid. ValueError: If the input is invalid.
@ -95,8 +121,14 @@ class Function:
) )
if coefficients[0] == 0 and self._largest_exponent > 0: if coefficients[0] == 0 and self._largest_exponent > 0:
raise ValueError("The first constant (for the largest exponent) cannot be 0.") raise ValueError("The first constant (for the largest exponent) cannot be 0.")
# Check if any coefficient is a float
is_float = any(isinstance(c, float) for c in coefficients)
self.coefficients = np.array(coefficients, dtype=np.int64) # Choose the dtype based on the input
target_dtype = np.float64 if is_float else np.int64
self.coefficients = np.array(coefficients, dtype=target_dtype)
self._initialized = True self._initialized = True
def _check_initialized(self): def _check_initialized(self):
@ -108,6 +140,11 @@ class Function:
def largest_exponent(self) -> int: def largest_exponent(self) -> int:
"""Returns the largest exponent of the function.""" """Returns the largest exponent of the function."""
return self._largest_exponent return self._largest_exponent
@property
def degree(self) -> int:
"""Returns the largest exponent of the function."""
return self._largest_exponent
def solve_y(self, x_val: float) -> float: def solve_y(self, x_val: float) -> float:
""" """
@ -129,15 +166,66 @@ class Function:
Returns: Returns:
Function: A new Function object representing the derivative. Function: A new Function object representing the derivative.
""" """
warnings.warn(
"The 'differential' function has been renamed. Please use 'derivative' instead.",
DeprecationWarning,
stacklevel=2
)
self._check_initialized() self._check_initialized()
if self._largest_exponent == 0: if self._largest_exponent == 0:
raise ValueError("Cannot differentiate a constant (Function of degree 0).") raise ValueError("Cannot differentiate a constant (Function of degree 0).")
return self.derivitive()
def derivative(self) -> 'Function':
"""
Calculates the derivative of the function.
Returns:
Function: A new Function object representing the derivative.
"""
self._check_initialized()
if self._largest_exponent == 0:
raise ValueError("Cannot differentiate a constant (Function of degree 0).")
derivative_coefficients = np.polyder(self.coefficients) derivative_coefficients = np.polyder(self.coefficients)
diff_func = Function(self._largest_exponent - 1) diff_func = Function(self._largest_exponent - 1)
diff_func.set_coeffs(derivative_coefficients.tolist()) diff_func.set_coeffs(derivative_coefficients.tolist())
return diff_func return diff_func
def nth_derivative(self, n: int) -> 'Function':
"""
Calculates the nth derivative of the function.
Args:
n (int): The order of the derivative to calculate.
Returns:
Function: A new Function object representing the nth derivative.
"""
self._check_initialized()
if not isinstance(n, int) or n < 1:
raise ValueError("Derivative order 'n' must be a positive integer.")
if n > self.largest_exponent:
function = Function(0)
function.set_coeffs([0])
return function
if n == 1:
return self.derivative()
function = self
for _ in range(n):
function = function.derivative()
return function
def get_real_roots(self, options: GA_Options = GA_Options(), use_cuda: bool = False) -> np.ndarray: def get_real_roots(self, options: GA_Options = GA_Options(), use_cuda: bool = False) -> np.ndarray:
""" """
@ -219,11 +307,16 @@ class Function:
def _solve_x_cuda(self, y_val: float, options: GA_Options) -> np.ndarray: def _solve_x_cuda(self, y_val: float, options: GA_Options) -> np.ndarray:
"""Genetic algorithm implementation using CuPy (GPU/CUDA).""" """Genetic algorithm implementation using CuPy (GPU/CUDA)."""
# Load the raw CUDA kernel
fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL, 'fitness_kernel')
# Move coefficients to GPU # Check the dtype of our coefficients array
d_coefficients = cupy.array(self.coefficients, dtype=cupy.int64) if self.coefficients.dtype == np.float64:
fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL_FLOAT, 'fitness_kernel')
d_coefficients = cupy.array(self.coefficients, dtype=cupy.float64)
elif self.coefficients.dtype == np.int64:
fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL_INT, 'fitness_kernel')
d_coefficients = cupy.array(self.coefficients, dtype=cupy.int64)
else:
raise TypeError(f"Unsupported dtype for CUDA solver: {self.coefficients.dtype}")
# Create initial random solutions on the GPU # Create initial random solutions on the GPU
d_solutions = cupy.random.uniform( d_solutions = cupy.random.uniform(
@ -281,12 +374,16 @@ class Function:
power = self._largest_exponent - i power = self._largest_exponent - i
# Coefficient part # Coefficient part
if c == 1 and power != 0: coeff_val = c
if c == int(c):
coeff_val = int(c)
if coeff_val == 1 and power != 0:
coeff = "" coeff = ""
elif c == -1 and power != 0: elif coeff_val == -1 and power != 0:
coeff = "-" coeff = "-"
else: else:
coeff = str(c) coeff = str(coeff_val)
# Variable part # Variable part
if power == 0: if power == 0:
@ -300,7 +397,7 @@ class Function:
sign = "" sign = ""
if i > 0: if i > 0:
sign = " + " if c > 0 else " - " sign = " + " if c > 0 else " - "
coeff = str(abs(c)) coeff = str(abs(coeff_val))
if abs(c) == 1 and power != 0: if abs(c) == 1 and power != 0:
coeff = "" # Don't show 1 for non-constant terms coeff = "" # Don't show 1 for non-constant terms
@ -336,34 +433,55 @@ class Function:
result_func = Function(len(new_coefficients) - 1) result_func = Function(len(new_coefficients) - 1)
result_func.set_coeffs(new_coefficients.tolist()) result_func.set_coeffs(new_coefficients.tolist())
return result_func return result_func
def __mul__(self, scalar: int) -> 'Function': def _multiply_by_scalar(self, scalar: Union[int, float]) -> 'Function':
"""Multiplies the function by a scalar constant.""" """Helper method to multiply the function by a scalar constant."""
self._check_initialized() self._check_initialized() # It's good practice to check here too
if not isinstance(scalar, (int, float)):
return NotImplemented
if scalar == 0:
raise ValueError("Cannot multiply a function by 0.")
if scalar == 0:
result_func = Function(0)
result_func.set_coeffs([0])
return result_func
new_coefficients = self.coefficients * scalar new_coefficients = self.coefficients * scalar
result_func = Function(self._largest_exponent) result_func = Function(self._largest_exponent)
result_func.set_coeffs(new_coefficients.tolist()) result_func.set_coeffs(new_coefficients.tolist())
return result_func return result_func
def __rmul__(self, scalar: int) -> 'Function': def _multiply_by_function(self, other: 'Function') -> 'Function':
"""Helper method for polynomial multiplication (Function * Function)."""
self._check_initialized()
other._check_initialized()
# np.polymul performs convolution of coefficients to multiply polynomials
new_coefficients = np.polymul(self.coefficients, other.coefficients)
# The degree of the resulting polynomial is derived from the new coefficients
new_degree = len(new_coefficients) - 1
result_func = Function(new_degree)
result_func.set_coeffs(new_coefficients.tolist())
return result_func
def __mul__(self, other: Union['Function', int, float]) -> 'Function':
"""Multiplies the function by a scalar constant."""
if isinstance(other, (int, float)):
return self._multiply_by_scalar(other)
elif isinstance(other, self.__class__):
return self._multiply_by_function(other)
else:
return NotImplemented
def __rmul__(self, scalar: Union[int, float]) -> 'Function':
"""Handles scalar multiplication from the right (e.g., 3 * func).""" """Handles scalar multiplication from the right (e.g., 3 * func)."""
return self.__mul__(scalar) return self.__mul__(scalar)
def __imul__(self, scalar: int) -> 'Function': def __imul__(self, other: Union['Function', int, float]) -> 'Function':
"""Performs in-place multiplication by a scalar (func *= 3).""" """Performs in-place multiplication by a scalar (func *= 3)."""
self._check_initialized()
if not isinstance(scalar, (int, float)): self.coefficients *= other
return NotImplemented
if scalar == 0:
raise ValueError("Cannot multiply a function by 0.")
self.coefficients *= scalar
return self return self
@ -408,9 +526,13 @@ if __name__ == '__main__':
print(f"Value of f1 at x=5 is: {y}") # Expected: 2*(25) - 3*(5) - 5 = 50 - 15 - 5 = 30 print(f"Value of f1 at x=5 is: {y}") # Expected: 2*(25) - 3*(5) - 5 = 50 - 15 - 5 = 30
# Find the derivative: 4x - 3 # Find the derivative: 4x - 3
df1 = f1.differential() df1 = f1.derivative()
print(f"Derivative of f1: {df1}") print(f"Derivative of f1: {df1}")
# Find the second derivative: 4
ddf1 = f1.nth_derivative(2)
print(f"Second derivative of f1: {ddf1}")
# --- Root Finding --- # --- Root Finding ---
# 1. Analytical solution for quadratic # 1. Analytical solution for quadratic
roots_analytic = quadratic_solve(f1) roots_analytic = quadratic_solve(f1)
@ -448,4 +570,18 @@ if __name__ == '__main__':
# Multiplication: (x + 10) * 3 = 3x + 30 # Multiplication: (x + 10) * 3 = 3x + 30
f_mul = f2 * 3 f_mul = f2 * 3
print(f"f2 * 3 = {f_mul}") print(f"f2 * 3 = {f_mul}")
# f3 represents 2x^2 + 3x + 1
f3 = Function(2)
f3.set_coeffs([2, 3, 1])
print(f"Function f3: {f3}")
# f4 represents 5x - 4
f4 = Function(1)
f4.set_coeffs([5, -4])
print(f"Function f4: {f4}")
# Multiply the two functions
product_func = f3 * f4
print(f"f3 * f4 = {product_func}")

View File

@ -24,6 +24,18 @@ def linear_func() -> Function:
f.set_coeffs([1, 10]) f.set_coeffs([1, 10])
return f return f
@pytest.fixture
def m_func_1() -> Function:
f = Function(2)
f.set_coeffs([2, 3, 1])
return f
@pytest.fixture
def m_func_2() -> Function:
f = Function(1)
f.set_coeffs([5, -4])
return f
# --- Core Functionality Tests --- # --- Core Functionality Tests ---
def test_solve_y(quadratic_func): def test_solve_y(quadratic_func):
@ -32,13 +44,20 @@ def test_solve_y(quadratic_func):
assert quadratic_func.solve_y(0) == -5.0 assert quadratic_func.solve_y(0) == -5.0
assert quadratic_func.solve_y(-1) == 0.0 assert quadratic_func.solve_y(-1) == 0.0
def test_differential(quadratic_func): def test_derivative(quadratic_func):
"""Tests the calculation of the function's derivative.""" """Tests the calculation of the function's derivative."""
derivative = quadratic_func.differential() derivative = quadratic_func.derivative()
assert derivative.largest_exponent == 1 assert derivative.largest_exponent == 1
# The derivative of 2x^2 - 3x - 5 is 4x - 3 # The derivative of 2x^2 - 3x - 5 is 4x - 3
assert np.array_equal(derivative.coefficients, [4, -3]) assert np.array_equal(derivative.coefficients, [4, -3])
def test_nth_derivative(quadratic_func):
"""Tests the calculation of the function's 2nd derivative."""
derivative = quadratic_func.nth_derivative(2)
assert derivative.largest_exponent == 0
# The derivative of 2x^2 - 3x - 5 is 4x - 3
assert np.array_equal(derivative.coefficients, [4])
def test_quadratic_solve(quadratic_func): def test_quadratic_solve(quadratic_func):
"""Tests the analytical quadratic solver for exact roots.""" """Tests the analytical quadratic solver for exact roots."""
roots = quadratic_solve(quadratic_func) roots = quadratic_solve(quadratic_func)
@ -61,13 +80,20 @@ def test_subtraction(quadratic_func, linear_func):
assert result.largest_exponent == 2 assert result.largest_exponent == 2
assert np.array_equal(result.coefficients, [2, -4, -15]) assert np.array_equal(result.coefficients, [2, -4, -15])
def test_multiplication(linear_func): def test_scalar_multiplication(linear_func):
"""Tests the multiplication of a Function object by a scalar.""" """Tests the multiplication of a Function object by a scalar."""
# (x + 10) * 3 = 3x + 30 # (x + 10) * 3 = 3x + 30
result = linear_func * 3 result = linear_func * 3
assert result.largest_exponent == 1 assert result.largest_exponent == 1
assert np.array_equal(result.coefficients, [3, 30]) assert np.array_equal(result.coefficients, [3, 30])
def test_function_multiplication(m_func_1, m_func_2):
"""Tests the multiplication of two Function objects."""
# (2x^2 + 3x + 1) * (5x -4) = 10x^3 + 7x^2 - 7x -4
result = m_func_1 * m_func_2
assert result.largest_exponent == 3
assert np.array_equal(result.coefficients, [10, 7, -7, -4])
# --- Genetic Algorithm Root-Finding Tests --- # --- Genetic Algorithm Root-Finding Tests ---
def test_get_real_roots_numpy(quadratic_func): def test_get_real_roots_numpy(quadratic_func):