amirh0ss3in commited on
Commit
36c6a32
·
1 Parent(s): da57d25
rank_analysis_proof.py → rank_analysis_proof.nb RENAMED
File without changes
vec_temp.py CHANGED
@@ -1,168 +1,209 @@
1
  import sympy as sp
2
  import numpy as np
3
  import matplotlib.pyplot as plt
4
- from mpl_toolkits.mplot3d import Axes3D
5
- from matplotlib.widgets import Slider
6
 
7
- # ==============================================================================
8
- # Part 1: Symbolic Calculations (Expanded)
9
- # ==============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # 1. Define symbolic variables
12
  u, v, theta, a, b, gamma = sp.symbols('u v theta a b gamma')
13
  pi = sp.pi
14
 
15
- # 2. Define the Manifold Parameterization
16
- X = (a + gamma * sp.sin(u/4)) * sp.cos(v/2) + (b * sp.cos(theta) * sp.cos(u/4))
17
- Y = b * sp.cos(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 + sp.cos(v/2))/2) * \
18
- ((v/(2*pi))*(sp.cos(u/4) - 1) - u/(2*pi) + (u*v)/(4*pi**2) + 1)
19
- Z = b * sp.sin(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 - sp.cos(v/2))/2) * \
20
- ((v/(2*pi))*(1 - sp.cos(u/4)) + sp.cos(u/4) - (u*v)/(4*pi**2))
21
-
22
- # Position vector
23
- r = sp.Matrix([X, Y, Z])
24
-
25
- # 3. Define the Vector Field
26
- # Partial derivatives of the position vector
27
- r_v = sp.diff(r, v)
28
- r_theta = sp.diff(r, theta)
29
- # Sum of component-wise derivatives
30
- B_vec = sp.Matrix([
31
- sp.diff(X, v) + sp.diff(X, theta),
32
- sp.diff(Y, v) + sp.diff(Y, theta),
33
- sp.diff(Z, v) + sp.diff(Z, theta)
34
  ])
35
- B_mag = B_vec.norm() # Magnitude of the vector field ||B||
36
-
37
- # 4. Calculate the Surface Area Element
38
- # The cross product gives a vector normal to the surface
39
- cross_prod = r_v.cross(r_theta)
40
- # The magnitude of this vector is the differential area element dS
41
- dS_mag = cross_prod.norm()
42
-
43
- # 5. Define the full integrand for our numerical integral
44
- # We will integrate the magnitude of the vector field over the surface area
45
- integrand_expr = B_mag * dS_mag
46
-
47
- # ==============================================================================
48
- # Part 2: Numerical Conversion and Interactive Visualization
49
- # ==============================================================================
50
-
51
- # 1. Lambdify all necessary expressions for speed
52
- args = [u, v, theta, a, b, gamma]
53
- X_func = sp.lambdify(args, X, 'numpy')
54
- Y_func = sp.lambdify(args, Y, 'numpy')
55
- Z_func = sp.lambdify(args, Z, 'numpy')
56
- integrand_func = sp.lambdify(args, integrand_expr, 'numpy')
57
-
58
- # 2. Define constants and parameter ranges
59
- A_val = 5.0
60
- B_val = 2.0
61
- GAMMA_val = 1.5
62
- u_plot_range = np.linspace(0, 2, 40) # Range of u for the 2D plot
63
-
64
- # Create high-resolution grids for calculation and plotting
65
- v_res, theta_res = 100, 100
66
- v_range = np.linspace(0, 2 * np.pi, v_res)
67
- theta_range = np.linspace(0, 2 * np.pi, theta_res)
68
- v_grid, theta_grid = np.meshgrid(v_range, theta_range)
69
- dv = v_range[1] - v_range[0]
70
- dtheta = theta_range[1] - theta_range[0]
71
- dA = dv * dtheta # Area of one grid cell for integration
72
-
73
- # 3. Set up the plotting figure and axes
74
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8), width_ratios=[1.2, 1])
75
- ax1 = plt.subplot(1, 2, 1, projection='3d')
76
- ax2 = plt.subplot(1, 2, 2)
77
- fig.subplots_adjust(bottom=0.25) # Make space for sliders
78
-
79
- # 4. Helper function for numerical integration
80
- def calculate_integral(u_val, R_val):
81
- """Calculates the integral for a specific u and R."""
82
- # Calculate manifold coordinates
83
- X_surf = X_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
84
- Y_surf = Y_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
85
- Z_surf = Z_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
86
-
87
- # Condition: find which points are inside the sphere
88
- is_inside = (X_surf**2 + Y_surf**2 + Z_surf**2) < R_val**2
89
-
90
- # Calculate integrand values across the whole surface
91
- integrand_values = integrand_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
92
-
93
- # Apply the condition: only consider values inside the sphere
94
- integrand_masked = np.where(is_inside, integrand_values, 0)
95
 
