Skip to content

Commit 3b4886d

Browse files
authored
Merge pull request #73 from FastNFT/fnft_nsep-improvements
fnft_nsep improvements
2 parents bdbf0b7 + faba23a commit 3b4886d

15 files changed

+930
-183
lines changed

examples/mex_fnft_nsep_example.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
% along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
%
1515
% Contributors:
16-
% Sander Wahls (TU Delft) 2017-2018.
16+
% Sander Wahls (TU Delft) 2017-2018, 2021.
1717
% Shrinivas Chimmalgi (TU Delft) 2020.
1818

1919
% This examples demonstrates how the nonlinear Fourier transform with
@@ -52,7 +52,9 @@
5252
% interval [-3j, 3j]. Furthermore, there are degenerate spines
5353
% of length zero at the degenerate points of the main spectrum.
5454

55-
spines = mex_fnft_nsep(q, T, kappa, 'phase_shift', phase_shift, 'points_per_spine', 100);
55+
filt_box = [-10 10 -10 10]; % manual filtering provides a speed up
56+
spines = mex_fnft_nsep(q, T, kappa, 'phase_shift', phase_shift, ...
57+
'points_per_spine', 100, 'filt_manual', filt_box, 'loc_max_evals', 100);
5658

5759
% Increase the number of points per spine above to improve the
5860
% resolution of the spine (at the cost of increased run times).

include/fnft_nsep.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
*
1616
* Contributors:
17-
* Sander Wahls (TU Delft) 2017-2018, 2020.
17+
* Sander Wahls (TU Delft) 2017-2018, 2020-21.
1818
* Shrinivas Chimmalgi (TU Delft) 2020.
1919
*/
2020

@@ -43,16 +43,21 @@
4343
* fnft_nsep_opts_loc_SUBSAMPLE_AND_REFINE: Similar approach as for
4444
* fnft_nsev_opts_dsloc_SUBSAMPLE_AND_REFINE (see
4545
* \link fnft_nsev_opts_t::bound_state_localization \endlink.)\n\n
46+
* fnft_nsep_opts_loc_NEWTON: Refine initial guesses for the main and/or
47+
* auxiliary spectrum by the user using Newton's method.\n\n
48+
* fnft_nsev_opts_dsloc_SUBSAMPLE_AND_REFINE (see
49+
* \link fnft_nsev_opts_t::bound_state_localization \endlink.)\n\n
4650
* fnft_nsep_opts_loc_GRIDSEARCH: Uses a grid search to localize roots. Can
4751
* only find main and auxiliary spectrum points on the real axis. In the
48-
* defocusing case, the main spectrum is always real. The implemented grid
52+
* defocusing case, the main spectrum is always real. The implemented grid
4953
* search gurantees only linear convergence.\n\n
5054
* fnft_nsep_opts_loc_MIXED: Uses the SUBSAMPLE_AND_REFINE method to find the
5155
* non-real parts of the spectra and the GRIDSEARCH method to find the real
5256
* parts.
5357
*/
5458
typedef enum {
5559
fnft_nsep_loc_SUBSAMPLE_AND_REFINE,
60+
fnft_nsep_loc_NEWTON,
5661
fnft_nsep_loc_GRIDSEARCH,
5762
fnft_nsep_loc_MIXED
5863
} fnft_nsep_loc_t;
@@ -211,6 +216,10 @@ fnft_nsep_opts_t fnft_nsep_default_opts();
211216
* - fnft_nse_discretization_2SPLIT8B
212217
* - fnft_nse_discretization_4SPLIT4A
213218
*
219+
* For the Newton refinement mode only, one of the following should be used instead:
220+
* - fnft_nse_discretization_BO
221+
* - fnft_nse_discretization_CF4_2
222+
*
214223
* @param[in] D Number of samples. Should be power of two and \f$ D\geq 2\f$.
215224
* @param[in] q Array of length D, contains samples \f$ q(t_n)=q(x_0, t_n) \f$,
216225
* where \f$ t_n = T[0] + nL/D \f$, where \f$L=T[1]-T[0]\f$ is the period and
@@ -222,18 +231,25 @@ fnft_nsep_opts_t fnft_nsep_default_opts();
222231
* @param[in] phase_shift Real scalar constant. It is the change in the phase
223232
* over one quasi-period,\f$ \angle(q(t+L)/q(t))\f$. For periodic signals it will be 0.
224233
* @param[in,out] K_ptr Upon entry, *K_ptr should contain the length of the array
225-
* main_spec. Upon return, *K_ptr contains the number of actually detected
234+
* main_spec (except for Newton localization, see below). Upon return,
235+
* *K_ptr contains the number of actually detected
226236
* points in the main spectrum. If the length of the array main_spec was not
227237
* sufficient to store all of the detected points in the main spectrum, a
228238
* warning is printed and as many points as possible are returned instead.
229239
* Note that in order to skip the computation of the main spectrum completely,
230240
* it is not sufficient to pass *K_ptr==NULL. Instead, pass main_spec==NULL.
241+
* EXCEPTION: If opts->localization==fnft_nsep_loc_NEWTON, then *K_ptr should
242+
* contain the number of initial guess per spine point (see opts->points_per_spine).
231243
* @param[out] main_spec Array. Upon return, the routine has stored the detected
232244
* main specrum points (i.e., the points for which the trace of the monodromy
233245
* matrix is either +2 or -2) in the first *K_ptr entries of this array.
234246
* If NULL is passed instead, the main spectrum will not be computed.
235247
* Has to be preallocated by the user. The user can choose an arbitrary
236248
* length. Typically, D is a good choice.
249+
* If opts->localization==fnft_nsep_loc_NEWTON, then main_spec should be of
250+
* the form [g1_1, ..., gK_1, g1_2, ..., gK_2, ..., g1_P, ..., gK_P] when
251+
* the routine is called, where gk_n is the k-th initial guess for the n-th
252+
* spine point, K=*K_ptr, and P=opts->points_per_spine.
237253
* @param[in,out] M_ptr Upon entry, *M_ptr should contain the length of the array
238254
* aux_spec. Upon return, *M_ptr contains the number of actually detected
239255
* points in the auxiliary spectrum. If the length of the array aux_spec was

