polysolve 0.4.1__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polysolve
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: A Python library for representing, manipulating, and solving exponential functions using analytical methods and genetic algorithms, with optional CUDA acceleration.
5
5
  Author-email: Jonathan Rampersad <jonathan@jono-rams.work>
6
6
  License: MIT License
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  # --- Core Metadata ---
7
7
  name = "polysolve"
8
- version = "0.4.1"
8
+ version = "0.4.2"
9
9
  authors = [
10
10
  { name="Jonathan Rampersad", email="jonathan@jono-rams.work" },
11
11
  ]
@@ -90,6 +90,23 @@ class GA_Options:
90
90
  "The number of returned solutions will be limited to data_size."
91
91
  )
92
92
 
93
+ def _get_cauchy_bound(coeffs: np.ndarray) -> float:
94
+ """
95
+ Calculates Cauchy's bound for the roots of a polynomial.
96
+ This provides a radius R such that all roots (real and complex)
97
+ have an absolute value less than or equal to R.
98
+
99
+ R = 1 + max(|c_n-1/c_n|, |c_n-2/c_n|, ..., |c_0/c_n|)
100
+ Where c_n is the leading coefficient (coeffs[0]).
101
+ """
102
+ # Normalize all coefficients by the leading coefficient
103
+ normalized_coeffs = np.abs(coeffs[1:] / coeffs[0])
104
+
105
+ # The bound is 1 + the maximum of these normalized values
106
+ R = 1 + np.max(normalized_coeffs)
107
+
108
+ return R
109
+
93
110
  class Function:
94
111
  """
95
112
  Represents an exponential function (polynomial) of the form:
@@ -287,8 +304,23 @@ class Function:
287
304
  mutation_size = int(data_size * mutation_ratio)
288
305
  random_size = data_size - elite_size - crossover_size - mutation_size
289
306
 
307
+ # Check if the user is using the default, non-expert range
308
+ user_range_is_default = (options.min_range == -100.0 and options.max_range == 100.0)
309
+
310
+ if user_range_is_default:
311
+ # User hasn't specified a custom range.
312
+ # We are the expert; use the smart, guaranteed bound.
313
+ bound = _get_cauchy_bound(self.coefficients)
314
+ min_r = -bound
315
+ max_r = bound
316
+ else:
317
+ # User has provided a custom range.
318
+ # Trust the expert; use their range.
319
+ min_r = options.min_range
320
+ max_r = options.max_range
321
+
290
322
  # Create initial random solutions
291
- solutions = np.random.uniform(options.min_range, options.max_range, data_size)
323
+ solutions = np.random.uniform(min_r, max_r, data_size)
292
324
 
293
325
  for _ in range(options.num_of_generations):
294
326
  # Calculate fitness for all solutions (vectorized)
@@ -332,7 +364,7 @@ class Function:
332
364
  mutated_solutions = mutation_candidates * mutation_factors
333
365
 
334
366
  # 4. New Randoms: Add new blood to prevent getting stuck
335
- random_solutions = np.random.uniform(options.min_range, options.max_range, random_size)
367
+ random_solutions = np.random.uniform(min_r, max_r, random_size)
336
368
 
337
369
  # Assemble the new generation
338
370
  solutions = np.concatenate([
@@ -373,9 +405,24 @@ class Function:
373
405
  fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL_FLOAT, 'fitness_kernel')
374
406
  d_coefficients = cupy.array(self.coefficients, dtype=cupy.float64)
375
407
 
408
+ # Check if the user is using the default, non-expert range
409
+ user_range_is_default = (options.min_range == -100.0 and options.max_range == 100.0)
410
+
411
+ if user_range_is_default:
412
+ # User hasn't specified a custom range.
413
+ # We are the expert; use the smart, guaranteed bound.
414
+ bound = _get_cauchy_bound(self.coefficients)
415
+ min_r = -bound
416
+ max_r = bound
417
+ else:
418
+ # User has provided a custom range.
419
+ # Trust the expert; use their range.
420
+ min_r = options.min_range
421
+ max_r = options.max_range
422
+
376
423
  # Create initial random solutions on the GPU
377
424
  d_solutions = cupy.random.uniform(
378
- options.min_range, options.max_range, options.data_size, dtype=cupy.float64
425
+ min_r, max_r, options.data_size, dtype=cupy.float64
379
426
  )
380
427
  d_ranks = cupy.empty(options.data_size, dtype=cupy.float64)
381
428
 
@@ -426,7 +473,7 @@ class Function:
426
473
 
427
474
  # 4. New Randoms
428
475
  d_random_solutions = cupy.random.uniform(
429
- options.min_range, options.max_range, random_size, dtype=cupy.float64
476
+ min_r, max_r, random_size, dtype=cupy.float64
430
477
  )
431
478
 
432
479
  # Assemble the new generation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polysolve
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: A Python library for representing, manipulating, and solving exponential functions using analytical methods and genetic algorithms, with optional CUDA acceleration.
5
5
  Author-email: Jonathan Rampersad <jonathan@jono-rams.work>
6
6
  License: MIT License
File without changes
File without changes
File without changes