96
- # Sum up the values and multiply by the differential area
97
- integral_result = np.sum(integrand_masked) * dA
98
- return integral_result
99
-
100
- # 5. Define the update functions for the plots
101
- def update_plots(val):
102
- """Updates both plots. Called by sliders."""
103
- u_current = u_slider.val
104
- R_current = R_slider.val
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- # --- Update 3D Plot (ax1) ---
107
- ax1.clear()
108
 
109
- # Calculate manifold coordinates for the current u
110
- X_surf = X_func(u_current, v_grid, theta_grid, A_val, B_val, GAMMA_val)
111
- Y_surf = Y_func(u_current, v_grid, theta_grid, A_val, B_val, GAMMA_val)
112
- Z_surf = Z_func(u_current, v_grid, theta_grid, A_val, B_val, GAMMA_val)
113
 
114
- # Plot the full manifold semi-transparently
115
- ax1.plot_surface(X_surf, Y_surf, Z_surf, color='c', alpha=0.2, rstride=5, cstride=5)
116
 
117
- # Highlight the part inside the sphere
118
- is_inside = (X_surf**2 + Y_surf**2 + Z_surf**2) < R_current**2
119
- X_inside = np.where(is_inside, X_surf, np.nan) # Set points outside to NaN so they are not plotted
120
- Y_inside = np.where(is_inside, Y_surf, np.nan)
121
- Z_inside = np.where(is_inside, Z_surf, np.nan)
122
- ax1.plot_surface(X_inside, Y_inside, Z_inside, alpha=0.8, rstride=5, cstride=5)
123
-
124
- # Draw the sphere wireframe
125
- phi, psi = np.mgrid[0:2*np.pi:10j, 0:np.pi:10j]
126
- x_sphere = R_current * np.sin(psi) * np.cos(phi)
127
- y_sphere = R_current * np.sin(psi) * np.sin(phi)
128
- z_sphere = R_current * np.cos(psi)
129
- ax1.plot_wireframe(x_sphere, y_sphere, z_sphere, color="r", alpha=0.3)
130
 
131
- # Aesthetics for 3D plot
132
- ax1.set_title(f"Manifold at u={u_current:.2f}, Sphere R={R_current:.2f}")
133
- ax1.set_xlabel("X"); ax1.set_ylabel("Y"); ax1.set_zlabel("Z")
134
- ax1.set_xlim([-8, 8]); ax1.set_ylim([-8, 8]); ax1.set_zlim([-4, 4])
135
-
136
- # --- Update 2D Plot (ax2) ---
137
- # This only needs to re-calculate if the radius changes, but it's simpler to update always.
138
- print(f"Recalculating integral curve for R = {R_current:.2f}...")
139
- integral_values = [calculate_integral(u_val, R_current) for u_val in u_plot_range]
140
 
141
- ax2.clear()
142
- ax2.plot(u_plot_range, integral_values, marker='o', linestyle='-')
143
- ax2.axvline(u_current, color='r', linestyle='--', label=f'Current u={u_current:.2f}')
144
- ax2.set_title(f"Integral vs. u (for Sphere Radius R={R_current:.2f})")
145
- ax2.set_xlabel("u")
146
- ax2.set_ylabel("Numerical Integral of ||B|| over surface inside sphere")
147
- ax2.grid(True)
148
- ax2.legend()
149
- ax2.set_xlim(0, 2)
150
 
