Skip to content

Commit 76a5af9

Browse files
committed
Fix index alignment for P(Rn) and verify R3 output
1 parent 3daa76e commit 76a5af9

4 files changed

Lines changed: 36 additions & 60 deletions

File tree

166 Bytes
Binary file not shown.
1.39 KB
Binary file not shown.
5.25 KB
Binary file not shown.

src/generator.py

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ---
132111
if __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

Comments
 (0)