plumial 0.1.0__py3-none-any.whl
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.
- plumial/__init__.py +83 -0
- plumial/core/D.py +563 -0
- plumial/core/P.py +785 -0
- plumial/core/__init__.py +9 -0
- plumial/core/basis.py +262 -0
- plumial/types.py +264 -0
- plumial/utils/__init__.py +171 -0
- plumial/utils/binary.py +380 -0
- plumial/utils/functions.py +327 -0
- plumial/utils/matrix_utils.py +134 -0
- plumial/utils/symbolic.py +402 -0
- plumial-0.1.0.dist-info/METADATA +220 -0
- plumial-0.1.0.dist-info/RECORD +16 -0
- plumial-0.1.0.dist-info/WHEEL +5 -0
- plumial-0.1.0.dist-info/licenses/LICENSE +21 -0
- plumial-0.1.0.dist-info/top_level.txt +1 -0
plumial/__init__.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plumial: A Python package for Collatz conjecture analysis and computation.
|
|
3
|
+
|
|
4
|
+
This package provides comprehensive tools and mathematical frameworks for analyzing
|
|
5
|
+
the Collatz conjecture through polynomial representations, d-polynomials,
|
|
6
|
+
and binary path analysis.
|
|
7
|
+
|
|
8
|
+
Key Features:
|
|
9
|
+
- Path Polynomials (P class): Represent Collatz sequence paths as mathematical polynomials
|
|
10
|
+
- D Objects (D class): Analyze d-polynomials d_p(g,h) = h^e - g^o fundamental to Collatz analysis
|
|
11
|
+
- Binary Operations: Comprehensive bit-level analysis and cycle navigation
|
|
12
|
+
- Symbolic Mathematics: Full SymPy integration for symbolic computation
|
|
13
|
+
- High Performance: LRU caching and optimized algorithms
|
|
14
|
+
- Type Safety: Comprehensive type hints and modern Python practices
|
|
15
|
+
|
|
16
|
+
Core Classes:
|
|
17
|
+
P: path object class for sequence analysis
|
|
18
|
+
- uv polynomial representations
|
|
19
|
+
- Mathematical relationships (x*d = a*k)
|
|
20
|
+
- Cycle navigation and analysis
|
|
21
|
+
|
|
22
|
+
D: d-polynomial class for algebraic analysis
|
|
23
|
+
- Symbolic form h^e - g^o
|
|
24
|
+
- GCD computation and factorization
|
|
25
|
+
- Polynomial evaluation and manipulation
|
|
26
|
+
|
|
27
|
+
Mathematical Background:
|
|
28
|
+
The Collatz conjecture studies sequences defined by:
|
|
29
|
+
- If n is even: n → n/2
|
|
30
|
+
- If n is odd: n → 3n+1
|
|
31
|
+
|
|
32
|
+
This package encodes these operations using polynomial representations where:
|
|
33
|
+
- h represents halving operations (typically h=2)
|
|
34
|
+
- g represents the 3x+1 operation (typically g=3)
|
|
35
|
+
- Path polynomials encode the sequence of operations
|
|
36
|
+
- Difference polynomials h^e - g^o capture cycle behavior
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
>>> from plumial import P, D
|
|
40
|
+
>>>
|
|
41
|
+
>>> # Create a path polynomial for p-value 133
|
|
42
|
+
>>> p = P(133)
|
|
43
|
+
>>> print(f"Path bits: n={p.n()}, odd={p.o()}, even={p.e()}")
|
|
44
|
+
>>>
|
|
45
|
+
>>> # Get uv polynomial representation
|
|
46
|
+
>>> uv_poly = p.uv()
|
|
47
|
+
>>> print(f"uv polynomial: {uv_poly}")
|
|
48
|
+
>>>
|
|
49
|
+
>>> # Evaluate k polynomial using modern encoding approach
|
|
50
|
+
>>> collatz_p = p.encode(g=3, h=2)
|
|
51
|
+
>>> k_value = collatz_p.k()
|
|
52
|
+
>>> print(f"k(3,2) = {k_value}")
|
|
53
|
+
>>>
|
|
54
|
+
>>> # Create d-polynomial
|
|
55
|
+
>>> d = D(2, 5)
|
|
56
|
+
>>> print(f"Difference polynomial: {d.d()}") # h^5 - g^2
|
|
57
|
+
>>> collatz_d = d.encode(g=3, h=2)
|
|
58
|
+
>>> print(f"Evaluated at g=3, h=2: {collatz_d.d()}") # 23
|
|
59
|
+
|
|
60
|
+
Installation:
|
|
61
|
+
pip install plumial
|
|
62
|
+
|
|
63
|
+
Dependencies:
|
|
64
|
+
- sympy: Symbolic mathematics
|
|
65
|
+
- numpy: Numerical computing
|
|
66
|
+
- typing: Type hints (Python 3.8+)
|
|
67
|
+
|
|
68
|
+
Author: Jon Seymour <jon@wildducktheories.com>
|
|
69
|
+
License: MIT
|
|
70
|
+
Version: 0.1.0
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
__version__ = "0.1.0"
|
|
74
|
+
__author__ = "Jon Seymour"
|
|
75
|
+
__email__ = "jon@wildducktheories.com"
|
|
76
|
+
|
|
77
|
+
from .core import D, P
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
"__version__",
|
|
81
|
+
"P",
|
|
82
|
+
"D",
|
|
83
|
+
]
|
plumial/core/D.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"""
|
|
2
|
+
D objects for Collatz conjecture analysis.
|
|
3
|
+
|
|
4
|
+
This module implements the D class representing d-polynomials of the form
|
|
5
|
+
d_p(g,h) = h^e - g^o, which are fundamental to the algebraic analysis of Collatz sequences.
|
|
6
|
+
|
|
7
|
+
The d-polynomial d_p(g,h) = h^e - g^o encodes the core mathematical structure of
|
|
8
|
+
Collatz sequences, where:
|
|
9
|
+
- h represents the "halving" operation (typically h=2)
|
|
10
|
+
- g represents the "3x+1" operation (typically g=3)
|
|
11
|
+
- e is the number of even steps (halving operations)
|
|
12
|
+
- o is the number of odd steps (3x+1 operations)
|
|
13
|
+
|
|
14
|
+
Key Features:
|
|
15
|
+
- Symbolic representation and evaluation of d-polynomials
|
|
16
|
+
- GCD computation for polynomial analysis
|
|
17
|
+
- Factorization and algebraic manipulation
|
|
18
|
+
- LRU caching for performance optimization
|
|
19
|
+
- Support for both symbolic and numerical computations
|
|
20
|
+
|
|
21
|
+
Mathematical Background:
|
|
22
|
+
The d-polynomial d_p(g,h) = h^e - g^o represents the net effect of a Collatz
|
|
23
|
+
sequence segment.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
>>> from plumial.core.D import D
|
|
27
|
+
>>> from plumial.core.basis import B
|
|
28
|
+
>>> d = D(2, 5) # Create from o=2, e=5 (h^5 - g^2)
|
|
29
|
+
>>> print(d.o(), d.e(), d.n()) # 2, 5, 7
|
|
30
|
+
>>> poly = d.d() # Get symbolic form: h^5 - g^2
|
|
31
|
+
>>> collatz_d = d.encode(B.Collatz) # Encode for Collatz evaluation
|
|
32
|
+
>>> result = collatz_d.d() # Evaluate: 2^5 - 3^2 = 32 - 9 = 23
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import math
|
|
36
|
+
from functools import lru_cache
|
|
37
|
+
from typing import Any, List, Optional, Union
|
|
38
|
+
|
|
39
|
+
import numpy as np
|
|
40
|
+
import sympy as sy
|
|
41
|
+
|
|
42
|
+
# Import type aliases for better type safety
|
|
43
|
+
from ..types import (
|
|
44
|
+
CacheInfo,
|
|
45
|
+
FactorResult,
|
|
46
|
+
GCDResult,
|
|
47
|
+
NumericOrSymbolic,
|
|
48
|
+
NumericType,
|
|
49
|
+
OptionalNumeric,
|
|
50
|
+
)
|
|
51
|
+
from ..utils.symbolic import g as g_sym
|
|
52
|
+
from ..utils.symbolic import h as h_sym
|
|
53
|
+
from .basis import Basis, B, resolve_basis
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _D:
|
|
57
|
+
"""
|
|
58
|
+
Internal class representing a d-polynomial d_p(g,h) = h^e - g^o.
|
|
59
|
+
|
|
60
|
+
This class encapsulates the mathematical structure of a d-polynomial
|
|
61
|
+
used in Collatz sequence analysis. It provides methods for symbolic manipulation
|
|
62
|
+
and numerical evaluation.
|
|
63
|
+
|
|
64
|
+
The class maintains the fundamental parameters n, o, e for the polynomial form h^e - g^o.
|
|
65
|
+
|
|
66
|
+
This class should not be instantiated directly. Use the D() factory function instead.
|
|
67
|
+
|
|
68
|
+
Mathematical Structure:
|
|
69
|
+
- n: total number of bits (path length)
|
|
70
|
+
- o: number of odd bits (3x+1 operations)
|
|
71
|
+
- e: number of even bits (halving operations), where e = n - o
|
|
72
|
+
- The polynomial form: h^e - g^o
|
|
73
|
+
|
|
74
|
+
Attributes:
|
|
75
|
+
_o: Number of odd bits
|
|
76
|
+
_e: Number of even bits
|
|
77
|
+
_n: Total number of bits (computed as o + e)
|
|
78
|
+
_expr: Cached symbolic expression
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(self, o: int, e: int, basis: Optional[Basis] = None) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Initialize d-polynomial from odd and even bit counts.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
o: Number of odd bits (must be >= 0)
|
|
87
|
+
e: Number of even bits (must be >= 0)
|
|
88
|
+
basis: The mathematical basis for this encoding (default: symbolic basis)
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
ValueError: If o < 0 or e < 0
|
|
92
|
+
|
|
93
|
+
Note:
|
|
94
|
+
This constructor is internal. Use D() factory function instead.
|
|
95
|
+
"""
|
|
96
|
+
if o < 0:
|
|
97
|
+
raise ValueError(f"Number of odd bits must be >= 0, got o={o}")
|
|
98
|
+
if e < 0:
|
|
99
|
+
raise ValueError(f"Number of even bits must be >= 0, got e={e}")
|
|
100
|
+
|
|
101
|
+
self._o = o
|
|
102
|
+
self._e = e
|
|
103
|
+
self._n = o + e
|
|
104
|
+
self._basis = basis if basis is not None else B.Symbolic
|
|
105
|
+
|
|
106
|
+
# Cached symbolic expression
|
|
107
|
+
self._expr = None
|
|
108
|
+
|
|
109
|
+
def n(self) -> int:
|
|
110
|
+
"""Return total number of bits (computed as o + e)."""
|
|
111
|
+
return self._n
|
|
112
|
+
|
|
113
|
+
def o(self) -> int:
|
|
114
|
+
"""Return number of odd bits."""
|
|
115
|
+
return self._o
|
|
116
|
+
|
|
117
|
+
def e(self) -> int:
|
|
118
|
+
"""Return number of even bits."""
|
|
119
|
+
return self._e
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def basis(self) -> Basis:
|
|
123
|
+
"""Return the basis of this encoding."""
|
|
124
|
+
return self._basis
|
|
125
|
+
|
|
126
|
+
def c(self) -> NumericOrSymbolic:
|
|
127
|
+
"""
|
|
128
|
+
Calculate the ceiling of log_h(g).
|
|
129
|
+
|
|
130
|
+
This method computes ceil(log_h(g)) where log_h(g) is the logarithm
|
|
131
|
+
of g with base h. The result represents an important mathematical bound
|
|
132
|
+
related to the d-polynomial structure.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Ceiling value, either symbolic or evaluated based on the basis
|
|
136
|
+
|
|
137
|
+
Mathematical Formula:
|
|
138
|
+
c = ceil(log_h(g)) = ceil(log(g) / log(h))
|
|
139
|
+
|
|
140
|
+
Examples:
|
|
141
|
+
>>> d = D(2, 5)
|
|
142
|
+
>>> d.c() # Symbolic form
|
|
143
|
+
>>> collatz_d = D(2, 5).encode(B.Collatz)
|
|
144
|
+
>>> collatz_d.c() # Numerical evaluation for g=3, h=2
|
|
145
|
+
"""
|
|
146
|
+
# Get basis parameters
|
|
147
|
+
basis_dict = self._basis.dict()
|
|
148
|
+
g_val = basis_dict.get('g')
|
|
149
|
+
h_val = basis_dict.get('h')
|
|
150
|
+
|
|
151
|
+
if self._basis == B.Symbolic or g_val is None or h_val is None:
|
|
152
|
+
# Return symbolic form
|
|
153
|
+
return sy.ceiling(sy.log(g_sym, h_sym))
|
|
154
|
+
else:
|
|
155
|
+
# Return numerical evaluation
|
|
156
|
+
if g_val <= 0 or h_val <= 0 or h_val == 1:
|
|
157
|
+
raise ValueError(f"Invalid basis values for logarithm: g={g_val}, h={h_val}")
|
|
158
|
+
|
|
159
|
+
log_val = math.log(g_val) / math.log(h_val)
|
|
160
|
+
return math.ceil(log_val)
|
|
161
|
+
|
|
162
|
+
def r(self) -> NumericOrSymbolic:
|
|
163
|
+
"""
|
|
164
|
+
Calculate the remainder: c() * o() - e().
|
|
165
|
+
|
|
166
|
+
This method computes the remainder value defined as the ceiling of log_h(g)
|
|
167
|
+
times the number of odd bits minus the number of even bits.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Remainder value, either symbolic or evaluated based on the basis
|
|
171
|
+
|
|
172
|
+
Mathematical Formula:
|
|
173
|
+
r = c * o - e = ceil(log_h(g)) * o - e
|
|
174
|
+
|
|
175
|
+
Examples:
|
|
176
|
+
>>> d = D(2, 5)
|
|
177
|
+
>>> d.r() # Symbolic form
|
|
178
|
+
>>> collatz_d = D(2, 5).encode(B.Collatz)
|
|
179
|
+
>>> collatz_d.r() # Numerical evaluation: 2 * 2 - 5 = -1
|
|
180
|
+
"""
|
|
181
|
+
c_val = self.c()
|
|
182
|
+
|
|
183
|
+
if self._basis == B.Symbolic:
|
|
184
|
+
# Return symbolic form
|
|
185
|
+
return c_val * self._o - self._e
|
|
186
|
+
else:
|
|
187
|
+
# Return numerical evaluation
|
|
188
|
+
return c_val * self._o - self._e
|
|
189
|
+
|
|
190
|
+
def encode(
|
|
191
|
+
self,
|
|
192
|
+
basis: Optional[Basis] = None,
|
|
193
|
+
g: OptionalNumeric = None,
|
|
194
|
+
h: OptionalNumeric = None
|
|
195
|
+
) -> "_D":
|
|
196
|
+
"""
|
|
197
|
+
Create a new D object encoded in a different basis.
|
|
198
|
+
|
|
199
|
+
This method enables the transitive encoding property where D objects
|
|
200
|
+
can be transformed between different coordinate systems while preserving
|
|
201
|
+
the underlying mathematical structure.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
basis: Target basis for encoding
|
|
205
|
+
g: g parameter for custom basis (alternative to basis parameter)
|
|
206
|
+
h: h parameter for custom basis (alternative to basis parameter)
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
New D object with same n,o values but different basis
|
|
210
|
+
|
|
211
|
+
Examples:
|
|
212
|
+
>>> d = D(2, 5) # Symbolic basis
|
|
213
|
+
>>> collatz_d = d.encode(B.Collatz) # Collatz basis
|
|
214
|
+
>>> custom_d = d.encode(g=5, h=2) # Custom basis
|
|
215
|
+
>>> back_d = collatz_d.encode() # Back to symbolic basis
|
|
216
|
+
>>> assert back_d == d # Round-trip equality
|
|
217
|
+
"""
|
|
218
|
+
# Handle empty encode() - return to symbolic basis
|
|
219
|
+
if basis is None and g is None and h is None:
|
|
220
|
+
return D(self._o, self._e, basis=B.Symbolic)
|
|
221
|
+
|
|
222
|
+
# Resolve target basis
|
|
223
|
+
target_basis = resolve_basis(basis=basis, g=g, h=h)
|
|
224
|
+
|
|
225
|
+
# Return new D object with same o,e but different basis
|
|
226
|
+
return D(self._o, self._e, basis=target_basis)
|
|
227
|
+
|
|
228
|
+
def _as_expr(self) -> sy.Expr:
|
|
229
|
+
"""
|
|
230
|
+
Return the symbolic expression h^e - g^o.
|
|
231
|
+
|
|
232
|
+
Private method for internal use. External code should use d() method
|
|
233
|
+
or SymPy integration methods (__add__, __mul__, etc.).
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
SymPy expression representing the d-polynomial
|
|
237
|
+
"""
|
|
238
|
+
if self._expr is None:
|
|
239
|
+
self._expr = h_sym**self._e - g_sym**self._o
|
|
240
|
+
return self._expr
|
|
241
|
+
|
|
242
|
+
def d(self) -> NumericOrSymbolic:
|
|
243
|
+
"""
|
|
244
|
+
Evaluate the d-polynomial.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Evaluated expression or symbolic form
|
|
248
|
+
|
|
249
|
+
Examples:
|
|
250
|
+
>>> d = D(2, 5)
|
|
251
|
+
>>> d.d() # Symbolic form
|
|
252
|
+
h**5 - g**2
|
|
253
|
+
>>> collatz_d = D(2, 5).encode(B.Collatz)
|
|
254
|
+
>>> collatz_d.d() # Uses basis automatically
|
|
255
|
+
23
|
|
256
|
+
"""
|
|
257
|
+
result = self._as_expr()
|
|
258
|
+
|
|
259
|
+
# Use basis if it's not symbolic
|
|
260
|
+
if self._basis != B.Symbolic:
|
|
261
|
+
basis_dict = self._basis.dict()
|
|
262
|
+
if basis_dict['g'] is not None:
|
|
263
|
+
result = result.subs(g_sym, basis_dict['g'])
|
|
264
|
+
if basis_dict['h'] is not None:
|
|
265
|
+
result = result.subs(h_sym, basis_dict['h'])
|
|
266
|
+
|
|
267
|
+
return result
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def G(self) -> sy.Matrix:
|
|
274
|
+
"""
|
|
275
|
+
Create column vector of g powers from g^(o-1) down to g^0.
|
|
276
|
+
|
|
277
|
+
This method generates a column vector containing powers of g in descending order,
|
|
278
|
+
used for matrix operations with polynomial coefficients. The vector has o elements
|
|
279
|
+
corresponding to the number of odd bits.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
SymPy Matrix column vector with g powers [g^(o-1), g^(o-2), ..., g^1, g^0]
|
|
283
|
+
|
|
284
|
+
Mathematical Structure:
|
|
285
|
+
For o odd bits, creates: [g^(o-1), g^(o-2), ..., g^1, g^0]^T
|
|
286
|
+
|
|
287
|
+
Examples:
|
|
288
|
+
>>> d = D(2, 5) # o=2, e=5
|
|
289
|
+
>>> g_vector = d.G()
|
|
290
|
+
>>> # Returns Matrix([[g], [1]]) for powers g^1, g^0
|
|
291
|
+
|
|
292
|
+
Matrix Operations:
|
|
293
|
+
>>> d = D(3, 4) # o=3, e=4
|
|
294
|
+
>>> G_vec = d.G()
|
|
295
|
+
>>> # Returns Matrix([[g**2], [g], [1]]) for use in polynomial operations
|
|
296
|
+
"""
|
|
297
|
+
if self._o == 0:
|
|
298
|
+
# Special case: no odd bits means empty matrix
|
|
299
|
+
return sy.Matrix([])
|
|
300
|
+
|
|
301
|
+
# Create column vector with g powers from g^(o-1) down to g^0
|
|
302
|
+
powers = [g_sym**i for i in range(self._o - 1, -1, -1)]
|
|
303
|
+
return sy.Matrix(powers)
|
|
304
|
+
|
|
305
|
+
def __str__(self) -> str:
|
|
306
|
+
"""String representation showing the polynomial."""
|
|
307
|
+
return str(self._as_expr())
|
|
308
|
+
|
|
309
|
+
def __repr__(self) -> str:
|
|
310
|
+
"""String representation showing the polynomial."""
|
|
311
|
+
return repr(self._as_expr())
|
|
312
|
+
|
|
313
|
+
# SymPy integration methods
|
|
314
|
+
def _sympy_(self) -> sy.Expr:
|
|
315
|
+
"""
|
|
316
|
+
SymPy conversion method - allows D objects to participate in SymPy expressions.
|
|
317
|
+
|
|
318
|
+
This method is called automatically by SymPy when a D object is used
|
|
319
|
+
in mathematical expressions, enabling seamless integration with symbolic math.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
SymPy expression representing the d-polynomial h^e - g^o
|
|
323
|
+
|
|
324
|
+
Examples:
|
|
325
|
+
>>> d = D(2, 5)
|
|
326
|
+
>>> expr = d + sy.Symbol('x') # Automatically calls _sympy_()
|
|
327
|
+
>>> result = sy.expand(expr * 2) # Works with all SymPy operations
|
|
328
|
+
"""
|
|
329
|
+
return self._as_expr()
|
|
330
|
+
|
|
331
|
+
def __add__(self, other) -> sy.Expr:
|
|
332
|
+
"""
|
|
333
|
+
Addition with other expressions.
|
|
334
|
+
|
|
335
|
+
Enables expressions like: D(2, 5) + x or D(1, 2) + D(3, 4)
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
other: Another D object, SymPy expression, or numeric value
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
SymPy expression representing the sum
|
|
342
|
+
"""
|
|
343
|
+
if isinstance(other, _D):
|
|
344
|
+
return self._as_expr() + other._as_expr()
|
|
345
|
+
return self._as_expr() + other
|
|
346
|
+
|
|
347
|
+
def __radd__(self, other) -> sy.Expr:
|
|
348
|
+
"""Right addition - handles x + D(2, 5)."""
|
|
349
|
+
return other + self._as_expr()
|
|
350
|
+
|
|
351
|
+
def __sub__(self, other) -> sy.Expr:
|
|
352
|
+
"""
|
|
353
|
+
Subtraction with other expressions.
|
|
354
|
+
|
|
355
|
+
Enables expressions like: D(2, 5) - x or D(3, 4) - D(1, 2)
|
|
356
|
+
"""
|
|
357
|
+
if isinstance(other, _D):
|
|
358
|
+
return self._as_expr() - other._as_expr()
|
|
359
|
+
return self._as_expr() - other
|
|
360
|
+
|
|
361
|
+
def __rsub__(self, other) -> sy.Expr:
|
|
362
|
+
"""Right subtraction - handles x - D(2, 5)."""
|
|
363
|
+
return other - self._as_expr()
|
|
364
|
+
|
|
365
|
+
def __mul__(self, other) -> sy.Expr:
|
|
366
|
+
"""
|
|
367
|
+
Multiplication with other expressions.
|
|
368
|
+
|
|
369
|
+
Enables expressions like: D(2, 5) * x or D(1, 2) * D(3, 4)
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
other: Another D object, SymPy expression, or numeric value
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
SymPy expression representing the product
|
|
376
|
+
"""
|
|
377
|
+
if isinstance(other, _D):
|
|
378
|
+
return self._as_expr() * other._as_expr()
|
|
379
|
+
return self._as_expr() * other
|
|
380
|
+
|
|
381
|
+
def __rmul__(self, other) -> sy.Expr:
|
|
382
|
+
"""Right multiplication - handles x * D(2, 5)."""
|
|
383
|
+
return other * self._as_expr()
|
|
384
|
+
|
|
385
|
+
def __truediv__(self, other) -> sy.Expr:
|
|
386
|
+
"""
|
|
387
|
+
Division with other expressions.
|
|
388
|
+
|
|
389
|
+
Enables expressions like: D(2, 5) / x or D(3, 4) / D(1, 2)
|
|
390
|
+
"""
|
|
391
|
+
if isinstance(other, _D):
|
|
392
|
+
return self._as_expr() / other._as_expr()
|
|
393
|
+
return self._as_expr() / other
|
|
394
|
+
|
|
395
|
+
def __rtruediv__(self, other) -> sy.Expr:
|
|
396
|
+
"""Right division - handles x / D(2, 5)."""
|
|
397
|
+
return other / self._as_expr()
|
|
398
|
+
|
|
399
|
+
def __pow__(self, other) -> sy.Expr:
|
|
400
|
+
"""
|
|
401
|
+
Exponentiation with other expressions.
|
|
402
|
+
|
|
403
|
+
Enables expressions like: D(2, 5) ** 2 or D(1, 2) ** x
|
|
404
|
+
"""
|
|
405
|
+
return self._as_expr() ** other
|
|
406
|
+
|
|
407
|
+
def __rpow__(self, other) -> sy.Expr:
|
|
408
|
+
"""Right exponentiation - handles x ** D(2, 5)."""
|
|
409
|
+
return other ** self._as_expr()
|
|
410
|
+
|
|
411
|
+
def __neg__(self) -> sy.Expr:
|
|
412
|
+
"""Negation - handles -D(2, 5)."""
|
|
413
|
+
return -self._as_expr()
|
|
414
|
+
|
|
415
|
+
def __pos__(self) -> sy.Expr:
|
|
416
|
+
"""Positive - handles +D(2, 5)."""
|
|
417
|
+
return +self._as_expr()
|
|
418
|
+
|
|
419
|
+
def __eq__(self, other) -> bool:
|
|
420
|
+
"""Equality comparison based on n, o, and basis values."""
|
|
421
|
+
if isinstance(other, _D):
|
|
422
|
+
return self._n == other._n and self._o == other._o and self._basis == other._basis
|
|
423
|
+
return False
|
|
424
|
+
|
|
425
|
+
def __hash__(self) -> int:
|
|
426
|
+
"""Hash based on n, o, and basis values."""
|
|
427
|
+
return hash((self._n, self._o, self._basis))
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@lru_cache(maxsize=1000)
|
|
431
|
+
def _create_d_cached(o: int, e: int, basis_hash: int) -> _D:
|
|
432
|
+
"""Create _D instance with LRU caching based on (o, e, basis) combination."""
|
|
433
|
+
# Reconstruct basis from hash - this is a workaround since we can't cache
|
|
434
|
+
# Basis objects directly. In practice, most usage will be with common bases.
|
|
435
|
+
if basis_hash == hash(B.Symbolic):
|
|
436
|
+
basis = B.Symbolic
|
|
437
|
+
elif basis_hash == hash(B.Collatz):
|
|
438
|
+
basis = B.Collatz
|
|
439
|
+
elif basis_hash == hash(B.Collatz_5_2):
|
|
440
|
+
basis = B.Collatz_5_2
|
|
441
|
+
elif basis_hash == hash(B.Collatz_7_2):
|
|
442
|
+
basis = B.Collatz_7_2
|
|
443
|
+
elif basis_hash == hash(B.Collatz_5_4):
|
|
444
|
+
basis = B.Collatz_5_4
|
|
445
|
+
elif basis_hash == hash(B.Collatz_7_4):
|
|
446
|
+
basis = B.Collatz_7_4
|
|
447
|
+
else:
|
|
448
|
+
# For custom bases, we skip caching to avoid hash collisions
|
|
449
|
+
raise ValueError(f"Cannot reconstruct basis from hash {basis_hash}")
|
|
450
|
+
|
|
451
|
+
return _D(o, e, basis=basis)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def D(o: Optional[int] = None,
|
|
455
|
+
e: Optional[int] = None,
|
|
456
|
+
n: Optional[int] = None,
|
|
457
|
+
basis: Optional[Basis] = None) -> _D:
|
|
458
|
+
"""
|
|
459
|
+
Factory function for creating D instances from bit counts.
|
|
460
|
+
|
|
461
|
+
This is the main entry point for creating D objects. It supports multiple
|
|
462
|
+
calling patterns for flexibility.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
o: Number of odd bits
|
|
466
|
+
e: Number of even bits
|
|
467
|
+
n: Total number of bits (alternative parameter)
|
|
468
|
+
basis: The mathematical basis for this encoding (default: symbolic basis)
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
_D instance representing the d-polynomial h^e - g^o
|
|
472
|
+
|
|
473
|
+
Raises:
|
|
474
|
+
ValueError: If parameters are invalid or inconsistent
|
|
475
|
+
|
|
476
|
+
Usage Patterns:
|
|
477
|
+
- D(o, e): Direct polynomial specification h^e - g^o
|
|
478
|
+
- D(o=2, e=5): Named parameters
|
|
479
|
+
- D(o=2, n=7): Named, compute e = n - o
|
|
480
|
+
- D(e=5, n=7): Named, compute o = n - e
|
|
481
|
+
|
|
482
|
+
Examples:
|
|
483
|
+
>>> d1 = D(2, 5) # h^5 - g^2
|
|
484
|
+
>>> d2 = D(o=2, e=5, basis=B.Collatz) # With specific basis
|
|
485
|
+
>>> d3 = D(o=2, n=7) # Compute e = 7 - 2 = 5
|
|
486
|
+
>>> d4 = D(e=5, n=7) # Compute o = 7 - 5 = 2
|
|
487
|
+
"""
|
|
488
|
+
# Set default basis
|
|
489
|
+
if basis is None:
|
|
490
|
+
basis = B.Symbolic
|
|
491
|
+
|
|
492
|
+
# Count how many parameters are provided
|
|
493
|
+
provided = sum(x is not None for x in [o, e, n])
|
|
494
|
+
|
|
495
|
+
if provided == 2:
|
|
496
|
+
if o is not None and e is not None:
|
|
497
|
+
# D(o, e) or D(o=2, e=5) - direct specification
|
|
498
|
+
pass # o and e are already set
|
|
499
|
+
elif o is not None and n is not None:
|
|
500
|
+
# D(o=2, n=7) - compute e = n - o
|
|
501
|
+
e = n - o
|
|
502
|
+
if e < 0:
|
|
503
|
+
raise ValueError(f"Invalid: n={n}, o={o} results in e={e} < 0")
|
|
504
|
+
elif e is not None and n is not None:
|
|
505
|
+
# D(e=5, n=7) - compute o = n - e
|
|
506
|
+
o = n - e
|
|
507
|
+
if o < 0:
|
|
508
|
+
raise ValueError(f"Invalid: n={n}, e={e} results in o={o} < 0")
|
|
509
|
+
else:
|
|
510
|
+
raise ValueError("Must specify exactly two of (o, e, n)")
|
|
511
|
+
elif provided == 3:
|
|
512
|
+
# All three provided - validate consistency
|
|
513
|
+
if n != o + e:
|
|
514
|
+
raise ValueError(
|
|
515
|
+
f"Inconsistent parameters: n={n} but o+e={o}+{e}={o+e}. "
|
|
516
|
+
f"Specify exactly two of (o, e, n)."
|
|
517
|
+
)
|
|
518
|
+
else:
|
|
519
|
+
raise ValueError("Must specify exactly two of (o, e, n)")
|
|
520
|
+
|
|
521
|
+
# Use cached version for predefined bases
|
|
522
|
+
try:
|
|
523
|
+
return _create_d_cached(o, e, hash(basis))
|
|
524
|
+
except ValueError:
|
|
525
|
+
# Custom basis - skip caching
|
|
526
|
+
return _D(o, e, basis=basis)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def clear_d_cache() -> None:
|
|
530
|
+
"""
|
|
531
|
+
Clear the D instance cache.
|
|
532
|
+
|
|
533
|
+
This function clears all cached D instances, which can be useful for
|
|
534
|
+
memory management or when you want to ensure fresh instances are created.
|
|
535
|
+
After calling this function, subsequent calls to D() will create new
|
|
536
|
+
instances rather than returning cached ones.
|
|
537
|
+
|
|
538
|
+
Examples:
|
|
539
|
+
>>> d1 = D(2, 5)
|
|
540
|
+
>>> clear_d_cache()
|
|
541
|
+
>>> d2 = D(2, 5)
|
|
542
|
+
>>> assert d1 is not d2 # Different instances after cache clear
|
|
543
|
+
"""
|
|
544
|
+
_create_d_cached.cache_clear()
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def d_cache_info() -> CacheInfo:
|
|
548
|
+
"""
|
|
549
|
+
Get cache statistics for D instances.
|
|
550
|
+
|
|
551
|
+
Returns cache hit/miss statistics and current cache size information
|
|
552
|
+
for the D instance cache. This is useful for performance monitoring
|
|
553
|
+
and cache effectiveness analysis.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
Cache information object with hits, misses, maxsize, and currsize
|
|
557
|
+
|
|
558
|
+
Examples:
|
|
559
|
+
>>> info = d_cache_info()
|
|
560
|
+
>>> print(f"D cache hits: {info.hits}, misses: {info.misses}")
|
|
561
|
+
>>> print(f"D cache size: {info.currsize}/{info.maxsize}")
|
|
562
|
+
"""
|
|
563
|
+
return _create_d_cached.cache_info()
|