polysolve 0.6.0__tar.gz → 0.6.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.6.0
3
+ Version: 0.6.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.6.0"
8
+ version = "0.6.2"
9
9
  authors = [
10
10
  { name="Jonathan Rampersad", email="jonathan@jono-rams.work" },
11
11
  ]
@@ -133,6 +133,15 @@ class GA_Options:
133
133
  raise ValueError(
134
134
  f"blend_alpha cannot be negative, but got {self.blend_alpha}"
135
135
  )
136
+ if self.root_precision > 15:
137
+ warnings.warn(
138
+ f"root_precision={self.root_precision} is greater than 15. "
139
+ "This demands an accuracy that is likely impossible for standard "
140
+ "64-bit floats (float64), which are limited to 15-16 significant digits. "
141
+ "The solver may fail to find any roots.",
142
+ UserWarning,
143
+ stacklevel=2
144
+ )
136
145
 
137
146
  def _get_cauchy_bound(coeffs: np.ndarray) -> float:
138
147
  """
@@ -256,7 +265,9 @@ class Function:
256
265
  """
257
266
  self._check_initialized()
258
267
  if self._largest_exponent == 0:
259
- raise ValueError("Cannot differentiate a constant (Function of degree 0).")
268
+ diff_func = Function(0)
269
+ diff_func.set_coeffs([0])
270
+ return diff_func
260
271
 
261
272
  derivative_coefficients = np.polyder(self.coefficients)
262
273
 
@@ -672,7 +683,7 @@ class Function:
672
683
 
673
684
  def _multiply_by_scalar(self, scalar: Union[int, float]) -> 'Function':
674
685
  """Helper method to multiply the function by a scalar constant."""
675
- self._check_initialized() # It's good practice to check here too
686
+ self._check_initialized()
676
687
 
677
688
  if scalar == 0:
678
689
  result_func = Function(0)
@@ -735,6 +746,21 @@ class Function:
735
746
  return NotImplemented
736
747
 
737
748
  return self
749
+
750
+ def __eq__(self, other: object) -> bool:
751
+ """
752
+ Checks if two Function objects are equal by comparing
753
+ their coefficients.
754
+ """
755
+ # Check if the 'other' object is even a Function
756
+ if not isinstance(other, Function):
757
+ return NotImplemented
758
+
759
+ # Ensure both are initialized before trying to access .coefficients
760
+ if not self._initialized or not other._initialized:
761
+ return False
762
+
763
+ return np.array_equal(self.coefficients, other.coefficients)
738
764
 
739
765
 
740
766
  def quadratic_solve(self) -> Optional[List[float]]:
@@ -759,9 +785,35 @@ class Function:
759
785
  return None # No real roots
760
786
 
761
787
  sqrt_discriminant = math.sqrt(discriminant)
762
- root1 = (-b + sqrt_discriminant) / (2 * a)
763
- root2 = (-b - sqrt_discriminant) / (2 * a)
788
+
789
+ # 1. Calculate the first root.
790
+ # We use math.copysign(val, sign) to get the sign of b.
791
+ # This ensures (-b - sign*sqrt) is always an *addition*
792
+ # (or subtraction of a smaller from a larger number),
793
+ # avoiding catastrophic cancellation.
794
+ root1 = (-b - math.copysign(sqrt_discriminant, b)) / (2 * a)
795
+
796
+ # 2. Calculate the second root using Vieta's formulas.
797
+ # We know that root1 * root2 = c / a.
798
+ # This is just a division, which is numerically stable.
799
+
800
+ # Handle the edge case where c=0.
801
+ # If c=0, then root1 is 0.0, and root2 is -b/a
802
+ # We can't divide by root1=0, so we check.
803
+ if root1 == 0.0:
804
+ # If c is also 0, the other root is -b/a
805
+ if c == 0.0:
806
+ root2 = -b / a
807
+ else:
808
+ # This case (root1=0 but c!=0) shouldn't happen
809
+ # with real numbers, but it's safe to just
810
+ # return the one root we found.
811
+ return [0.0]
812
+ else:
813
+ # Standard case: Use Vieta's formula
814
+ root2 = (c / a) / root1
764
815
 
816
+ # Return roots in a consistent order
765
817
  return [root1, root2]
766
818
 
767
819
  # Example Usage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polysolve
3
- Version: 0.6.0
3
+ Version: 0.6.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
@@ -37,6 +37,12 @@ def m_func_2() -> Function:
37
37
  f.set_coeffs([5, -4])
38
38
  return f
39
39
 
40
+ @pytest.fixture
41
+ def base_func():
42
+ f = Function(2)
43
+ f.set_coeffs([1, 2, 3])
44
+ return f
45
+
40
46
  # --- Core Functionality Tests ---
41
47
 
42
48
  def test_solve_y(quadratic_func):
@@ -95,6 +101,32 @@ def test_function_multiplication(m_func_1, m_func_2):
95
101
  assert result.largest_exponent == 3
96
102
  assert np.array_equal(result.coefficients, [10, 7, -7, -4])
97
103
 
104
+ def test_equality(base_func):
105
+ """Tests the __eq__ method for the Function class."""
106
+
107
+ # 1. Test for equality with a new, identical object
108
+ f_identical = Function(2)
109
+ f_identical.set_coeffs([1, 2, 3])
110
+ assert base_func == f_identical
111
+
112
+ # 2. Test for inequality (different coefficients)
113
+ f_different = Function(2)
114
+ f_different.set_coeffs([1, 9, 3])
115
+ assert base_func != f_different
116
+
117
+ # 3. Test for inequality (different degree)
118
+ f_diff_degree = Function(1)
119
+ f_diff_degree.set_coeffs([1, 2])
120
+ assert base_func != f_diff_degree
121
+
122
+ # 4. Test against a different type
123
+ assert base_func != "some_string"
124
+ assert base_func != 123
125
+
126
+ # 5. Test against an uninitialized Function
127
+ f_uninitialized = Function(2)
128
+ assert base_func != f_uninitialized
129
+
98
130
  # --- Genetic Algorithm Root-Finding Tests ---
99
131
 
100
132
  def test_get_real_roots_numpy(quadratic_func):
File without changes
File without changes
File without changes