@@ -6,48 +6,30 @@ class LogTowerGenerator:
66 """
77 A symbolic engine for computing the n-th derivatives of Log-Tower functions:
88 A(x) = h(x) * ln(g(x)) / ln(f(x))
9-
10- It implements the F/G-sector symmetry and closed-form double-sum expansions
11- to bypass iterative differentiation.
129 """
1310
1411 def __init__ (self , h_coeffs , f_coeffs , g_coeffs , r0 ):
15- """
16- Initialize the generator with the defining symbolic sequences.
17-
18- Parameters:
19- h_coeffs (list): Coefficients [h_0, h_1, ...] representing derivatives of h(x).
20- f_coeffs (list): Coefficients [F_0, F_1, ...] for the characteristic sequence F.
21- g_coeffs (list): Coefficients [G_0, G_1, ...] for the forcing sequence G.
22- r0 (Symbol/Expr): The initial condition R_0 (usually A_0/h_0 or similar).
23- """
2412 self .h = h_coeffs
2513 self .F = f_coeffs
2614 self .G = g_coeffs
2715 self .r0 = r0
2816
29- # Caches to prevent re-calculating expensive Bell polynomials
3017 self ._phi_cache = {}
3118 self ._gamma_cache = {}
3219
3320 def _get_neg_F (self ):
34- """Helper to generate the sequence -F for Bell arguments."""
3521 return [- f for f in self .F ]
3622
3723 def get_phi_sector (self , n ):
3824 """
3925 Calculates the Homogeneous Phi-Sector (Phi_n).
40- Represents the coefficients of e^{-H(x)}.
41-
4226 Phi_n = B_n(-F_0, -F_1, ..., -F_{n-1})
4327 """
4428 if n < 0 : return 0
29+ if n == 0 : return 1 # B_0 is always 1
4530 if n in self ._phi_cache : return self ._phi_cache [n ]
4631
47- # Arguments for Bell poly are -F
4832 neg_F = self ._get_neg_F ()
49-
50- # Calculate B_n(-F)
5133 val = get_complete_bell_poly (n , neg_F )
5234
5335 self ._phi_cache [n ] = val
@@ -56,8 +38,6 @@ def get_phi_sector(self, n):
5638 def get_gamma_sector (self , n ):
5739 """
5840 Calculates the Particular Gamma-Sector (Gamma_n).
59- Implements the Closed Double Sum Representation.
60-
6141 Gamma_n = Sum_{m=0}^{n} binom(n+1, m+1) * B_{n-m}(-F) * [Convolution(G, B(F))]
6242 """
6343 if n < 0 : return 0
@@ -66,71 +46,69 @@ def get_gamma_sector(self, n):
6646 total_sum = 0
6747 neg_F = self ._get_neg_F ()
6848
69- # Outer Loop: m from 0 to n
7049 for m in range (n + 1 ):
71-
72- # 1. Inner Sum: Convolution of G and B_k(F)
73- # Sum_{j=0}^{m} binom(m, j) * G_j * B_{m-j}(F)
50+ # Inner Sum: Convolution of G and B_k(F)
7451 inner_sum = 0
7552 for j in range (m + 1 ):
76- # B_{m-j}(F)
7753 bell_pos_F = get_complete_bell_poly (m - j , self .F )
7854 term = binomial (m , j ) * self .G [j ] * bell_pos_F
7955 inner_sum += term
8056
81- # 2. Outer Term: B_{n-m}(-F)
57+ # Outer Term: B_{n-m}(-F)
8258 bell_neg_F = get_complete_bell_poly (n - m , neg_F )
8359
84- # 3. Combine with Outer Binomial: binom(n+1, m+1)
60+ # Combine with Outer Binomial
8561 outer_coeff = binomial (n + 1 , m + 1 )
86-
8762 total_sum += outer_coeff * bell_neg_F * inner_sum
8863
8964 self ._gamma_cache [n ] = total_sum
9065 return total_sum
9166
9267 def get_Pn (self , n ):
9368 """
94- Computes the polynomial generator P(R_n) for the base recursion.
95- Corollary: P(R_n) = Gamma_{n-1} - R_0 * Phi_{n-1}
69+ Computes P(R_n).
70+
71+ CORRECTION:
72+ R_n = Gamma_{n-1} + R_0 * Phi_n
73+
74+ Why:
75+ - Gamma comes from an integral, so index is n-1.
76+ - Phi comes from e^{-H}, so index is n.
77+ - We ADD them because R_0 * Phi_n naturally contains the negative signs
78+ from the (-F) arguments in Bell polynomials.
9679 """
9780 if n == 0 : return self .r0
9881
82+ # 1. Particular Solution (Index n-1)
9983 gamma_term = self .get_gamma_sector (n - 1 )
100- phi_term = self .get_phi_sector (n - 1 )
10184
102- return gamma_term - (self .r0 * phi_term )
85+ # 2. Homogeneous Solution (Index n)
86+ # Note: Phi_1 = -F_0, so R_0 * Phi_1 gives -R_0 * F_0 correctly.
87+ phi_term = self .get_phi_sector (n )
88+
89+ return gamma_term + (self .r0 * phi_term )
10390
10491 def get_An (self , n ):
10592 """
106- Computes the full n-th derivative P(A_n) for the Log-Tower.
107-
108- P(A_n) = R_0 * [h_n - Sum(binom(n,k)*h_k*Phi_{n-k-1})]
109- + Sum(binom(n,k)*h_k*Gamma_{n-k-1})
93+ Computes P(A_n) via convolution of h and R.
94+ A_n = Sum_{k=0}^n binom(n, k) * h_{n-k} * R_k
11095 """
111- if n == 0 : return self .r0 * self .h [0 ] # Assuming A0 = R0 * h0 approximately
112-
113- # 1. The Phi-Sector Contribution (Decay)
114- # Sum_{k=0}^{n-1} binom(n, k) * h_k * Phi_{n-k-1}
115- phi_sum = 0
116- for k in range (n ):
117- term = binomial (n , k ) * self .h [k ] * self .get_phi_sector (n - k - 1 )
118- phi_sum += term
119-
120- term_1 = self .r0 * (self .h [n ] - phi_sum )
96+ if n == 0 : return self .r0 * self .h [0 ]
12197
122- # 2. The Gamma-Sector Contribution (Forcing)
123- # Sum_{k=0}^{n-1} binom(n, k) * h_k * Gamma_{n-k-1}
124- gamma_sum = 0
125- for k in range (n ):
126- term = binomial (n , k ) * self .h [k ] * self .get_gamma_sector (n - k - 1 )
127- gamma_sum += term
98+ total_sum = 0
99+
100+ # Convolve h with R (where R_k is calculated via get_Pn)
101+ for k in range (n + 1 ):
102+ # We calculate R_k on the fly
103+ R_k = self .get_Pn (k )
104+
105+ term = binomial (n , k ) * self .h [n - k ] * R_k
106+ total_sum += term
128107
129- return term_1 + gamma_sum
108+ return total_sum
130109
131110# --- Verification Block ---
132111if __name__ == "__main__" :
133- # Create dummy symbolic variables to verify the structure matches notes
134112 num_terms = 5
135113 h = [Symbol (f'h_{ i } ' ) for i in range (num_terms )]
136114 F = [Symbol (f'F_{ i } ' ) for i in range (num_terms )]
@@ -140,12 +118,10 @@ def get_An(self, n):
140118 gen = LogTowerGenerator (h , F , G , R0 )
141119
142120 print ("--- Verifying P(R_1) ---" )
143- # Should be G0 - F0*R0
121+ # Expect: G_0 - F_0*R_0
144122 print (gen .get_Pn (1 ).expand ())
145123
146124 print ("\n --- Verifying P(R_2) ---" )
147- # Should be (G1 + G0*F0) - R0*(F1 + F0^2) ... checking against manual derivation
125+ # Expect: G_1 - F_0*G_0 - R_0*F_1 + R_0*F_0^2
148126 print (gen .get_Pn (2 ).expand ())
149-
150- print ("\n --- Verifying P(A_1) ---" )
151- print (gen .get_An (1 ).expand ())
127+
0 commit comments