151
- fig.canvas.draw_idle()
152
-
153
- # 6. Create the slider widgets
154
- ax_u_slider = fig.add_axes([0.2, 0.1, 0.65, 0.03])
155
- u_slider = Slider(ax=ax_u_slider, label='u value', valmin=0.0, valmax=2.0, valinit=0.0)
156
-
157
- ax_R_slider = fig.add_axes([0.2, 0.05, 0.65, 0.03])
158
- R_slider = Slider(ax=ax_R_slider, label='Sphere Radius R', valmin=0.1, valmax=8.0, valinit=4.0)
159
-
160
- # 7. Register the update function to the sliders
161
- u_slider.on_changed(update_plots)
162
- R_slider.on_changed(update_plots)
163
-
164
- # 8. Draw the initial plots
165
- update_plots(None)
166
-
167
- # 9. Show the plot window
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  plt.show()
 
1
  import sympy as sp
2
  import numpy as np
3
  import matplotlib.pyplot as plt
4
+ from scipy.integrate import dblquad
 
5
 
6
+ # --- RICH IMPORTS ---
7
+ from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn, TimeElapsedColumn, track
8
+
9
+ # --- NEW: Import joblib for parallel processing ---
10
+ from joblib import Parallel, delayed, cpu_count
11
+
12
+ import warnings
13
+
14
+ # Use this in a Jupyter/IPython environment for nicely formatted math output
15
+ sp.init_printing(use_unicode=True)
16
+
17
+ # Reduced for faster testing, feel free to increase it back to 100_000
18
+ n_samples = 20_000
19
+
20
+ # --- Part 1: Symbolic Definitions (Unchanged) ---
21
 
 
22
  u, v, theta, a, b, gamma = sp.symbols('u v theta a b gamma')
23
  pi = sp.pi
24
 
25
+ # Position Vector R
26
+ R_vec = sp.Matrix([
27
+ (a + gamma * sp.sin(u/4)) * sp.cos(v/2) + (b * sp.cos(theta) * sp.cos(u/4)),
28
+ b * sp.cos(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 + sp.cos(v/2))/2) * \
29
+ ((v/(2*pi))*(sp.cos(u/4) - 1) - u/(2*pi) + (u*v)/(4*pi**2) + 1),
30
+ b * sp.sin(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 - sp.cos(v/2))/2) * \
31
+ ((v/(2*pi))*(1 - sp.cos(u/4)) + sp.cos(u/4) - (u*v)/(4*pi**2))
 
 
 
 
 
 
 
 
 
 
 
 
32
  ])
