You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The _wigner_discretized_iterative function calculates the Wigner function iteratively,
which is essential for simulating quantum states in continuous variable quantum mechanics.
The original implementation had redundant operations, recalculations, and excessive memory
allocations, leading to performance bottlenecks. The make_grid function was also optimized
with @njit to further enhance performance. This update improves both mathematical efficiency
and memory management.
Description of the Change:
Precompute Exponential Terms: The exponential grid is computed only once and reused to
avoid redundant calculations in each iteration.
Precompute Square Roots: All square roots required for the computation are calculated at
the beginning and stored in an array. This reduces overhead inside nested loops.
Efficient Matrix Swapping: Instead of creating new matrices in every loop iteration, the
function reuses memory by swapping matrices, which reduces memory allocations.
Optimized make_grid Function:
The make_grid function was updated to use @njit to accelerate its performance.
Memory allocations for the Q and P coordinate matrices are optimized to reduce overhead.
A precomputed sqrt_factor is used to avoid redundant calculations inside nested loops.
By leveraging @njit, the function is now compliant with Numba’s JIT compilation, allowing
faster and more efficient execution.
Benefits:
Improved Mathematical Efficiency:
The function minimizes the number of floating-point operations inside loops by precomputing
values such as exponential terms and square roots.
The iterative Wigner calculation avoids recalculating these values multiple times, making
the function more computationally efficient.
Memory-intensive operations are reduced by swapping matrices rather than allocating new ones,
ensuring cache-friendlier execution.
Better Performance:
Memory Reuse: By reusing the same memory blocks with matrix swapping, the function avoids
costly memory allocations, improving speed.
Optimized make_grid: Using @njit in make_grid improves both speed and memory efficiency,
enabling seamless integration with the rest of the optimized functions.
Numba Compliance:
The optimized function ensures type safety and alignment with Numba’s @njit constraints,
avoiding common type-related issues.
Possible Drawbacks:
Increased Memory Usage at Startup: Precomputing the square roots and exponential grid slightly
increases memory usage at the beginning, but this is offset by performance gains during execution.
Related GitHub Issues:
NA
PR Type
Enhancement
Description
Optimized the iterative Wigner function computation for efficiency
Precomputed exponential and square root terms to reduce redundant calculations
Improved memory usage by reusing and swapping matrices
Enhanced make_grid function for better performance and Numba compatibility
Used explicit loops and precomputed factors for faster grid creation
Changes walkthrough 📝
Relevant files
Enhancement
wigner.py
Optimize Wigner function and grid computation for speed and memory
mrmustard/physics/wigner.py
Refactored make_grid to use explicit loops and precomputed factors for Numba JIT
Precomputed exponential and square root terms in _wigner_discretized_iterative
Reduced memory allocations by swapping matrices instead of creating new ones
Improved overall mathematical and computational efficiency of Wigner calculation
jk20342
changed the title
Update wigner.py
Optimize Wigner Function and Improve make_grid with @njit for Better Performance and Memory Efficiency #2
May 16, 2025
qodo-code-reviewBot
changed the title
Optimize Wigner Function and Improve make_grid with @njit for Better Performance and Memory Efficiency #2
Update wigner.py
May 16, 2025
There's a potential numerical instability when m is very small, as division by sqrt_n[m] could lead to large values. Add a check to handle the case where sqrt_n[m] is close to zero to prevent potential overflow.
# Compute the remaining coefficients and accumulate the Wigner function.
for m in range(1, cutoff):
- Wmat[1, m] = (2 * np.conj(grid) * Wmat[0, m] - sqrt_n[m] * Wmat[0, m - 1]) / sqrt_n[m]+ if sqrt_n[m] > 1e-10: # Avoid division by very small numbers+ Wmat[1, m] = (2 * np.conj(grid) * Wmat[0, m] - sqrt_n[m] * Wmat[0, m - 1]) / sqrt_n[m]+ else:+ Wmat[1, m] = 2 * np.conj(grid) * Wmat[0, m] # Simplified when sqrt(m) ≈ 0
W += rho[m, m].real * Wmat[1, m].real
Apply / Chat
Suggestion importance[1-10]: 7
__
Why: Adding a check to avoid division by very small numbers in sqrt_n[m] is a reasonable precaution for numerical stability, though in practice m starts at 1 and sqrt(1) is not problematic. The suggestion is valid but its practical impact is limited.
Medium
Validate input parameter
The function should validate that hbar is positive before computing sqrt_factor to prevent potential runtime errors or complex number results when calculating the square root of a negative number.
@njit
def make_grid(q_vec: np.ndarray, p_vec: np.ndarray, hbar: float):
r"""Returns two coordinate matrices `Q` and `P` from coordinate vectors
`q_vec` and `p_vec`, along with the grid over which Wigner functions can be
discretized.
"""
+ if hbar <= 0:+ raise ValueError("hbar must be positive")
n_q = q_vec.size
n_p = p_vec.size
Q = np.empty((n_q, n_p), dtype=np.float64)
P = np.empty((n_q, n_p), dtype=np.float64)
sqrt_factor = 1.0 / np.sqrt(2.0 * hbar)
Apply / Chat
Suggestion importance[1-10]: 7
__
Why: Adding input validation for hbar being positive is a good defensive programming practice, preventing potential runtime errors or unexpected behavior. This improves robustness but is not critical for correctness if the function is always called with valid hbar.
Medium
General
Use precomputed value directly
The initialization of W should use exp_grid directly instead of Wmat[0, 0].real since they are identical. This avoids unnecessary memory access and ensures consistency with the precomputed value.
# Precompute the exponential term to avoid recalculating it.
exp_grid = np.exp(-2.0 * np.abs(grid) ** 2) / np.pi
# Initialize Wmat and W with the |0><0| component.
Wmat[0, 0] = exp_grid
-W = rho[0, 0].real * Wmat[0, 0].real+W = rho[0, 0].real * exp_grid.real
Apply / Chat
Suggestion importance[1-10]: 4
__
Why: This suggestion makes a minor optimization by using the already computed exp_grid instead of accessing Wmat[0, 0].real, which is functionally identical. The impact on correctness and performance is minimal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
Optimized Wigner Function Iterative Computation
Context:
The
_wigner_discretized_iterativefunction calculates the Wigner function iteratively,which is essential for simulating quantum states in continuous variable quantum mechanics.
The original implementation had redundant operations, recalculations, and excessive memory
allocations, leading to performance bottlenecks. The
make_gridfunction was also optimizedwith
@njitto further enhance performance. This update improves both mathematical efficiencyand memory management.
Description of the Change:
avoid redundant calculations in each iteration.
the beginning and stored in an array. This reduces overhead inside nested loops.
function reuses memory by swapping matrices, which reduces memory allocations.
make_gridFunction:make_gridfunction was updated to use@njitto accelerate its performance.QandPcoordinate matrices are optimized to reduce overhead.sqrt_factoris used to avoid redundant calculations inside nested loops.@njit, the function is now compliant with Numba’s JIT compilation, allowingfaster and more efficient execution.
Benefits:
Improved Mathematical Efficiency:
values such as exponential terms and square roots.
the function more computationally efficient.
ensuring cache-friendlier execution.
Better Performance:
costly memory allocations, improving speed.
make_grid: Using@njitinmake_gridimproves both speed and memory efficiency,enabling seamless integration with the rest of the optimized functions.
Numba Compliance:
@njitconstraints,avoiding common type-related issues.
Possible Drawbacks:
increases memory usage at the beginning, but this is offset by performance gains during execution.
Related GitHub Issues:
NA
PR Type
Enhancement
Description
Optimized the iterative Wigner function computation for efficiency
Enhanced
make_gridfunction for better performance and Numba compatibilityChanges walkthrough 📝
wigner.py
Optimize Wigner function and grid computation for speed and memorymrmustard/physics/wigner.py
make_gridto use explicit loops and precomputed factors forNumba JIT
_wigner_discretized_iterativenew ones
calculation