Add prime residue and density analysis utilities#2
Conversation
Lucidia Math - Advanced mathematical engines: forge/ - Mathematical Foundations: - consciousness.py (650 lines) - Consciousness modeling - unified_geometry.py (402 lines) - Geometric transformations - advanced_tools.py (356 lines) - Advanced utilities - main.py (209 lines) - CLI orchestration - numbers.py, proofs.py, fractals.py, dimensions.py lab/ - Experimental Mathematics: - unified_geometry_engine.py (492 lines) - Geometry engine - amundson_equations.py (284 lines) - Custom equations - iterative_math_build.py (198 lines) - Iterative construction - trinary_logic.py (111 lines) - Three-valued logic - prime_explorer.py (108 lines) - Prime exploration - quantum_finance.py (83 lines) - Quantum finance models 3,664 lines of Python. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Auto Deploy: Deploys to Railway/Cloudflare on PR and push - Auto Merge: Approves and merges PRs when CI passes - CI: Runs lint, tests, and builds 🤖 Generated by BlackRoad OS Automation
…i-cd.yml 🤖 Generated with BlackRoad Repo Buildout Master Co-Authored-By: Cece <noreply@blackroad.io>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR adds prime number analysis utilities for residue class counting and density profiling. The new functions enable analysis of how primes distribute across modular residue classes and how prime density compares to the Prime Number Theorem's predictions across intervals.
- Added
residue_class_counts()to count primes in each residue class modulo n - Added
prime_density_profile()to compute empirical vs predicted prime densities using the logarithmic integral - Added corresponding plotting functions
plot_residue_class_counts()andplot_density_profile() - Updated README with usage examples for the new analysis capabilities
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| lab/prime_explorer.py | Implements residue class counting and prime density profiling functions with matplotlib visualization utilities |
| README.md | Adds quick start examples demonstrating the new residue and density analysis functions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| backend_cfg = select_backend(backend) | ||
| xp = backend_cfg.array_module | ||
| counts = xp.zeros(mod, dtype=int) | ||
| for prime in sp.primerange(2, limit): | ||
| counts[int(prime % mod)] += 1 |
There was a problem hiding this comment.
The backend parameter is requested but not effectively utilized. While the function selects a backend and uses its array module to create the counts array, all prime computation happens in SymPy's primerange, and the result is immediately converted back to numpy. This means the backend parameter has minimal impact on the actual computation. Consider either removing the backend parameter or documenting that it only affects the intermediate array storage.
| backend_cfg = select_backend(backend) | ||
| xp = backend_cfg.array_module | ||
| starts = xp.arange(2, limit, window) | ||
| empirical: list[float] = [] | ||
| predicted: list[float] = [] | ||
|
|
||
| for start in starts: | ||
| end = min(int(start + window), limit) | ||
| primes = list(sp.primerange(int(start), end)) | ||
| count = len(primes) | ||
| empirical.append(count / (end - int(start))) | ||
| predicted.append(float(sp.li(end) - sp.li(int(start))) / (end - int(start))) |
There was a problem hiding this comment.
The backend parameter is requested but not effectively utilized. The function creates a starts array with the selected backend, but then iterates over it and converts each element to int, performs all prime calculations using SymPy, and stores results in Python lists before converting everything back to numpy. This means the backend parameter has no meaningful effect on the computation. Consider either removing the backend parameter or restructuring to use the backend's array operations more effectively.
| centers = np.asarray(starts + window / 2.0, dtype=float) | ||
| return np.asarray(centers), np.asarray(empirical), np.asarray(predicted) |
There was a problem hiding this comment.
The centers calculation performs array addition with the starts array from the selected backend, but immediately converts it to numpy with dtype=float. This creates an unnecessary intermediate array. Consider simplifying by computing centers directly as a numpy array, especially since the backend selection has minimal impact on the overall computation.
| centers = np.asarray(starts + window / 2.0, dtype=float) | |
| return np.asarray(centers), np.asarray(empirical), np.asarray(predicted) | |
| centers = np.arange(2, limit, window, dtype=float) + window / 2.0 | |
| return centers, np.asarray(empirical), np.asarray(predicted) |
| x: np.ndarray, empirical: np.ndarray, predicted: np.ndarray | ||
| ) -> plt.Figure: | ||
| fig, ax = plt.subplots(figsize=(7, 4)) | ||
| ax.plot(x, empirical, label="empirical", marker="o", linestyle="-") | ||
| ax.plot(x, predicted, label="PNT estimate", linestyle="--") |
There was a problem hiding this comment.
The parameter name 'x' is ambiguous. Based on the docstring of prime_density_profile which describes it as 'sample positions' and the usage in the plot (as x-axis with label 'Sample center'), a more descriptive name like 'centers' or 'sample_centers' would improve clarity and consistency with the terminology used in the related function.
| x: np.ndarray, empirical: np.ndarray, predicted: np.ndarray | |
| ) -> plt.Figure: | |
| fig, ax = plt.subplots(figsize=(7, 4)) | |
| ax.plot(x, empirical, label="empirical", marker="o", linestyle="-") | |
| ax.plot(x, predicted, label="PNT estimate", linestyle="--") | |
| centers: np.ndarray, empirical: np.ndarray, predicted: np.ndarray | |
| ) -> plt.Figure: | |
| fig, ax = plt.subplots(figsize=(7, 4)) | |
| ax.plot(centers, empirical, label="empirical", marker="o", linestyle="-") | |
| ax.plot(centers, predicted, label="PNT estimate", linestyle="--") |
|
|
||
| The function walks the integer line in windows and reports the observed | ||
| density of primes along with the prime number theorem approximation | ||
| using the logarithmic integral :math:`Li(x)`. |
There was a problem hiding this comment.
The mathematical notation uses :math:Li(x) but the actual SymPy function being called is sp.li(), which computes the logarithmic integral li(x), not Li(x). While these are closely related functions (Li(x) = li(x) - li(2)), the documentation should either clarify this distinction or use the correct notation :math:li(x) to match the implementation.
| using the logarithmic integral :math:`Li(x)`. | |
| using the logarithmic integral :math:`li(x)`. |
| primes = list(sp.primerange(int(start), end)) | ||
| count = len(primes) | ||
| empirical.append(count / (end - int(start))) | ||
| predicted.append(float(sp.li(end) - sp.li(int(start))) / (end - int(start))) | ||
|
|
There was a problem hiding this comment.
Potential division by zero error when the last window is very small. If 'start' is very close to 'limit', then 'end - int(start)' could be zero or negative, causing a ZeroDivisionError on lines 183-184. This can occur when 'limit - start' is less than 1. Consider adding a check to skip windows where 'end <= int(start)' or ensure a minimum window size.
| primes = list(sp.primerange(int(start), end)) | |
| count = len(primes) | |
| empirical.append(count / (end - int(start))) | |
| predicted.append(float(sp.li(end) - sp.li(int(start))) / (end - int(start))) | |
| if end <= int(start): | |
| continue | |
| primes = list(sp.primerange(int(start), end)) | |
| count = len(primes) | |
| empirical.append(count / (end - int(start))) | |
| predicted.append(float(sp.li(end) - sp.li(int(start))) / (end - int(start))) |
bff3596 to
e950bff
Compare
Summary
Testing
Codex Task