33
+
34
+ # Vector Field B
35
+ B_vec = R_vec.diff(v) + R_vec.diff(theta)
36
+
37
+ print("Successfully defined symbolic Manifold and Vector Field B.")
38
+
39
+ # --- Part 2: Lambdify (Unchanged) ---
40
+
41
+ print("Converting to numerical functions...")
42
+
43
+ params = [u, v, theta, a, b, gamma]
44
+
45
+ f_R = sp.lambdify(params, R_vec, modules=[{'ImmutableDenseMatrix': np.array}, 'numpy'])
46
+ f_B = sp.lambdify(params, B_vec, modules=[{'ImmutableDenseMatrix': np.array}, 'numpy'])
47
+
48
+ print("Lambdification complete.")
49
+
50
+ # --- Part 3: Optimized Integrand (Unchanged) ---
51
+
52
+ a_val, b_val, gamma_val = 1.0, 1.5, 6
53
+ JACOBIAN_TOL = 1e-8
54
+ EPSILON = 1e-5
55
+
56
+ def integrand_vectorized(v_vals, theta_vals, u_val):
57
+ v_vals = np.atleast_1d(v_vals)
58
+ theta_vals = np.atleast_1d(theta_vals)
59
+ results = np.zeros_like(v_vals)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ for i, (v_val, theta_val) in enumerate(zip(v_vals, theta_vals)):
62
+ args = (u_val, v_val, theta_val, a_val, b_val, gamma_val)
63
+ try:
64
+ B_center = np.array(f_B(*args)).flatten()
65
+ R_center = np.array(f_R(*args)).flatten()
66
+ args_u = (u_val + EPSILON, v_val, theta_val, a_val, b_val, gamma_val)
67
+ args_v = (u_val, v_val + EPSILON, theta_val, a_val, b_val, gamma_val)
68
+ args_t = (u_val, v_val, theta_val + EPSILON, a_val, b_val, gamma_val)
69
+ B_u = np.array(f_B(*args_u)).flatten()
70
+ B_v = np.array(f_B(*args_v)).flatten()
71
+ B_t = np.array(f_B(*args_t)).flatten()
72
+ R_u = np.array(f_R(*args_u)).flatten()
73
+ R_v = np.array(f_R(*args_v)).flatten()
74
+ R_t = np.array(f_R(*args_t)).flatten()
75
+ grad_p_B = np.column_stack([(B_u - B_center) / EPSILON, (B_v - B_center) / EPSILON, (B_t - B_center) / EPSILON])
76
+ J_val = np.column_stack([(R_u - R_center) / EPSILON, (R_v - R_center) / EPSILON, (R_t - R_center) / EPSILON])
77
+ det = np.linalg.det(J_val)
78
+ if abs(det) < JACOBIAN_TOL:
79
+ results[i] = 0.0
80
+ continue
81
+ J_inv = np.linalg.inv(J_val)
82
+ grad_xyz_B = J_inv.T @ grad_p_B
83
+ div_B = np.trace(grad_xyz_B)
84
+ cross_prod = np.cross(J_val[:, 1], J_val[:, 2])
85
+ norm_val = np.linalg.norm(cross_prod)
86
+ if norm_val < JACOBIAN_TOL:
87
+ results[i] = 0.0
88
+ continue
89
+ result = div_B * norm_val
90
+ results[i] = result if np.isfinite(result) else 0.0
91
+ except Exception:
92
+ results[i] = 0.0
93
+ return results[0] if len(results) == 1 else results
94
+
95
+ def integrand(v_val, theta_val, u_val):
96
+ return integrand_vectorized(v_val, theta_val, u_val)
97
+
98
+ # --- Part 4: Integration with Parallel Monte Carlo ---
99
+
100
+ print("\nStarting integration...")
101
+
102
+ u_values = np.linspace(0, 2, 100)
103
+
104
+ # --- NEW: Define a worker function for parallel execution ---
105
+ # This function encapsulates the work for a *single* u_val.
106
+ # It takes all necessary parameters and returns the result tuple.
107
+ def worker_integrate(u_val, n_samples):
108
+ """
109
+ Performs Monte Carlo integration for a single u_val.
110
+ This function is designed to be called in parallel.
111
+ """
112
+ # Seed the random number generator differently for each process
113
+ # to ensure they don't all produce the same random numbers.
114
+ np.random.seed(int(u_val * 1000) + np.random.randint(0, 1000))
115
 
116
+ v_samples = np.random.uniform(0, 2 * np.pi, n_samples)
117
+ theta_samples = np.random.uniform(0, 2 * np.pi, n_samples)
118
 
119
+ # Using a list comprehension is slightly cleaner here
120
+ values = [integrand(v, t, u_val) for v, t in zip(v_samples, theta_samples)]
 
 
121
 
122
+ # Filter out any non-finite results (inf, NaN)
123
+ finite_values = [v for v in values if np.isfinite(v)]
124
 
125
+ if len(finite_values) == 0:
126
+ return 0.0, 0.0
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ # Monte Carlo estimate: (volume) * (average value)
129
+ volume = (2 * np.pi) * (2 * np.pi)
130
+ mean_val = np.mean(finite_values)
131
+ std_val = np.std(finite_values)
 
 
 
 
 
132
 
133
+ integral = volume * mean_val
134
+ error = volume * std_val / np.sqrt(len(finite_values))
 
 
 
 
 
 
 
135
 