matlab/mex_fnft_nsep.c

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
*
1616
* Contributors:
17-
* Sander Wahls (TU Delft) 2017-2018, 2020.
17+
* Sander Wahls (TU Delft) 2017-2018, 2020-2021.
1818
* Shrinivas Chimmalgi (TU Delft) 2020.
1919
*/
2020

@@ -30,17 +30,17 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
3030
double * T;
3131
double phase_shift = 0.0;
3232
size_t M;
33-
double complex * main_spec;
33+
double complex * main_spec = NULL;
3434
size_t K;
3535
double complex * aux_spec = NULL;
3636
int * sheet_indices = NULL;
3737
int kappa;
3838
size_t upsampling_factor = 1;
39-
size_t i;
39+
size_t i, j;
4040
ptrdiff_t k;
4141
double *re, *im;
4242
double *msr, *msi, *asr, *asi;
43-
char msg[128]; // buffer for error messages
43+
char msg[256]; // buffer for error messages
4444
fnft_nsep_opts_t opts;
4545
int ret_code;
4646

@@ -145,6 +145,51 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
145145

146146
opts.localization = fnft_nsep_loc_SUBSAMPLE_AND_REFINE;
147147

148+
} else if ( strcmp(str, "loc_newton") == 0 ) {
149+
150+
opts.localization = fnft_nsep_loc_NEWTON;
151+
152+
/* Extract initial guesses for the main spectrum */
153+
if (k+1 == nrhs ||
154+
(!mxIsEmpty(prhs[k+1]) &&
155+
(!mxIsComplex(prhs[k+1]) || mxGetM(prhs[k+1]) != 1))) {
156+
snprintf(msg, sizeof msg, "'loc_newton' should be followed by two complex row vectors of initial guesses for the main and auxiliary spectrum, respectively. Try passing complex(...).");
157+
goto on_error;
158+
}
159+
K = mxGetN(prhs[k+1]);
160+
main_spec = mxMalloc(K * sizeof(FNFT_COMPLEX));
161+
if (K > 0 && main_spec == NULL) {
162+
snprintf(msg, sizeof msg, "Out of memory.");
163+
goto on_error;
164+
}
165+
re = mxGetPr(prhs[k+1]);
166+
im = mxGetPi(prhs[k+1]);
167+
for (j=0; j<K; j++)
168+
main_spec[j] = re[j] + I*im[j];
169+
170+
/* Extract initial guesses for the aux spectrum */
171+
if (k+2 == nrhs ||
172+
(!mxIsEmpty(prhs[k+2]) &&
173+
(!mxIsComplex(prhs[k+2]) || mxGetM(prhs[k+2]) != 1))) {
174+
snprintf(msg, sizeof msg, "'loc_newton' should be followed by two complex row vectors of initial guesses for the main and auxiliary spectrum, respectively. Try passing complex(...).");
175+
goto on_error;
176+
}
177+
M = mxGetN(prhs[k+2]);
178+
aux_spec = mxMalloc(M * sizeof(FNFT_COMPLEX));
179+
if (M > 0 && aux_spec == NULL) {
180+
snprintf(msg, sizeof msg, "Out of memory.");
181+
goto on_error;
182+
}
183+
re = mxGetPr(prhs[k+2]);
184+
im = mxGetPi(prhs[k+2]);
185+
for (j=0; j<M; j++)
186+
aux_spec[j] = re[j] + I*im[j];
187+
188+
/* Increase k to account for the two vectors of initial guesses */
189+
k += 2;
190+
191+
opts.discretization = fnft_nse_discretization_BO;
192+
148193
} else if ( strcmp(str, "loc_gridsearch") == 0 ) {
149194

150195
opts.localization = fnft_nsep_loc_GRIDSEARCH;
@@ -204,13 +249,24 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
204249

205250
/* Allocate memory */
206251
q = mxMalloc(D * sizeof(double complex));
207-
K = (opts.points_per_spine)*upsampling_factor*D + 1;
208-
M = upsampling_factor*D;
209-
main_spec = mxMalloc(K * sizeof(double complex));
210-
aux_spec = mxMalloc(M * sizeof(double complex));
211-
sheet_indices = mxMalloc(M * sizeof(int));
212-
if ( q == NULL || main_spec == NULL || aux_spec == NULL
213-
|| sheet_indices == NULL ) {
252+
if (M > 0)
253+
sheet_indices = mxMalloc(M * sizeof(int));
254+
if (opts.localization != fnft_nsep_loc_NEWTON) { /* Not Newton */
255+
K = (opts.points_per_spine)*upsampling_factor*D + 1;
256+
M = upsampling_factor*D;
257+
main_spec = mxMalloc(K * sizeof(double complex));
258+
aux_spec = mxMalloc(M * sizeof(double complex));
259+
} else { /* Newton */
260+
if (K%opts.points_per_spine != 0) {
261+
snprintf(msg, sizeof msg, "The lengths of the first initial guess vector for Newton localization has to be a multiple of points_per_spine (=%u).",
262+
(unsigned int)opts.points_per_spine);
263+
goto on_error;
264+
}
265+
K /= opts.points_per_spine;
266+
}
267+
268+
if ( q == NULL || (K>0 && main_spec == NULL) || (M > 0 && aux_spec == NULL)
269+
|| (M > 0 && sheet_indices == NULL) ) {
214270
snprintf(msg, sizeof msg, "Out of memory.");
215271
goto on_error;
216272
}

matlab/mex_fnft_nsep.m

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,24 @@
1818
% It is possible to provide additional inputs. These come either in the
1919
% form of a single string or of a string followed by a value.
2020
%
21-
% 'loc_subsample_and_refine' Localize spectra by applying fast eigenmethod
21+
% 'loc_subsample_and_refine' Localize spectra by applying a fast eigenmethod
2222
% to a subsampled version of the signal, then refine using
23-
% the complete signal.
23+
% the complete signal (default).
2424
% 'loc_gridsearch' Localize real spectra by using a FFT-based grid search.
2525
% 'loc_mixed' Localize real spectra using grid search and non-real using
26-
% subsample and refine (default).
26+
% subsample and refine.
27+
% 'loc_newton' Localize spectra by refining initial guesses using Newton's
28+
% method (currently, the BO discretization is always used).
29+
% This argument must be followed by two complex row vectors
30+
% of initial guesses for the main and auxiliary spectrum,
31+
% respectively. (Alternatively, it is also possible to pass
32+
% [] if the corresponding part of the spectrum should be
33+
% skipped.) The length of the first vector must be a
34+
% multiple of points_per_spine (default two, see below).
35+
% It should be of the form
36+
% [g1_1,...,gK_1, g1_2, ..., gK_2, ..., g1_P, ..., gK_P],
37+
% where P=points_per_spine and gk_n denotes the k-th initial
38+
% guess for the n-th spine point.
2739
% 'loc_max_evals' Maximum number of monodromy matrix evaluations after which
2840
% refinement is stopped. (The acutual number might be slightly
2941
% higher.) This argument must be followed by a real
@@ -49,11 +61,11 @@
4961
% quasi-period. This argument must be followed by a real
5062
% scalar i.e. angle(q(T(2))/q(T(1))). The default value
5163
% is 0.0, which assumes that q is periodic.
52-
% 'discr_modal' Use modified Ablowitz-Ladik discretization.
53-
% 'discr_2split2A' Use split Boffetta-Osborne discretization (default).
54-
% 'discr_2split4A' Use fifth order splitting with 4th degree polynomial.
55-
% 'discr_2split4B' Use fifth order splitting with 2nd degree polynomial.
56-
% 'discr_4split4B' Fourth-order method. Uses fifth order splitting with
64+
% 'discr_modal' Use modified Ablowitz-Ladik discretization.
65+
% 'discr_2split2A' Use split Boffetta-Osborne discretization (default).
66+
% 'discr_2split4A' Use fifth order splitting with 4th degree polynomial.
67+
% 'discr_2split4B' Use fifth order splitting with 2nd degree polynomial.
68+
% 'discr_4split4B' Fourth-order method. Uses fifth order splitting with
5769
% 2nd degree polynomial.
5870
% 'quiet' Turns off messages generated by then FNFT C library.
5971
% (To turn off the messages generated by the mex
@@ -79,6 +91,6 @@
7991
% along with this program. If not, see <http://www.gnu.org/licenses/>.
8092
%
8193
% Contributors:
82-
% Sander Wahls (TU Delft) 2018, 2020.
94+
% Sander Wahls (TU Delft) 2018, 2020-2021.
8395
% Shrinivas Chimmalgi (TU Delft) 2020.
8496

0 commit comments

Comments
 (0)