3

My problem is the following, I have 3 curves/signals (1D) , the measure, the signal and the resolution of my detector: $\mathcal{M},\mathcal{S},\mathcal{R}$, knowing that : \begin{equation}\label{eq:four1} \mathcal{M} = \mathcal{S} * \mathcal{R} \end{equation} with $*$ being the convolution product, and my goal is to find out what $\mathcal{R}$ is, I could 'deconvoluate' but don't know how to so I though about the properties of the Fourier transform ($\mathfrak{F}$) in the frequency domain: \begin{align} \mathfrak{F}\left(\mathcal{S} * \mathcal{R}\right) &= \mathfrak{F}\left(\mathcal{S}\right) \cdot \mathfrak{F}\left(\mathcal{R}\right) \\ &= \mathfrak{F}\left(\mathcal{M}\right) \end{align} And so we get our resolution (a gaussian): \begin{equation}\label{eq:resol_eq} \mathcal{R} = \mathfrak{F}^{-1} \left( \frac{\mathfrak{F}\left(\mathcal{M}\right)}{\mathfrak{F}\left(\mathcal{S}\right)}\right) \end{equation} ($\cdot$ is the 'classical product) Is this something correct to do? In my case I am working with 1 D curves, both $\mathbb{M}$ and $\mathbb{S}$ are sigmoids and to get their fft I use numpy (python library): np.fft.fft(sigmoid_curve) and then I use ifft to get the inverse Fourier transform and finally get my $\mathbb{R}$.

When I test it I get indeed a gaussian but with the wrong $\sigma$ (I only care about this parameter) and I get some sort of repetitive pattern as you can see in the Figure below (light blue curve). Maybe it comes from the fact that I use fft's and not a proper Fourier transform ? Thanks

Looking at the plot, my goal is to find the best gaussian (purple) so that the green curve (S) fits the red one (M). Plot python

Thanks to Gideon's answer I could come up with this:

#time scales for the sigmoids
x_s = np.arange(-100, 100, 0.01)
x_r = np.arange(-950, 105, 0.01)

source and measure

s = 1/(1 + np.exp(-x_s)) + 1 m = 1/(1 + np.exp(-.5*x_r)) + 1.1 #is the convolution of m and s

#direct solution as in the presented example S = linalg.toeplitz(s, np.concatenate([[s[0]], np.zeros("r???".size-1)])) assert np.allclose(m, np.matmul(S, "r ???")) r_estimated = linalg.lstsq(S, m)[0] assert np.allclose(r_estimated, "r???")

#fast and efficeint solution (Levinson-Durbin recursion) r_estimated_levin= linalg.solve_toeplitz((s, np.zeros(s.size)), m)[:"r???".size]

since I do not have access to r (I am actually looking for it) how could I do this ? like so ?

S = linalg.toeplitz(np.concatenate([[s[0]], np.zeros(len(m) - 1)]), s)
Michael
  • 75
  • 5
  • 1
    One issue that immediately pops out is in this expression: \begin{equation}\label{eq:resol_eq} \mathbb{R} = \mathfrak{F}^{-1} \left( \frac{\mathfrak{F}\left(\mathbb{M}\right)}{\mathfrak{F}\left(\mathbb{S}\right)}\right) \end{equation} If $\mathfrak{F}\left(\mathbb{S}\right)$ takes small values in certain ranges then you should try and exclude those ranges from your division. In essence, dividing by a very small number ($\lim_{x\rightarrow 0} x$) may lead to extremely large values or undefined results. – Ahsan Yousaf Jul 20 '23 at 17:16
  • Oh yes I forgot about that what a shame great idea thank you, apart from that the 'formula' is correct right ? – Michael Jul 20 '23 at 17:21
  • Yes but in the line before that you have a typo I believe. Convolution in one domain is multiplication in the other domain. – Ahsan Yousaf Jul 20 '23 at 17:24
  • Indeed thanks again I just edited it – Michael Jul 20 '23 at 17:26
  • So I checked and now I never divide by 0 but still got the same figure, moreover the gaussian I am expecting has to be centered in the inflexion point of my sigmoid which is the case so I am now wondering why there are many gaussian with the wrong $\sigma$ – Michael Jul 20 '23 at 18:57
  • I am not sure but my best guess is that it could be a result of the periodic assumption inherent in the FFT. If your signal isn't periodic, the FFT implicitly "repeats" your signal to make it so, which can result in strange effects in the frequency domain.

    To mitigate these issues, you might want to carefully preprocess your signals (e.g., apply a window function, zero-pad to avoid circular convolution effects, etc.), and consider using a regularized deconvolution method to handle the division in the frequency domain.

    – Ahsan Yousaf Jul 20 '23 at 20:14
  • OK so how could you devoncoluate then ? Cause the gaussian I get using this process with the inverse fft is not the 'correct' one. – Michael Jul 22 '23 at 07:45
  • If you have no noise and the spectrum of $\mathbb{S}$ (odd font choice) never goes to zero, then you should only be limited by numerical precision. Without posting your code, nobody will be able to help much. Could be a zeropadding issue, could be a host of things. Have you tried with some simple test cases to make sure your code is functioning as expected at all stages in the process? – AnonSubmitter85 Jul 24 '23 at 21:40
  • (why odd font choice ?). Yes I'll add the code tomorrow, well I also tried another code where I basically perform a convolution between my sigmoid and different gaussian (trying with different $\sigma$'s) so that it fits the 'smoothed' sigmoid as best as possible. – Michael Jul 25 '23 at 16:48
  • My goodness! Where did you come up with your notational convention? Like your use of \mathbb{} for signals and $\mathfrak{F}$ for the Fourier Transform?

    Is there any textbook that does this? Why use such an unusual convention in notation?

    – robert bristow-johnson Jul 27 '23 at 19:54
  • Just liked the beauty of the letters nothing more, why not use this ? – Michael Jul 28 '23 at 21:06

