I am doing some matrix computation with sympy 0.7.6 in python 2.7.10. For example,
M =
[cos(q1), -6.12323399573677e-17*sin(q1), -1.0*sin(q1), 150*sin(q1)]
[sin(q1), 6.12323399573677e-17*cos(q1), 1.0*cos(q1), 150*sin(q1)]
[ 0, -1.0, 6.12323399573677e-17, 445]
[ 0, 0, 0, 1]
Then I apply simplify to M and it results in:
M =
[cos(q1), 0, -1.0*sin(q1), 150*sin(q1)]
[sin(q1), 0, 1.0*cos(q1), 150*sin(q1)]
[ 0, -1.0, 6.12323399573677e-17, 445]
[ 0, 0, 0, 1]
It's clear that -6.12323399573677e-17*sin(q1) is simplified to 0 but 6.12323399573677e-17 is not. Is it possible to simplify the pure number item with simplify?
Sympy’s nsimplify function with the rational=True argument converts floats within an expression to rational numbers (within a given tolerance). Something like 6.12323399573677e-17 will be converted to 0 if below the threshold. So, in your case:
from sympy import Symbol, Matrix, sin, cos, nsimplify
q1 = Symbol("q1")
M = Matrix([
[cos(q1), -6.12323e-17*sin(q1), 1.0*sin(q1), 150*sin(q1)],
[sin(q1), 6.12323e-17*cos(q1), 1.0*cos(q1), 150*sin(q1)],
[ 0, -1.0, 6.123233e-17, 445],
[ 0, 0, 0, 1],
])
nsimplify(M,tolerance=1e-10,rational=True)
# Matrix([
# [cos(q1), 0, -sin(q1), 150*sin(q1)],
# [sin(q1), 0, cos(q1), 150*sin(q1)],
# [ 0, -1, 0, 445],
# [ 0, 0, 0, 1]])
Note how this also converted -1.0 to -1.
If you are using a Matrix (sympy.matrices.dense.MutableDenseMatrix), including one with sybolic elements, the conversion can be done with the following function:
def round2zero(m, e):
for i in range(m.shape[0]):
for j in range(m.shape[1]):
if (isinstance(m[i,j], Float) and m[i,j] < e):
m[i,j] = 0
For example:
from sympy import *
e = .0000001 # change according to your definition of small
x, y, z = symbols('x y z')
mlist = [[0.0, 1.0*cos(z)], [x*y, 1.05000000000000], [0, 6.12323399573677e-17]]
m = Matrix(mlist)
m
Out[4]:
Matrix([
[0.0, 1.0*cos(z)],
[x*y, 1.05],
[ 0, 6.12323399573677e-17]])
round2zero(m,e)
m
Matrix([
[ 0, 1.0*cos(z)],
[x*y, 1.05],
[ 0, 0]])
Some fuzzing can help detect more values that can be set to zero:
import sympy
import random
def fuzz_simplify(matrix, min=-1.0, max=1.0, iterations=1000, tolerance=0.005):
m = sympy.Matrix(matrix)
free_sym = range(len(J.free_symbols))
f = sympy.lambdify(m.free_symbols,m)
sum = f(*[0 for i in free_sym])
for i in range(0, iterations):
rand_params = [random.uniform(min,max) for i in free_sym]
sum += f(*rand_params)
for i in range(0, J.shape[0]):
for j in range(0, J.shape[1]):
if sum[i,j] < tolerance:
m[i,j] *= 0
return m
Related
I'm following the code presented here:
Compute homography for a virtual camera with opencv
As a note, I made a tiny modification to the code: in the translation matrix, I'm left-multiplying the first 3 rows of the last column by -R to get the translation in the global frame. I also changed the translation matrix definition to use "-dist" because in the global frame, movement toward the camera would be in the negative z direction.
When I turn the X rotation to 0, I get a weird... aliased version of the loaded image that appears ABOVE the horizon line, where there should be nothing.
My question:
Why? Is this just a weird artifact of how the homography is calculated? How can I get rid of it? I know for x=0 (in the presented code) I can just ignore/erase anything above the horizon line, but my use case the x rotation might be -10 to 10 degrees or so - how can I calculate where the horizon line would be in those cases (so I can ignore image data above it) - or is there a mathematical solution the computing the homography that will bypass this problem all together?
Thanks!
EDIT: Adding in code/image in question:
import cv2
import numpy as np
rotXdeg = 90
rotYdeg = 90
rotZdeg = 90
f = 500
dist = 500
def onRotXChange(val):
global rotXdeg
rotXdeg = val
def onRotYChange(val):
global rotYdeg
rotYdeg = val
def onRotZChange(val):
global rotZdeg
rotZdeg = val
def onFchange(val):
global f
f=val
def onDistChange(val):
global dist
dist=val
if __name__ == '__main__':
#Read input image, and create output image
src = cv2.imread('/path/to/image.jpg')
dst = np.ndarray(shape=src.shape,dtype=src.dtype)
#Create user interface with trackbars that will allow to modify the parameters of the transformation
wndname1 = "Source:"
wndname2 = "WarpPerspective: "
cv2.namedWindow(wndname1, 1)
cv2.namedWindow(wndname2, 1)
cv2.createTrackbar("Rotation X", wndname2, rotXdeg, 180, onRotXChange)
cv2.createTrackbar("Rotation Y", wndname2, rotYdeg, 180, onRotYChange)
cv2.createTrackbar("Rotation Z", wndname2, rotZdeg, 180, onRotZChange)
cv2.createTrackbar("f", wndname2, f, 2000, onFchange)
cv2.createTrackbar("Distance", wndname2, dist, 2000, onDistChange)
#Show original image
cv2.imshow(wndname1, src)
h , w = src.shape[:2]
while True:
rotX = (rotXdeg - 90)*np.pi/180
rotY = (rotYdeg - 90)*np.pi/180
rotZ = (rotZdeg - 90)*np.pi/180
#Projection 2D -> 3D matrix
A1= np.matrix([[1, 0, -w/2],
[0, 1, -h/2],
[0, 0, 0 ],
[0, 0, 1 ]])
# Rotation matrices around the X,Y,Z axis
RX = np.matrix([[1, 0, 0, 0],
[0,np.cos(rotX),-np.sin(rotX), 0],
[0,np.sin(rotX),np.cos(rotX) , 0],
[0, 0, 0, 1]])
RY = np.matrix([[ np.cos(rotY), 0, np.sin(rotY), 0],
[ 0, 1, 0, 0],
[ -np.sin(rotY), 0, np.cos(rotY), 0],
[ 0, 0, 0, 1]])
RZ = np.matrix([[ np.cos(rotZ), -np.sin(rotZ), 0, 0],
[ np.sin(rotZ), np.cos(rotZ), 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1]])
#Composed rotation matrix with (RX,RY,RZ)
R = RX * RY * RZ
#Translation matrix on the Z axis change dist will change the height
T = np.matrix([[1,0,0,0],
[0,1,0,0],
[0,0,1,-dist],
[0,0,0,1]])
extractT = T[:3, 3:4]
solveT = -R[:3, :3]#extractT
T[:3, 3:4] = solveT
#Camera Intrisecs matrix 3D -> 2D
A2= np.matrix([[f, 0, w/2,0],
[0, f, h/2,0],
[0, 0, 1,0]])
# Final and overall transformation matrix
H = A2 * (T * (R * A1))
# Apply matrix transformation
cv2.warpPerspective(src, H, (w, h), dst, cv2.INTER_CUBIC)
#Show the image
cv2.imshow(wndname2, dst)
if (cv2.waitKey(1) == ord('q')):
break
Image:
I'm implementing a Kalman filter for an 2D tracked object. I'm measuring the position and the velocity of the object. For the moment, I assume I have all the data from the sensors at the same time, so my observation matrix H is
H = eye(4,4), a 4x4 identity matrix. (See code below)
However, in my final implementation I will have the data from the sensors at different times. So in some update loops I will have the velocity, and in others I will have the position. How would I write the H matrix in those cases?
Is it okay to write
[position loop]
[1, 0, 0, 0 ]
[0, 1, 0, 0 ]
[0, 0, 0, 0 ]
[0, 0, 0, 0 ]
[velocity loop]
[0, 0, 0, 0 ]
[0, 0, 0, 0 ]
[0, 0, 1, 0 ]
[0, 0, 0, 1 ]
Note that my state space variables are [x, y, vx, vy]
I wonder if using those matrices does not imply that my observations are zero, or something like that.
Can I leave the covariances matrices untouched? I guess not.
#Implementation of 2D filter with FilterPy.
import numpy as np
from filterpy.kalman import KalmanFilter
from filterpy.common import Q_discrete_white_noise
import matplotlib.pyplot as plt
# --------- PARAM -----------
dt = 0.1
v_dev = 0.3
pos_dev = 0.8
duration = 50
acceleration_noise = 0.3
# --------- MODEL ------------
transition_matrix = [[1,0,dt,0],[0,1,0,dt],[0,0,1,0],[0,0,0,1]]
transition_covariance = np.array([
[ 0.25*pow(dt, 4), 0, 0.5* pow(dt, 3), 0 ],
[ 0, 0.25*pow(dt, 4), 0, 0.5* pow(dt, 3)],
[ 0.5* pow(dt, 3), 0, dt*dt, 0],
[ 0, 0.5*dt*dt*dt, 0, dt*dt]]) * acceleration_noise *acceleration_noise # A large process noise favors the measurements. ()
#Transition matrix with acceleration componentn
observation_matrix = np.eye(4, 4)
initial_state = [0, 0, 0.5, 0.5]
initial_state_covariance = [[ pos_dev*pos_dev, 0, 0 ,0],[0, pos_dev*pos_dev, 0, 0],[0, 0, v_dev * v_dev, 0 ],[0, 0, 0, v_dev * v_dev ]]
observation_covariance = [[pos_dev * pos_dev , 0, 0 ,0],[0, pos_dev * pos_dev, 0, 0],[0, 0, v_dev * v_dev, 0 ],[0, 0, 0, v_dev * v_dev ]]
#-----------------------------
#---------- FAKE DATA ---------
ind = np.array( range( round(duration/dt) ) )
time = ind * dt
position = np.zeros( (2, len(ind)) )
position[0,:] = time
position[1,:] = 3 * np.sin(time)
noise = pos_dev * np.random.randn(2, len(ind))
noisy_pos = position + noise
vel = position[:,1:len(ind)] - position[:,0:len(ind)-1]
vel = vel / dt
vel_ind = np.zeros( (2, len(ind) -1 ) )
vel_ind[0,:] = position[0,0:len(ind)-1]
vel_ind[1,:] = position[1,0:len(ind)-1]
vel_noise = v_dev * np.random.randn(2, len(ind) - 1 )
noisy_vel = vel + vel_noise
observations = np.zeros((len(ind), 4))
observations[:,[0,1]] = np.transpose(noisy_pos)
observations[1:len(ind),[2,3]] = np.transpose(noisy_vel)
observations[0,[2,3]] = np.transpose(noisy_vel[[0,1],0] )
# KALMAN!
filtered_state_means = np.zeros((len(time), 4))
filtered_state_covariances = np.zeros( ( len(time), 4, 4) )
kf = KalmanFilter( dim_x = 4, dim_z = 4) # state space: x, y, vx, vy, measuring all
kf.x = np.array( initial_state )
kf.F = np.array( transition_matrix )
kf.H = np.array( observation_matrix )
kf.P = np.array( initial_state_covariance )
kf.Q = np.array( transition_covariance )
kf.R =np.array( observation_covariance ) #measurement covariance
for i in range(0, len(time) ):
# Ommitting some data points
if( i > no_gps_start and i < no_gps_end):
# No data from gps
kf.H = np.array( ([0, 0, 0, 0],[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) )
else:
kf.H = observation_matrix
kf.predict()
kf.update(observations[i])
filtered_state_means[i] = kf.x
filtered_state_covariances[i] = kf.P
# Plotting everything
xmin = -2
xmax = 22
ymin = -4.3
ymax = 4.3
axisLimits = [xmin, xmax, ymin, ymax]
plt.figure(1)
plt.plot( position[0,:], position[1,:], linewidth=1 , color= '0.7')
plt.plot( noisy_pos[0,:], noisy_pos[1,:], '.')
plt.axis( axisLimits )
plt.figure(2)
plt.plot( position[0,:], position[1,:], linewidth=1 , color= '0.7')
plt.quiver( vel_ind[0,:], vel_ind[1,:], noisy_vel[0,:], noisy_vel[1,:], angles='xy', scale_units='xy', scale=10)
plt.axis( axisLimits )
plt.figure(3)
plt.plot( position[0,:], position[1,:], linewidth=1 , color= '0.7', zorder= 1)
plt.plot( filtered_state_means[:,0], filtered_state_means[:,1], linewidth = 1, zorder= 2)
plt.plot( noisy_pos[0,:], noisy_pos[1,:], '.', color = "#fd92f8", zorder= 0)
plt.plot( no_gps_x, no_gps_y, 'ro')
plt.show()
You are right, you are not allowed to modify the observation matrix in this way.
In your case the best solution would be a sequential Kalman Filter, which was developed exactly for handling of missing measurements. The measurement vector is replaced through a sequence of separate scalar measurements. The filter can proceed them independently and is not corrupted if one or more measurements do not exist at some point in time.
Have a look at Dan Simon's "Optimal State Estimation" Chapter 6.1 (you can try to find the book online). He derives alternative equations for the Kalman Filter, which are pretty easy to implement. The prediction step stays the same, you need to modify the update step.
Pros:
you don't need to compute the inverse matrix at all (nice for embedded systems)
if your H matrix has a lot of zeros the equivalent sequential expressions are very short and computationally efficient
Contras:
the R matrix (measurement covariance) has to be diagonal
I have a task to check if a matrix is a rotation matrix, I write code as follow:
import numpy as np
def isRotationMatrix(R):
# some code here
# return True or False
R = np.array([
[0, 0, 1],
[1, 0, 0],
[0, 1, 0],
])
print(isRotationMatrix(R)) # Should be True
R = np.array([
[-1, 0, 0],
[0, 1, 0],
[0, 0, 1],
])
print(isRotationMatrix(R)) # Should be False
I don't know how to implement the function isRotationMatrix.
My naive implement, it only works for a 3x3 matrix:
def isRotationMatrix(R_3x3):
should_be_norm_one = np.allclose(np.linalg.norm(R_3x3, axis=0), np.ones(shape=3))
x = R_3x3[:, 0].ravel()
y = R_3x3[:, 1].ravel()
z = R_3x3[:, 2].ravel()
should_be_perpendicular = \
np.allclose(np.cross(x, y), z) \
and np.allclose(np.cross(y, z), x) \
and np.allclose(np.cross(z, x), y)
return should_be_perpendicular and should_be_norm_one
I am using this definition of rotation matrix. A rotation matrix should satisfy the conditions M (M^T) = (M^T) M = I and det(M) = 1. Here M^T denotes transpose of M, I denotes identity matrix and det(M) represents determinant of matrix M.
You can use the following python code to check if the matrix is a rotation matrix.
import numpy as np
''' I have chosen `M` as an example. Feel free to put in your own matrix.'''
M = np.array([[0,-1,0],[1,0,0],[0,0,1]])
def isRotationMatrix(M):
tag = False
I = np.identity(M.shape[0])
if np.all((np.matmul(M, M.T)) == I) and (np.linalg.det(M)==1): tag = True
return tag
if(isRotationMatrix(M)): print 'M is a rotation matrix.'
else: print 'M is not a rotation matrix.'
A rotation matrix is a orthonormal matrix and its determinant should be 1.
My implement:
import numpy as np
def isRotationMatrix(R):
# square matrix test
if R.ndim != 2 or R.shape[0] != R.shape[1]:
return False
should_be_identity = np.allclose(R.dot(R.T), np.identity(R.shape[0], np.float))
should_be_one = np.allclose(np.linalg.det(R), 1)
return should_be_identity and should_be_one
if __name__ == '__main__':
R = np.array([
[0, 0, 1],
[1, 0, 0],
[0, 1, 0],
])
print(isRotationMatrix(R)) # True
R = np.array([
[-1, 0, 0],
[0, 1, 0],
[0, 0, 1],
])
print(isRotationMatrix(R)) # True
print(isRotationMatrix(np.zeros((3, 2)))) # False
The problem is as follows:
I have an equation: A*x = 0, where A is matrix 8x8, x is vector with 8 elements, and 0 means zero vector. Elements of matrix A contain a parameter E whose values for which the equation is solvable I have to find - and I already did it by using the condition: det(A)=0. And there is the source of my problem - det(A) for found values E is not exactly 0.0 but something very, very near 0.0, for example 9.5e-12. I interpret it as a numerical issue, but I may be wrong?
Next step is to find x. My concept was to find the kernel of the matrix A, but there my problem with non zero det(A) returns, because I do not have E for which my equation can be solved. Is it any way to force Python to operate with approximate values?
Summarizing: I need to find method to determine kernel when A*x is not exactly 0.0 but value is very, very near 0.0.
Edit:
Value of matrix:
[[6.60489454233399, -0.000899873003155720, -1111.26791946547, 0, 0, 0, 0, 0], [8.46748025849121e-8 + 2.5809235665861e-7*I, -9.08063389853990e-10, 0.00112138236223004, 0, 0, 0, 0, 0],
[0, 0.635021913463456, 1.57474880598360, -1.95244188971305, -0.512179135916290, 0, 0, 0],
[0, 6.40801701294518e-7, -1.58908171921515e-6, 2.90298102267184e-6, -7.61531659204450e-7, 0, 0, 0],
[0, 0, 0, 0.512179135916290, 1.95244188971305, -1.57474880598360, -0.635021913463456, 0],
[0, 0, 0, -7.61531659204450e-7, 2.90298102267184e-6, -1.58908171921515e-6, 6.40801701294518e-7, 0],
[0, 0, 0, 0, 0, 784198.968204183, 1.27518657961257e-6, -38.6053002011412],
[0, 0, 0, 0, 0, 0.791336522920740, -1.28679296313874e-12, -1.04862603277821e-5]])
And the code example:
from scipy import *
from numpy.linalg import *
from sympy import *
import sys
import numpy
import cmath
import math
a2= 65e5 #Ae-5
a3= 9e5 #Ae-5
a4= 130e5 #Ae-5
l = -a2-a3/2.0
t = -a3/2.0
f = a3/2.0
g = a4+a3/2.0
print 'Defined thickness'
V1= 0.74015 #eV fabs
V2= 1.1184 #eV fabs
V3= 0.74015 #eV fabs
m= 0.11*0.511e6/(2.99792458e+23)**2 #eV*s**2/A**2
hkr= 6.582119514e-16 #eV*s
x= 0.765705051154
print 'other symbols'
k1=(cmath.sqrt(2.0*(V1-x)*m))/hkr
k2=(cmath.sqrt(2.0*(V2-x)*m))/hkr
k3=(cmath.sqrt(-2.0*x*m))/hkr
k4=(cmath.sqrt(2.0*(V2-x)*m))/hkr
k5=(cmath.sqrt(2.0*(V3-x)*m))/hkr
print 'k-vectors'
a11 = cmath.exp(1.0j*k1*l)
a12 = -1.0*cmath.exp(k2*l)
a13 = -1.0*cmath.exp(-k2*l)
print '1st row'
a21 = 1.0j*k1*cmath.exp(k1*l)
a22 = -k2*cmath.exp(k2*l)
a23 = k2*cmath.exp(-k2*l)
print '2nd row'
a32 = cmath.exp(k2*t)
a33 = cmath.exp(-k2*t)
a34 = -1.0*cmath.exp(1.0j*k3*t)
a35 = -1.0*cmath.exp(-1.0j*k3*t)
print '3rd row'
a42 = k2*cmath.exp(k2*t)
a43 = -k2*cmath.exp(-k2*t)
a44 = -1.0j*k3*cmath.exp(1.0j*k3*t)
a45 = 1.0j*k3*cmath.exp(-1.0j*k3*t)
print '4th row'
a54 = cmath.exp(1.0j*k3*f)
a55 = cmath.exp(-1.0j*k3*f)
a56 = -1.0*cmath.exp(k4*f)
a57 = -1.0*cmath.exp(-k4*f)
print '5th row'
a64 = 1.0j*k3*cmath.exp(1.0j*k3*f)
a65 = -1.0j*k3*cmath.exp(-1.0j*k3*f)
a66 = -k4*cmath.exp(k4*f)
a67 = k4*cmath.exp(-k4*f)
print '6th row'
a76 = cmath.exp(k4*g)
a77 = cmath.exp(-k4*g)
a78 = -1.0*cmath.exp(-1.0j*k5*g)
print '7th row'
a86 = k4*cmath.exp(k4*g)
a87 = -k4*cmath.exp(-k4*g)
a88 = 1.0j*k5*cmath.exp(-1.0j*k5*g)
print '8th row'
M = Matrix([[a11,a12,a13,0,0,0,0,0],
[a21,a22,a23,0,0,0,0,0],
[0,a32,a33,a34,a35,0,0,0],
[0,a42,a43,a44,a45,0,0,0],
[0,0,0,a54,a55,a56,a57,0],
[0,0,0,a64,a65,a66,a67,0],
[0,0,0,0,0,a76,a77,a78],
[0,0,0,0,0,a86,a87,a88]])
v=M.nullspace()
m=lcm([val.q for val in v])
PSI=m*v
print M
print PSI
I've been tinkering with this python snippet which is supposed to demonstrate four different approaches of calculating PageRank.
the code:
from numpy import *
def powerMethodBase(A,x0,iter):
""" basic power method """
for i in range(iter):
x0 = dot(A,x0)
x0 = x0/linalg.norm(x0,1)
return x0
def powerMethod(A,x0,m,iter):
""" power method modified to compute
the maximal real eigenvector
of the matrix M built on top of the input matrix A """
n = A.shape[1]
delta = m*(array([1]*n,dtype='float64')/n) # array([1]*n is [1 1 ... 1] n times
for i in range(iter):
x0 = dot((1-m),dot(A,x0)) + delta
return x0
def maximalEigenvector(A):
""" using the eig function to compute eigenvectors """
n = A.shape[1]
w,v = linalg.eig(A)
return abs(real(v[:n,0])/linalg.norm(v[:n,0],1))
def linearEquations(A,m):
""" solving linear equations
of the system (I-(1-m)*A)*x = m*s """
n = A.shape[1]
C = eye(n,n)-dot((1-m),A)
b = m*(array([1]*n,dtype='float64')/n)
return linalg.solve(C,b)
def getTeleMatrix(A,m):
""" return the matrix M
of the web described by A """
n = A.shape[1]
S = ones((n,n))/n
return (1-m)*A+m*S
A = array([ [0, 0, 0, 1, 0, 1],
[1/2.0, 0, 0, 0, 0, 0],
[0, 1/2.0, 0, 0, 0, 0],
[0, 1/2.0, 1/3.0, 0, 0, 0],
[0, 0, 1/3.0, 0, 0, 0],
[1/2.0, 0, 1/3.0, 0, 1, 0 ] ])
n = A.shape[1] # A is n x n
m = 0.15
M = getTeleMatrix(A,m)
x0 = [1]*n
x1 = powerMethod(A,x0,m,130)
x2 = powerMethodBase(M,x0,130)
x3 = maximalEigenvector(M)
x4 = linearEquations(A,m)
# comparison of the four methods
labels = range(1,6)
print array([labels, x1, x2, x3, x4]).T
The expected output:
[[ 1. 0.32954577 0.32954577 0.32954577 0.32954577]
[ 2. 0.16505695 0.16505695 0.16505695 0.16505695]
[ 3. 0.0951492 0.0951492 0.0951492 0.0951492 ]
[ 4. 0.12210815 0.12210815 0.12210815 0.12210815]
[ 5. 0.05195894 0.05195894 0.05195894 0.05195894]
[ 6. 0.23618099 0.23618099 0.23618099 0.23618099]]
After modifying line 59 to labels = range(1,7), I ran the script locally and got the expected output above. Note how each line in the output repeats the same page rank four times to demonstrate that all four methods of calculating pagerank work and return the same results.
However, when I swap out the A array for a different one:
A = array([ [0, 0, 1/2.0, 0],
[1, 0, 1/2.0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0] ])
It gives me this output:
[[ 1. 0.0534375 0.11790211 0.11790211 0.0534375 ]
[ 2. 0.09885937 0.29698454 0.29698454 0.09885937]
[ 3. 0.0375 0.06701063 0.06701063 0.0375 ]
[ 4. 0.12153047 0.51810272 0.51810272 0.12153047]]
note how the first and fourth methods return the same result and the second and third return the same results, but all four are returning different answers. What am I doing wrong here? Are the four methods supposed to return different results at times?
I tried modifying the getTeleMatrix() function but it didn't solve the problem.
Any help is greatly appreciated.