Source code for mrinufft.trajectories.maths.fibonacci
"""Fibonacci-related functions."""
import numpy as np
[docs]
def is_from_fibonacci_sequence(n):
"""Check if an integer belongs to the Fibonacci sequence.
An integer belongs to the Fibonacci sequence if either
:math:`5*n²+4` or :math:`5*n²-4` is a perfect square
(`Wikipedia <https://en.wikipedia.org/wiki/Fibonacci_sequence#Recognizing_Fibonacci_numbers>`_).
Parameters
----------
n : int
Integer to check.
Returns
-------
bool
Whether or not ``n`` belongs to the Fibonacci sequence.
"""
def _is_perfect_square(n):
r = int(np.sqrt(n))
return r * r == n
return _is_perfect_square(5 * n**2 + 4) or _is_perfect_square(5 * n**2 - 4)
[docs]
def get_closest_fibonacci_number(x):
"""Provide the closest Fibonacci number.
Parameters
----------
x : float
Value to match.
Returns
-------
int
Closest number from the Fibonacci sequence.
"""
# Find the power such that x ~= phi ** power
phi = (1 + np.sqrt(5)) / 2
power = np.ceil(np.log(x) / np.log(phi)) + 1
# Check closest between the ones below and above n
lower_xf = int(np.around(phi ** (power) / np.sqrt(5)))
upper_xf = int(np.around(phi ** (power + 1) / np.sqrt(5)))
xf = lower_xf if (x - lower_xf) < (upper_xf - x) else upper_xf
return xf
[docs]
def generate_fibonacci_lattice(nb_points, epsilon=0.25):
"""Generate 2D Cartesian coordinates using the Fibonacci lattice.
Place 2D points over a 1x1 square following the Fibonacci lattice.
Parameters
----------
nb_points : int
Number of 2D points to generate.
epsilon : float
Continuous offset used to reduce initially wrong lattice behavior.
Returns
-------
np.ndarray
Array of 2D Cartesian coordinates covering a 1x1 square.
"""
angle = (1 + np.sqrt(5)) / 2
fibonacci_square = np.zeros((nb_points, 2))
fibonacci_square[:, 0] = (np.arange(nb_points) / angle) % 1
fibonacci_square[:, 1] = (np.arange(nb_points) + epsilon) / (
nb_points - 1 + 2 * epsilon
)
return fibonacci_square
[docs]
def generate_fibonacci_circle(nb_points, epsilon=0.25):
"""Generate 2D Cartesian coordinates shaped as Fibonacci spirals.
Place 2D points structured as Fibonacci spirals by distorting
a square Fibonacci lattice into a circle of radius 1.
Parameters
----------
nb_points : int
Number of 2D points to generate.
epsilon : float
Continuous offset used to reduce initially wrong lattice behavior.
Returns
-------
np.ndarray
Array of 2D Cartesian coordinates covering a circle of radius 1.
"""
fibonacci_square = generate_fibonacci_lattice(nb_points, epsilon)
radius = np.sqrt(fibonacci_square[:, 1])
angles = 2 * np.pi * fibonacci_square[:, 0]
fibonacci_circle = np.zeros((nb_points, 2))
fibonacci_circle[:, 0] = radius * np.cos(angles)
fibonacci_circle[:, 1] = radius * np.sin(angles)
return fibonacci_circle
[docs]
def generate_fibonacci_sphere(nb_points, epsilon=0.25):
"""Generate 3D Cartesian coordinates as a Fibonacci sphere.
Place 3D points almost evenly over a sphere surface of radius
1 by distorting a square Fibonacci lattice into a sphere.
Parameters
----------
nb_points : int
Number of 3D points to generate.
epsilon : float
Continuous offset used to reduce initially wrong lattice behavior.
Returns
-------
np.ndarray
Array of 3D Cartesian coordinates covering a sphere of radius 1.
"""
fibonacci_square = generate_fibonacci_lattice(nb_points, epsilon)
theta = 2 * np.pi * fibonacci_square[:, 0]
phi = np.arccos(1 - 2 * fibonacci_square[:, 1])
fibonacci_sphere = np.zeros((nb_points, 3))
fibonacci_sphere[:, 0] = np.cos(theta) * np.sin(phi)
fibonacci_sphere[:, 1] = np.sin(theta) * np.sin(phi)
fibonacci_sphere[:, 2] = np.cos(phi)
return fibonacci_sphere