1 Answers1

3

$$\mathbb{M} = \mathbb{S} * \mathbb{R}$$

might be explicitly written as

$$\mathbb{M}[n]=\sum_{m=0}^{N-1}\mathbb{S}[n-m]\mathbb{R}[m]$$

So, assuming $n>m$, you have $n$ equations with $m$ parameters to find. The solution might be found using the least squares method.

To solve this system, it is common to use the matrix form, with the Toeplitz matrix.

Edges could be handled with respect to the edge conditions or neglected.

edit

Following the request, I attached a code.

import numpy as np
from scipy import linalg, __version__
import matplotlib.pyplot as plt

#semitimescales x = np.arange(-100, 100, 0.01) x2 = np.arange(-15, 15, 0.01)

source (I have added a bias to allow further inversion of the Toeplitz)

s = 1/(1 + np.exp(-x)) + 1

semi-goussian

r = np.exp(-x2**2/30) r /= r.sum() #convolution m = np.convolve(s, r)[:x.size]

#plotting plt.figure() plt.plot(x, s) plt.plot(x2, r/r.max()) plt.plot(x, m) plt.grid() plt.legend(['S', 'Normalized R', 'M']) plt.show()

#direct solution as in the presented example S = linalg.toeplitz(s, np.concatenate([[s[0]], np.zeros(r.size-1)])) assert np.allclose(m, np.matmul(S, r)) r_estimated = linalg.lstsq(S, m)[0] assert np.allclose(r_estimated, r)

#fast and efficeint solution (Levinson-Durbin recursion) r_estimated = linalg.solve_toeplitz((s, np.zeros(s.size)), m)[:r.size] assert np.allclose(r_estimated, r)

print(np.version) print(version)

enter image description here

Gideon Genadi Kogan
  • 1,156
  • 5
  • 17
  • Okay but then using the Toeplitz matrix I can perform a 'deconvolution' to obtain the $\mathbb{S}$ ? Could you give me some insights on how to use the least square methods to obtain my signal ? – Michael Jul 25 '23 at 16:46
  • @Michael, Have a look at https://dsp.stackexchange.com/questions/23350, https://dsp.stackexchange.com/questions/74419 and https://dsp.stackexchange.com/questions/55284. You have a code attached there. – Royi Jul 25 '23 at 19:03
  • Yes I've seen those posts but I don't know how to implement those matrices calculations for 1D signals like you did but in python, in my case i need to find my matrix (NxN where N is the length of the region I am interested in: the inflexion point of my sigmoid right ? )but then I how can I get the $\mathbb{R}$ matrix using the equation above ? – Michael Jul 26 '23 at 21:26
  • @Michael, I have attached a code. Please tell me if there is anything preventing you from accepting the answer... – Gideon Genadi Kogan Jul 27 '23 at 18:24
  • @GideonGenadiKogan I did not had enough credit to validate.. now I do , thank you very much it was very basic indeed using spicy functions... really impressive I tried with convolution processes but always had a problem with the boundaries and all but now it works, I compared r and r_estimated and they are indeed the same (with different amplitudes). – Michael Jul 28 '23 at 20:25
  • Also, in my case I do not know r, so I should just use a window for the convolution (you use r.size but I cannot have it so I should try with different values ? and same I thus cannot use assert but I guess it was just to prove that the 2 values are close for the example) – Michael Jul 28 '23 at 21:14
  • I've added the kind of code I'd like to have in my post, should I make another question or is it okay like so ? – Michael Jul 28 '23 at 21:24
  • I think your additional question could be a comment or separate question as we usually do not edit questions after receiving answers. For your question: please mind that the code you suggested will not work. You can cut the filter to an arbitrary length or use some kind of "knee" method – Gideon Genadi Kogan Jul 29 '23 at 00:07