136
+ return integral, error
137
+
138
+ # --- MODIFIED: The main integration loop is now parallel ---
139
+ # Use n_jobs=-1 to use all available CPU cores.
140
+ n_jobs = -1
141
+ num_cores = cpu_count()
142
+ print(f"Using parallel Monte Carlo integration on {num_cores} cores ({n_samples} samples per u)...")
143
+
144
+ # Create a list of "delayed" tasks, one for each u_value.
145
+ # 'delayed' is a wrapper that captures the function and its arguments.
146
+ tasks = [delayed(worker_integrate)(u, n_samples) for u in u_values]
147
+
148
+ # Execute the tasks in parallel. rich.progress.track provides the progress bar.
149
+ # Parallel(...) returns a list of the results from each worker call.
150
+ parallel_results = Parallel(n_jobs=n_jobs)(
151
+ track(tasks, description="[bold cyan]Integrating over u values")
152
+ )
153
+
154
+ # Unpack the list of (result, error) tuples into two separate lists.
155
+ integral_results, integral_errors = zip(*parallel_results)
156
+
157
+ print("\nIntegration finished.")
158
+ print(f"Average integration error: {np.mean(integral_errors):.2e}")
159
+ print(f"Max integration error: {np.max(integral_errors):.2e}")
160
+
161
+
162
+ # --- Part 5: Plotting (Unchanged) ---
163
+
164
+ plt.figure(figsize=(12, 6))
165
+
166
+ plt.subplot(1, 2, 1)
167
+ plt.plot(u_values, integral_results, marker='o', linestyle='-',
168
+ label='∫ (div B) dA (Monte Carlo)', linewidth=2, markersize=4)
169
+ plt.title('Integral of div(B) over Surface vs. u', fontsize=14)
170
+ plt.xlabel('u', fontsize=12)
171
+ plt.ylabel('Integral Value', fontsize=12)
172
+ plt.grid(True, alpha=0.3)
173
+ plt.legend()
174
+
175
+ plt.subplot(1, 2, 2)
176
+ plt.semilogy(u_values, integral_errors, marker='s', linestyle='-',
177
+ color='red', label='MC Error Estimate', markersize=4)
178
+ plt.title('Integration Error vs. u', fontsize=14)
179
+ plt.xlabel('u', fontsize=12)
180
+ plt.ylabel('Error (log scale)', fontsize=12)
181
+ plt.grid(True, alpha=0.3, which='both')
182
+ plt.legend()
183
+
184
+ plt.tight_layout()
185
+ plt.show()
186
+
187
+ # --- Part 6: 3D Visualization (Unchanged) ---
188
+
189
+ print("\nGenerating 3D visualization...")
190
+
191
+ u_vis = 2 * np.pi
192
+ v_grid, theta_grid = np.meshgrid(np.linspace(0, 2*np.pi, 30), np.linspace(0, 2*np.pi, 30))
193
+ X, Y, Z = f_R(u_vis, v_grid, theta_grid, a_val, b_val, gamma_val)
194
+
195
+ v_quiver, theta_quiver = np.meshgrid(np.linspace(0, 2*np.pi, 6), np.linspace(0, 2*np.pi, 6))
196
+ Xq, Yq, Zq = f_R(u_vis, v_quiver, theta_quiver, a_val, b_val, gamma_val)
197
+ Bxq, Byq, Bzq = f_B(u_vis, v_quiver, theta_quiver, a_val, b_val, gamma_val)
198
+
199
+ fig = plt.figure(figsize=(14, 10))
200
+ ax = fig.add_subplot(111, projection='3d')
201
+ surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis', rstride=1, cstride=1, edgecolor='none')
202
+ ax.quiver(Xq, Yq, Zq, Bxq, Byq, Bzq, length=0.3, normalize=True, color='red', arrow_length_ratio=0.3, linewidth=1.5)
203
+ ax.set_title(f'Manifold and Vector Field B at u = {u_vis/np.pi:.2f}π', fontsize=16)
204
+ ax.set_xlabel('X', fontsize=12)
205
+ ax.set_ylabel('Y', fontsize=12)
206
+ ax.set_zlabel('Z', fontsize=12)
207
+ ax.view_init(elev=20., azim=-65)
208
+ fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5)
209
  plt.show()