Compare commits

..

8 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
3 changed files with 66 additions and 21 deletions

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 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
* **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.
* **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`).
@ -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
# 1. Define the function f(x) = 2x^2 - 3x - 5
# Coefficients can be integers or floats.
f1 = Function(largest_exponent=2)
f1.set_constants([2, -3, -5])
@ -61,8 +64,8 @@ print(f"Derivative of f1: {df1}")
# > Derivative of f1: 4x - 3
# 4. Get the 2nd derivative: 4
df1 = f1.nth_derivative(2)
print(f"2nd Derivative of f1: {df1}")
ddf1 = f1.nth_derivative(2)
print(f"2nd Derivative of f1: {ddf1}")
# > Derivative of f1: 4
# 5. Find roots analytically using the quadratic formula

View File

@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
# --- Core Metadata ---
name = "polysolve"
version = "0.2.0"
version = "0.3.2"
authors = [
{ name="Jonathan Rampersad", email="jonathan@jono-rams.work" },
]
@ -42,6 +42,7 @@ cuda12 = ["cupy-cuda12x"]
dev = ["pytest"]
[project.urls]
Homepage = "https://github.com/jono-rams/PolySolve"
"Source Code" = "https://github.com/jono-rams/PolySolve"
Homepage = "https://polysolve.jono-rams.work"
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

@ -12,8 +12,32 @@ try:
except ImportError:
_CUPY_AVAILABLE = False
# The CUDA kernel for the fitness function
_FITNESS_KERNEL = """
# The CUDA kernels for the fitness function
_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(
const long long* coefficients,
int num_coefficients,
@ -38,6 +62,7 @@ extern "C" __global__ void fitness_kernel(
}
"""
@dataclass
class GA_Options:
"""
@ -76,13 +101,14 @@ class Function:
self.coefficients: Optional[np.ndarray] = None
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.
Args:
coefficients (List[int]): A list of integer coefficients. The list size
must be largest_exponent + 1.
coefficients (List[Union[int, float]]): A list of integer or float
coefficients. The list size
must be largest_exponent + 1.
Raises:
ValueError: If the input is invalid.
@ -95,8 +121,14 @@ class Function:
)
if coefficients[0] == 0 and self._largest_exponent > 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
def _check_initialized(self):
@ -275,11 +307,16 @@ class Function:
def _solve_x_cuda(self, y_val: float, options: GA_Options) -> np.ndarray:
"""Genetic algorithm implementation using CuPy (GPU/CUDA)."""
# Load the raw CUDA kernel
fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL, 'fitness_kernel')
# Move coefficients to GPU
d_coefficients = cupy.array(self.coefficients, dtype=cupy.int64)
# Check the dtype of our coefficients array
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
d_solutions = cupy.random.uniform(
@ -337,12 +374,16 @@ class Function:
power = self._largest_exponent - i
# 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 = ""
elif c == -1 and power != 0:
elif coeff_val == -1 and power != 0:
coeff = "-"
else:
coeff = str(c)
coeff = str(coeff_val)
# Variable part
if power == 0:
@ -356,7 +397,7 @@ class Function:
sign = ""
if i > 0:
sign = " + " if c > 0 else " - "
coeff = str(abs(c))
coeff = str(abs(coeff_val))
if abs(c) == 1 and power != 0:
coeff = "" # Don't show 1 for non-constant terms