polysolve 0.4.0__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.0
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.0"
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,15 +304,31 @@ 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)
295
327
  y_calculated = np.polyval(self.coefficients, solutions)
296
328
  error = y_calculated - y_val
297
329
 
298
- ranks = np.where(error == 0, np.finfo(float).max, np.abs(1.0 / error))
330
+ with np.errstate(divide='ignore'):
331
+ ranks = np.where(error == 0, np.finfo(float).max, np.abs(1.0 / error))
299
332
 
300
333
  # Sort solutions by fitness (descending)
301
334
  sorted_indices = np.argsort(-ranks)
@@ -331,7 +364,7 @@ class Function:
331
364
  mutated_solutions = mutation_candidates * mutation_factors
332
365
 
333
366
  # 4. New Randoms: Add new blood to prevent getting stuck
334
- 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)
335
368
 
336
369
  # Assemble the new generation
337
370
  solutions = np.concatenate([
@@ -345,7 +378,8 @@ class Function:
345
378
  # After all generations, do one last ranking to find the best solutions
346
379
  y_calculated = np.polyval(self.coefficients, solutions)
347
380
  error = y_calculated - y_val
348
- ranks = np.where(error == 0, np.finfo(float).max, np.abs(1.0 / error))
381
+ with np.errstate(divide='ignore'):
382
+ ranks = np.where(error == 0, np.finfo(float).max, np.abs(1.0 / error))
349
383
  sorted_indices = np.argsort(-ranks)
350
384
 
351
385
  # Get the top 'sample_size' solutions the user asked for
@@ -371,9 +405,24 @@ class Function:
371
405
  fitness_gpu = cupy.RawKernel(_FITNESS_KERNEL_FLOAT, 'fitness_kernel')
372
406
  d_coefficients = cupy.array(self.coefficients, dtype=cupy.float64)
373
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
+
374
423
  # Create initial random solutions on the GPU
375
424
  d_solutions = cupy.random.uniform(
376
- options.min_range, options.max_range, options.data_size, dtype=cupy.float64
425
+ min_r, max_r, options.data_size, dtype=cupy.float64
377
426
  )
378
427
  d_ranks = cupy.empty(options.data_size, dtype=cupy.float64)
379
428
 
@@ -424,7 +473,7 @@ class Function:
424
473
 
425
474
  # 4. New Randoms
426
475
  d_random_solutions = cupy.random.uniform(
427
- options.min_range, options.max_range, random_size, dtype=cupy.float64
476
+ min_r, max_r, random_size, dtype=cupy.float64
428
477
  )
429
478
 
430
479
  # Assemble the new generation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polysolve
3
- Version: 0.4.0
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