├── LICENSE
├── Matlab_codes_for_spectra_display
├── Draw_DOSY_Contour.m
├── draw_GSP_net.m
├── draw_QGC_net.m
├── draw_sim_net.m
└── par2spectr_DOSY.m
├── README.md
├── data
├── GSP
│ ├── GSP.mat
│ └── GSP_net_input.mat
├── QGC
│ ├── QGC.mat
│ └── QGC_net_input.mat
└── simulation
│ ├── SimulatedData_SI.m
│ ├── testdataSigma0.015.mat
│ ├── testdataSigma0.1.mat
│ ├── testdataSigma0.mat
│ ├── trueparaSigma0.015.mat
│ ├── trueparaSigma0.1.mat
│ └── trueparaSigma0.mat
├── demo.py
├── examples.ipynb
├── model_DOSY_est.py
├── train_DOSYEst.py
└── utils.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 hserf
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Matlab_codes_for_spectra_display/Draw_DOSY_Contour.m:
--------------------------------------------------------------------------------
1 | function Draw_DOSY_Contour(Spec_grid, DiffCoef, diff_v, ppm, ContourLevel, linewidth, range_ppm, range_diff);
2 | if nargin < 8 || isempty(range_diff)
3 | range_diff = [diff_v(1),diff_v(end)];
4 | end
5 | if nargin < 7 || isempty(range_ppm)
6 | range_ppm = [ppm(1), ppm(end)];
7 | end
8 | if nargin < 6 || isempty(linewidth)
9 | linewidth = 1;
10 | end
11 | if nargin < 5 || isempty(ContourLevel)
12 | ContourLevel = 20;
13 | end
14 | if nargin < 4||isempty(ppm)
15 | Nf = size(Spec_grid,2);
16 | ppm = 1: Nf;
17 | end
18 | if nargin < 3||isempty(diff_v)
19 | Nd = size(Spec_grid,1);
20 | diff_v = linspace(min(DiffCoef),max(DiffCoef),Nd);
21 | end
22 | %%
23 | proj_ppm = sum(Spec_grid, 1);
24 | proj_diff = sum(Spec_grid, 2);
25 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26 | figure,
27 | ax1 = axes('position',[0.1 0.85 0.795 0.13]);
28 | plot(ax1,ppm,proj_ppm/max(abs(proj_ppm(:))),'k-','LineWidth',1);
29 | axis off;
30 | set(gca,'Xdir','reverse');
31 | xlim(range_ppm);ylim([-0.2,1]);
32 | ax2 = axes('position',[0.1 0.20 0.795 0.65]);
33 | contour(ax2,ppm,diff_v,Spec_grid,ContourLevel,'linewidth',linewidth)
34 | xlabel(ax2,'Chemical Shift (ppm)')
35 | ylabel(ax2,'Diffusion Coefficient (10^{-10} m^2/s)')
36 | set(ax2,'Ydir','reverse','Xdir','reverse');
37 | set(ax2,'YTick',unique(DiffCoef) );
38 | xlim(range_ppm);
39 | ylim(range_diff);
40 | for i = 1:length(DiffCoef)
41 | line(ax2,get(ax2,'xlim'),DiffCoef(i)*ones(1,2),'LineWidth',0.8,'color',[0.85 0.85 0.85],'LineStyle','--');
42 | end
43 | ax3 = axes('position',[0.91 0.20 0.08 0.65]);
44 | plot(ax3,proj_diff/max(abs(proj_diff)),diff_v,'k-','linewidth',1);
45 | set(ax3,'Ydir','reverse');
46 | ylim(range_diff); xlim([0 1]);
47 | axis off;
48 |
49 |
50 | end
51 |
52 |
--------------------------------------------------------------------------------
/Matlab_codes_for_spectra_display/draw_GSP_net.m:
--------------------------------------------------------------------------------
1 | %% showing the resulting spectrum for GSP
2 | clear all; %close all;
3 | file_path = fileparts(mfilename('fullpath'));
4 | addpath(file_path)
5 |
6 | file_path = '..\Net_Results\GSP\';
7 | SubFolderNames = dir(file_path);
8 | file_folder = strcat(file_path,SubFolderNames(end).name),% Find the latest folder
9 | load(strcat(file_folder,'\data_org.mat'));
10 | idx_peaks = double(idx_peaks);
11 |
12 | %% Read in the result
13 | dc_z = csvread(strcat(file_folder, '\diffusion_coeffs.csv'));
14 | Sp_z = csvread(strcat(file_folder, '\Sp.csv'));
15 | [N_iter, N_d] = size(dc_z);
16 | k = N_iter;
17 | dc = dc_z(k,:);
18 | N_freq = round(size(Sp_z,2)/N_d);
19 | Nf = length(ppm);
20 | Sp = reshape(Sp_z(k,:),[N_d,N_freq]);
21 | Decay = exp(-b(:)*dc);
22 | X_rec = Decay*Sp;
23 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24 | %% final loss
25 | norm(X_rec.'-S,'fro')^2
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | %% Draw contour figure
28 | ContourLevel = 20;
29 | linewidth = 1.5;
30 | range_ppm = [3, 5.5];
31 | range_diff = [1.5, 5];
32 | Dn = 100;
33 | diff_v = linspace(range_diff(1),range_diff(2),Dn);
34 | sgm_f = 1; % linewidth in frequency dimension
35 | sgm_d = 0.05; % linewidth in diffusion coefficient dimension
36 |
37 | dc_round = roundn(dc,-1);
38 | Spec_grid = par2spectr_DOSY(dc_round,Sp,idx_peaks, sgm_d, sgm_f, diff_v, ppm); % generate a speudo DOSY spectrum
39 |
40 | Spec_grid = Spec_grid/max(abs(Spec_grid(:)));
41 | Draw_DOSY_Contour(Spec_grid, dc_round, diff_v, ppm, ContourLevel, linewidth, range_ppm, range_diff);
42 |
43 | %% Plot the spectra of each diffusion components
44 | [dc_s, ind] = sort(dc);
45 | Sp_s = Sp(ind,:);
46 | sgm_f = 3;
47 | nf = 1:length(ppm);
48 | figure,
49 | for k = 1: length(dc)
50 | spec_k = Sp_s(k,:)*exp(-(idx_peaks'-nf).^2/2/sgm_f^2);
51 | subplot(length(dc),1,k); plot(ppm, spec_k, 'k','linewidth',1);
52 | set(gca,'xdir','reverse');
53 | xlim([range_ppm(1),range_ppm(2)]);
54 | text(mean(range_ppm),max(spec_k)/2, ['D_{',num2str(k),'} = ',num2str(roundn(dc_s(k),-2))]);
55 | end
56 | xlabel('ppm')
57 |
58 |
--------------------------------------------------------------------------------
/Matlab_codes_for_spectra_display/draw_QGC_net.m:
--------------------------------------------------------------------------------
1 | %% showing the resulting spectrum for QGC
2 | clear all; close all;
3 | file_path = fileparts(mfilename('fullpath'));
4 | addpath(file_path)
5 |
6 | is_mat_file = 0;
7 | file_path = '..\Net_Results\QGC\';
8 | SubFolderNames = dir(file_path);
9 | file_folder = strcat(file_path,SubFolderNames(end).name),% Find the latest folder
10 | load(strcat(file_folder,'\data_org.mat'));
11 | idx_peaks = double(idx_peaks);
12 |
13 | beta0 = 0.1;
14 | %% Read in the result
15 | dc_z = csvread(strcat(file_folder, '\diffusion_coeffs.csv'));
16 | Sp_z = csvread(strcat(file_folder, '\Sp.csv'));
17 | [N_iter, N_d] = size(dc_z);
18 | N_freq = round(size(Sp_z,2)/N_d);
19 | Nf = length(ppm);
20 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21 | %% Plot the changing of diffusion coefficients through the iterations
22 | N_iter = size(dc_z,1);
23 | iter = (0:N_iter-1)*500;
24 | figure, plot(iter, dc_z,'linewidth',1.5);
25 | xlabel('Iterations'); ylabel('Diffusion Coefficient (10^{-10} m^2/s)')
26 | %%
27 | fidelity_loss = zeros(1,N_iter);
28 | sparsity = zeros(1,N_iter);
29 | for k = 1: N_iter
30 | dc = dc_z(k,:);
31 | Sp = reshape(Sp_z(k,:),[N_d,N_freq]);
32 | Decay = exp(-b(:)*dc);
33 | X_rec = Decay*Sp;
34 | X_rec = X_rec.';
35 | fidelity_loss(k) = norm(X_rec(:)-S(:))^2;
36 | lambda_dc = sum(Decay.^2,1);
37 | Sp_norm = Sp./lambda_dc(:);
38 | Sp_norm = Sp_norm.*max(S,[],2).';
39 | sparsity(k) = sum(abs(Sp_norm(:)));
40 | end
41 | figure, subplot(211); semilogy(iter, fidelity_loss,'k','linewidth',1.5)
42 | xlabel('Iterations'); ylabel('Fidelity Loss');
43 | subplot(212); semilogy(iter, sparsity,'k','linewidth',1.5)
44 | xlabel('Iterations'); ylabel('Sparsity');
45 | total_loss = fidelity_loss+beta0*sparsity;
46 | figure, semilogy(iter, total_loss,'k','linewidth',1.5);
47 | xlabel('Iterations'); ylabel('Total Loss');
48 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49 | %% Final output
50 | k = N_iter;
51 | dc = dc_z(k,:);
52 | Sp = reshape(Sp_z(k,:),[N_d,N_freq]);
53 | Decay = exp(-b(:)*dc);
54 | X_rec = Decay*Sp;
55 | %%%
56 | sp0 = sum(Sp(:));
57 | Sp_norm = Sp./lambda_dc(:);
58 | sp1 = sum(Sp_norm(:));
59 | Sp_norm = Sp.*max(S,[],2).';
60 | sp2 = sum(Sp_norm(:));
61 | Sp_norm = Sp.*sqrt(lambda_dc(:));
62 | sp3 = sum(Sp_norm(:));
63 |
64 | % [fidelity_loss(end),sparsity(end),sp0,sp1,sp2,sp3]
65 | [fidelity_loss(end),sparsity(end),sp0,sp3]
66 | %%%
67 | %% Draw contour figure for the final output
68 | ContourLevel = 40; linewidth = 1.5;
69 | range_ppm = [4 12.5];
70 | range_diff = [1, 12];
71 | Dn = 100;
72 | diff_v = linspace(range_diff(1),range_diff(2),Dn);
73 | sgm_f = 1; % linewidth in frequency dimension
74 | sgm_d = 0.1; % linewidth in diffusion coefficient dimension
75 |
76 | dc_round = roundn(dc,-1);
77 | Spec_grid = par2spectr_DOSY(dc_round,Sp,idx_peaks, sgm_d, sgm_f, diff_v, ppm); % generate a speudo DOSY spectrum
78 |
79 | Spec_grid = Spec_grid/max(abs(Spec_grid(:)));
80 | Draw_DOSY_Contour(Spec_grid, dc_round, diff_v, ppm, ContourLevel, linewidth, range_ppm, range_diff);
81 |
82 | %% Plot the spectra of each diffusion components
83 | [dc_s, ind] = sort(dc);
84 | Sp_s = Sp(ind,:);
85 | sgm_f = 3;
86 | nf = 1:length(ppm);
87 | figure,
88 | for k = 1: length(dc)
89 | spec_k = Sp_s(k,:)*exp(-(idx_peaks(:)-nf).^2/2/sgm_f^2);
90 | subplot(length(dc),1,k); plot(ppm, spec_k, 'k','linewidth',1);
91 | set(gca,'xdir','reverse');
92 | xlim([range_ppm(1),range_ppm(2)]);
93 | text(mean(range_ppm),max(spec_k)/2, ['D_{',num2str(k),'} = ',num2str(roundn(dc_s(k),-2))]);
94 | end
95 | xlabel('ppm')
96 |
97 |
--------------------------------------------------------------------------------
/Matlab_codes_for_spectra_display/draw_sim_net.m:
--------------------------------------------------------------------------------
1 | clear all; close all;
2 |
3 | load('..\data\simulation\trueparaSigma0.1.mat')
4 | file_path = '..\Net_Results\sim\';
5 | SubFolderNames = dir(file_path);
6 | file_folder = strcat(file_path,SubFolderNames(end).name),% Find the latest folder
7 | load(strcat(file_folder,'\data_org.mat'));
8 | idx_peaks = double(idx_peaks);
9 |
10 | a_z = csvread([file_folder, '\diffusion_coeffs.csv']);
11 | A_z = csvread([file_folder, '\Sp.csv']);
12 | [N_iter, N_d] = size(a_z);
13 | N_freq = size(A_z,2)/N_d;
14 | k = N_iter;
15 | ak = a_z(k,:);
16 | Ak = reshape(A_z(k,:),[N_d,N_freq]);
17 |
18 | Decay = exp(-b(:)*ak);
19 | X_rec = Decay*Ak;
20 | %%
21 | Nf = size(S,1);
22 | ff = 0:1/Nf:1-1/Nf;
23 | sgm_d = 0.005; sgm_f = 0.001; diff_v = linspace(0,0.5,100);
24 | ContourLevel = 20;
25 | Spec_grid = par2spectr_DOSY(alpha,Aksave,1:Nf, sgm_d, sgm_f, diff_v, ff);
26 | Draw_DOSY_Contour(Spec_grid, alpha, diff_v, ff, ContourLevel);
27 |
28 | Spec_grid_rec = par2spectr_DOSY(ak,Ak,1:size(Ak,2), sgm_d, sgm_f, diff_v, ff);
29 | Draw_DOSY_Contour(Spec_grid_rec, ak, diff_v, ff, ContourLevel);
30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 | %% display the convergence curve
32 | N_iter = size(a_z,1);
33 | iter = 1:N_iter;
34 | fidelity_loss = zeros(1,N_iter);
35 | for k = 1: N_iter
36 | dc = a_z(k,:);
37 | Sp = reshape(A_z(k,:),[N_d,N_freq]);
38 | Decay = exp(-b(:)*dc);
39 | X_rec = Decay*Sp;
40 | X_rec = X_rec.';
41 | fidelity_loss(k) = norm(X_rec(:)-S(:))^2;
42 | lambda_dc = sum(Decay.^2,1);
43 | end
44 | figure, semilogy(iter, fidelity_loss,'k','linewidth',1.5)
45 | xlabel('Iterations'); ylabel('Fidelity Loss');
46 | % return
47 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48 | color_list = {'k','r','b'};
49 | figure, subplot(131);hold on;
50 | for k = 1:size(Aksave,1)
51 | idx = find(Aksave(k,:));
52 | xn_k = S(idx,:);
53 | plot(b,exp(-alpha(k)*b),'g:','linewidth',5);
54 | plot(b,xn_k,color_list{k});
55 | end
56 | xlim([0,b(end)]);
57 | ylim([-0.05,1])
58 | %%
59 | model_fun = @(c,b) c(1)*exp(-c(2)*b);
60 | xn_fit = zeros(size(S));
61 | Ak_expfit = zeros(1,size(S,1));
62 | ak_expfit = zeros(1,size(S,1));
63 | subplot(132); hold on;
64 | for k = 1:size(S,1)
65 | xn_k = S(k,:);
66 | mdl = fitnlm(b,xn_k,model_fun,[1,0.1]);
67 | coefficients = mdl.Coefficients{:, 'Estimate'};
68 | A_k = coefficients(1); Ak_expfit(k) = A_k;
69 | a_k = coefficients(2); ak_expfit(k) = a_k;
70 | xn_fit(k,:) = A_k*exp(-a_k*b);
71 | plot(b, xn_fit(k,:),'r')
72 | end
73 | xlim([0,b(end)]);
74 | ylim([-0.05,1])
75 |
76 | subplot(133), hold on;
77 | for k = 1:length(ak)
78 | xn_net = exp(-ak(k)*b);
79 | plot(b, xn_net,'k')
80 | end
81 | xlim([0,b(end)]);
82 | ylim([-0.05,1])
83 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 | %% Display monofitting spectrum
85 | FC_expfit = diag(Ak_expfit);
86 | Spec_grid_expfit = par2spectr_DOSY(ak_expfit,FC_expfit,1:size(S,1), sgm_d, sgm_f, diff_v,ff);
87 | Draw_DOSY_Contour(Spec_grid_expfit, ak_expfit, diff_v, ff, ContourLevel);
88 |
89 |
--------------------------------------------------------------------------------
/Matlab_codes_for_spectra_display/par2spectr_DOSY.m:
--------------------------------------------------------------------------------
1 | function Spec_grid = par2spectr_DOSY(DiffCoef,Spectrum, idx_freq, sgm_d, sgm_f, diff_v, ppm);
2 | %% This function generate a whole DOSY spectrum from the DOSY parameter estimation result
3 | %% Input:
4 | %% -DiffCoef: the estimated diffusion coefficients, is a vector or column containing n_decay elements
5 | %% -Spectrum: with size [n_decay, n_freq]
6 | %% -idx_freq: the index of the frequency points in Spectrum
7 | %% -sgm_d: the peak linewidth of the diffusion coefficient dimension
8 | %% -sgm_d: the peak linewidth of the frequency dimension
9 | %% -diff_v: the grids on the diffusion coefficient dimension, with a length of Nd
10 | %% -ppm: the grids on the frequency dimension, with a length of Nf
11 | %% OUTPUT:
12 | %% -Spec_grid with size [Nd, Nf]
13 | [n_decay, n_freq] = size(Spectrum);
14 | if nargin < 7||isempty(ppm)
15 | Nf = 1e3;
16 | ppm = 1: Nf;
17 | else
18 | Nf = length(ppm);
19 | end
20 | if nargin < 6||isempty(diff_v)
21 | Nd = 1e2;
22 | diff_v = linspace(min(DiffCoef),max(DiffCoef),Nd);
23 | else
24 | Nd = length(diff_v);
25 | end
26 | if nargin < 5||isempty(sgm_f)
27 | sgm_f = 1;
28 | end
29 | if nargin < 4||isempty(sgm_d)
30 | sgm_d = (diff_v(2)-diff_v(1))*2;
31 | end
32 |
33 | if length(idx_freq) == length(ppm)
34 | spec_whole = Spectrum;
35 | else
36 | spec_whole = zeros(n_decay, Nf);
37 | nf = 1:Nf;
38 | for k1 = 1:n_decay
39 | spec_whole(k1,:) = Spectrum(k1,:)*exp(-(idx_freq(:)-nf).^2/2/sgm_f^2);
40 | end
41 | end
42 |
43 | Spec_grid = zeros(Nd, Nf);
44 | for k = 1: n_decay
45 | dk = DiffCoef(k);
46 | tmp = exp(-(diff_v(:)-dk).^2/2/sgm_d^2)*spec_whole(k,:);
47 | Spec_grid = Spec_grid+tmp;
48 | end
49 |
50 | end
51 |
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Code from "**A Neural Network Method for Diffusion-Ordered NMR Spectroscopy**"
2 |
3 | This repository contains information and code from the paper "A Neural Network Method for Diffusion-Ordered NMR Spectroscopy", which is published at *Analytical Chemistry* **2022**, 94(6), 2699-2705, by Enping Lin, Nannan Zou, Yuqing Huang, Zhong Chen, and Yu Yang.
4 |
5 | ## Requirements
6 |
7 | Here is a list of libraries you might need to install to execute the code:
8 |
9 | - python (=3.6)
10 | - tensorflow (=1.14.0)
11 | - numpy
12 | - scipy
13 | - h5py
14 | - pandas
15 | - matplotlib
16 | - jupyter notebook
17 |
18 | ## Data
19 |
20 | A .mat file for DOSY data should be prepared before running the python code. Examples for the .mat file are presented in the 'data' folder. To generate this .mat file, one could use one of the following two methods:
21 | (1) Use DOSYToolbox to export the .mat file, which contains a structure named "NmrData". Make sure that these variables exist in NmrData: NmrData.SPECTRA (to-be-process data matrix, the same as "S" in the paper), NmrData.Gzlvl (pulse field gradients, as "g" in the paper), NmrData.ngrad (the number of gradients), NmrData.dosyconstant (gamma.^2\*delts^2\*DELTAprime).
22 |
23 | (2) Define a .mat file including "S" (to-be-process data matrix), "b" (the gradient-related vector), "idx_peaks" (the indices of the selected spectral points), and "ppm" (the chemical shift coordinates in ppm of the original data). It should be mentioned that only "S" and "b" are used for the DOSY parameter estimation while "idx_peaks" and "ppm" are used for the subsequent spectrum reconstruction and display.
24 |
25 | - The subfolder 'simulation' contains a simple example of simulation data and the generation code written in Matlab.
26 | - The subfolder 'QGC' contains the data that are applied in the paper as the first example. The corresponding .mat file (QGC_net_input.mat) contains data that are transformed and extracted from the original DOSY experimental data. If you want to use this data, please refer to (and cite) the original paper: Foroozandeh, M.; Castanar, L.; Martins, L. G.; Sinnaeve, D.; Poggetto, G. D.; Tormena, C. F.; Adams, R. W.; Morris, G. A.; Nilsson, M. Ultrahigh-Resolution Diffusion-Ordered Spectroscopy, *Angewandte Chemie* **2016**, *128*, 15808-15811.
27 | - The subfolder 'GSP' contains the data that are applied in the paper as the second example. The corresponding .mat file (GSP_net_input.mat) contains data that are transformed and extracted from the original DOSY experimental data. If you want to use this data, please refer to (and cite) the original paper: Yuan, B.; Ding, Y.; Kamal, G. M.; Shao, L.; Zhou, Z.; Jiang, B.; Sun, P.; Zhang, X.; Liu, M. Reconstructing diffusion ordered NMR spectroscopy by simultaneous inversion of Laplace transform, *Journal of Magnetic Resonance* **2017**, *278*, 1-7.
28 |
29 | ## Python Code (to generate parameters for DOSY spectrum)
30 |
31 | - utils.py provides the script for loading the data, displaying and saving the results.
32 | - train_DOSYEst.py is the code for setting and training the neural network model to generate the parameters for the fitting.
33 | - examples.ipynb is the example code written in Jupyter Notebook to present how to use this neural-network-based optimizer and generate the parameters for DOSY spectra.
34 |
35 | After running the code (demo.py or examples.ipynb) with defaulted setting, a folder named "Net_Results" would be created and expanded with new results, including the estimated diffusion coefficients D(l) and the spectral coefficients C(l, f). To show the results, the following Matlab codes are provided:
36 |
37 |
38 | ## Matlab Code (to show the DOSY spectrum in a prettier way)
39 |
40 | The Matlab code for spectrum reconstruction and display is included in the folder "Matlab_codes_for_spectra_display".
41 |
42 | ## Others
43 |
44 | Email me if you have any questions: yuyang15@xmu.edu.cn
45 |
--------------------------------------------------------------------------------
/data/GSP/GSP.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/GSP/GSP.mat
--------------------------------------------------------------------------------
/data/GSP/GSP_net_input.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/GSP/GSP_net_input.mat
--------------------------------------------------------------------------------
/data/QGC/QGC.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/QGC/QGC.mat
--------------------------------------------------------------------------------
/data/QGC/QGC_net_input.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/QGC/QGC_net_input.mat
--------------------------------------------------------------------------------
/data/simulation/SimulatedData_SI.m:
--------------------------------------------------------------------------------
1 | %% Simulated data (applied in Supporting Information - Section A)
2 | clc;
3 | clear;
4 | %close all;
5 | file_path = fileparts(mfilename('fullpath'));
6 |
7 | %%
8 | rng(1)
9 | %%%%%%%%%%%%%%%%%%
10 | n = 14;
11 | % n = 20;
12 | b = 0:n-1;
13 | pn = 10;
14 | ppm = linspace(0.05,0.95,pn);
15 | IndFre = {[1,3,10],[2,5,7], [4,6,8,9]};
16 |
17 | alpha = [0.1,0.2,0.3];
18 | cn = length(alpha);
19 | %%%%%%%%%%%%%%%%%%
20 | Aksave = zeros(length(alpha),pn);
21 |
22 | for it = 1:cn
23 | DifC(:,it) = exp(-alpha(it)*b.' );
24 | Aksave(it,IndFre{it}) = 1;
25 | end
26 | figure,hold on,plot(DifC)
27 | DOSYExp = DifC * Aksave;
28 |
29 | normedDOSYExp = max(DOSYExp(:));
30 | DOSYExp = DOSYExp/normedDOSYExp;
31 |
32 | sigma2 = 0.1; % noise level
33 | % sigma2 = 0.015;
34 | % sigma2 = 0.0;
35 | noise = sigma2*randn(size(DOSYExp));
36 | DOSYExp_noise = DOSYExp + noise;
37 |
38 | S = DOSYExp_noise;
39 | S = S.';
40 | idx_peaks = 1:size(S,1);
41 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 | Nf = size(Aksave,2);
43 | ff = 0:1/Nf:1-1/Nf;
44 | sgm_d = 0.01; sgm_f = 0.01; diff_v = linspace(0,0.5,100);
45 | ContourLevel = 40;
46 | Spec_grid = par2spectr_DOSY(alpha,Aksave,1:Nf, sgm_d, sgm_f, diff_v, ff);
47 | Draw_DOSY_Contour(Spec_grid, alpha, diff_v, ff, ContourLevel);
48 |
49 | save([file_path,'\testdataSigma',num2str(sigma2),'.mat'],'S','b','idx_peaks','ppm')
50 | save([file_path,'\trueparaSigma',num2str(sigma2),'.mat'],'alpha','Aksave')
51 |
52 | return
53 |
--------------------------------------------------------------------------------
/data/simulation/testdataSigma0.015.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/testdataSigma0.015.mat
--------------------------------------------------------------------------------
/data/simulation/testdataSigma0.1.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/testdataSigma0.1.mat
--------------------------------------------------------------------------------
/data/simulation/testdataSigma0.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/testdataSigma0.mat
--------------------------------------------------------------------------------
/data/simulation/trueparaSigma0.015.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/trueparaSigma0.015.mat
--------------------------------------------------------------------------------
/data/simulation/trueparaSigma0.1.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/trueparaSigma0.1.mat
--------------------------------------------------------------------------------
/data/simulation/trueparaSigma0.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hserf/DOSY_Net/880c47ff791d64679aae31df2b9964b2de1e2205/data/simulation/trueparaSigma0.mat
--------------------------------------------------------------------------------
/demo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Mon Oct 25 15:05:35 2021
4 |
5 | @author: Yu
6 | """
7 |
8 | from train_DOSYEst import train, options
9 |
10 | args = options()
11 |
12 | idx_case = 1
13 |
14 | if idx_case == 1:
15 | ## Case 1: simulation
16 | #args.input_file = 'data/simulation/testdataSigma0.015.mat'
17 | args.input_file = 'data/simulation/testdataSigma0.1.mat'
18 | args.output_path = 'Net_Results/sim/'
19 | args.diff_range = []
20 | args.reg_A = 0.3
21 | args.max_iter = 20000
22 | args.learning_rate_1 = 1e-4
23 | args.learning_rate_2 = 5e-6
24 | args.fidelity = 'norm-2'
25 | args.n_decay = 3
26 |
27 | elif idx_case == 2:
28 | ## Case 2: QGC
29 | ###############################
30 | ## Two ways to import the data:
31 | #(1) from a user-defined .mat file
32 | #args.input_file = 'data/QGC/QGC_net_input.mat'
33 | #(2) from a DOSYToolbox .mat file
34 | args.input_file = 'data/QGC/QGC.mat'
35 | args.threshold = 0.0138 # for DOSYToolbox data, it requires a threshold to pick the effective spectral points and remove some noise
36 | args.data_matrix_proc = 'real' # Take the real part of the data (default). If the phases of the spectral data are not adjusted, then 'abs' is suggested.
37 | ###############################
38 | args.output_path = 'Net_Results/QGC/'
39 | args.diff_range = []
40 | #args.diff_range = [3.0, 12.0]
41 | args.reg_A = 0.1
42 | args.max_iter = 30000
43 | args.learning_rate_1 = 2e-4 # 1e-4 is also fine
44 | args.learning_rate_2 = 1e-5 # 5e-6 is also fine but may need a larger "max_iter"
45 | args.fidelity = 'norm-2'
46 | args.n_decay = 3
47 |
48 | elif idx_case == 3:
49 | ## Case 3: GSP
50 | ###############################
51 | ## Two ways to import the data:
52 | #(1) from a user-defined .mat file
53 | #args.input_file = 'data/GSP/GSP_net_input.mat'
54 | #(2) from a DOSYToolbox .mat file
55 | args.input_file = 'data/GSP/GSP.mat'
56 | args.threshold = 0.03 # for DOSYToolbox data, it requires a threshold to pick the effective spectral points and remove some noise
57 | args.data_matrix_proc = 'real' # Take the real part of the data (default). If the phases of the spectral data are not adjusted, then 'abs' is suggested.
58 | ###############################
59 | args.output_path = 'Net_Results/GSP/'
60 | args.diff_range = []
61 | #args.diff_range = [1.0, 6.0]
62 | args.reg_A = 0.8
63 | args.max_iter = 30000
64 | args.learning_rate_1 = 2e-4 # 1e-4 is also fine
65 | args.learning_rate_2 = 1e-5 # 5e-6 is also fine but may need a larger "max_iter"
66 | args.fidelity = 'norm-2'
67 | args.n_decay = 3
68 |
69 |
70 | dr, Sp = train(args)
71 |
--------------------------------------------------------------------------------
/examples.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "import argparse\n",
10 | "from train_DOSYEst import options, train"
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": 2,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "# Case 1: simulation data\n",
20 | "parser = argparse.ArgumentParser()\n",
21 | "parser.add_argument(\"--learning_rate_1\", default=1e-4, type=float, help='Learning rate for the first 15000 steps')\n",
22 | "parser.add_argument(\"--learning_rate_2\", default=5e-6, type=float, help='Learning rate after 15000 steps')\n",
23 | "parser.add_argument(\"--input_file\", default='data/simulation/testdataSigma0.015.mat', help='File path of the input data')\n",
24 | "parser.add_argument(\"--threshold\", default=0.01, type=float, help='Threshold for choosing the spectral points')\n",
25 | "parser.add_argument(\"--output_path\", default='Net_Results/sim/', help='Output file path')\n",
26 | "parser.add_argument(\"--diff_range\", default=[0, 1], type=float, help='The range of the diffusion coefficient')\n",
27 | "parser.add_argument(\"--n_decay\", default=3, type=int, help='The number of different decay components')\n",
28 | "parser.add_argument(\"--dim_in\", default=100, type=int, help='Dimension of the input of the network')\n",
29 | "parser.add_argument(\"--reg_A\", default=0.01, type=float, help='Regularization parameter')\n",
30 | "parser.add_argument(\"--max_iter\", default=20000, type=int, help='Maximum iterations')\n",
31 | "parser.add_argument(\"--fidelity\", default='norm-2', help='Fidelity term setting, could be norm-1 or norm-2')\n",
32 | "parser.add_argument(\"--save_process\", default=True, help='If you want to save the intermediate process parameters?')\n",
33 | "parser.add_argument(\"--save_interval\", default=1000, type=int, help='The step size to save the intermediate results')\n",
34 | "parser.add_argument(\"--display\", default=True, help='Do you want to show the convergence process in the terminal?')\n",
35 | "\n",
36 | "args = parser.parse_args(args=[])\n"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": 3,
42 | "metadata": {},
43 | "outputs": [
44 | {
45 | "name": "stderr",
46 | "output_type": "stream",
47 | "text": [
48 | "WARNING: Logging before flag parsing goes to stderr.\n",
49 | "W0119 11:32:48.005402 2408 deprecation.py:506] From D:\\Program_Files\\Miniconda3\\envs\\tensorflow\\lib\\site-packages\\tensorflow\\python\\ops\\init_ops.py:1251: calling VarianceScaling.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n",
50 | "Instructions for updating:\n",
51 | "Call initializer instance with the dtype argument instead of passing it to the constructor\n",
52 | "W0119 11:32:48.131067 2408 deprecation.py:323] From D:\\Program_Files\\Miniconda3\\envs\\tensorflow\\lib\\site-packages\\tensorflow\\python\\ops\\math_grad.py:1205: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n",
53 | "Instructions for updating:\n",
54 | "Use tf.where in 2.0, which has the same broadcast rule as np.where\n"
55 | ]
56 | },
57 | {
58 | "name": "stdout",
59 | "output_type": "stream",
60 | "text": [
61 | "Read from a user-defined Mat file.\n",
62 | "step: 1, total loss: 435.637939, fidelity loss: 435.329742\n",
63 | "time cost: 0.9205301\n",
64 | "step: 1001, total loss: 0.511916, fidelity loss: 0.476423\n",
65 | "time cost: 3.5113738\n",
66 | "step: 2001, total loss: 0.100505, fidelity loss: 0.062605\n",
67 | "time cost: 6.0295926\n",
68 | "step: 3001, total loss: 0.077057, fidelity loss: 0.040870\n",
69 | "time cost: 8.4055351\n",
70 | "step: 4001, total loss: 0.070415, fidelity loss: 0.035008\n",
71 | "time cost: 10.7806103\n",
72 | "step: 5001, total loss: 0.068223, fidelity loss: 0.033155\n",
73 | "time cost: 13.254710399999999\n",
74 | "step: 6001, total loss: 0.066665, fidelity loss: 0.031837\n",
75 | "time cost: 15.6325855\n",
76 | "step: 7001, total loss: 0.064811, fidelity loss: 0.030293\n",
77 | "time cost: 17.6935004\n",
78 | "step: 8001, total loss: 0.063299, fidelity loss: 0.029147\n",
79 | "time cost: 19.7234869\n",
80 | "step: 9001, total loss: 0.062754, fidelity loss: 0.028840\n",
81 | "time cost: 21.7358049\n",
82 | "step: 10001, total loss: 0.062514, fidelity loss: 0.028625\n",
83 | "time cost: 23.7547737\n",
84 | "step: 11001, total loss: 0.062474, fidelity loss: 0.028593\n",
85 | "time cost: 25.810348\n",
86 | "step: 12001, total loss: 0.062468, fidelity loss: 0.028588\n",
87 | "time cost: 27.818339400000003\n",
88 | "step: 13001, total loss: 0.062461, fidelity loss: 0.028572\n",
89 | "time cost: 29.8307435\n",
90 | "step: 14001, total loss: 0.062460, fidelity loss: 0.028568\n",
91 | "time cost: 31.8415816\n",
92 | "step: 15001, total loss: 0.062488, fidelity loss: 0.028576\n",
93 | "time cost: 33.8529005\n",
94 | "step: 16001, total loss: 0.062460, fidelity loss: 0.028568\n",
95 | "time cost: 35.857757799999995\n",
96 | "step: 17001, total loss: 0.062460, fidelity loss: 0.028568\n",
97 | "time cost: 37.8689067\n",
98 | "step: 18001, total loss: 0.062460, fidelity loss: 0.028568\n",
99 | "time cost: 39.8714527\n",
100 | "step: 19001, total loss: 0.062460, fidelity loss: 0.028568\n",
101 | "time cost: 41.870048399999995\n",
102 | "Loop over\n",
103 | "Total time cost: 43.8895256\n",
104 | "Final step: 20000, total loss: 0.062460, fidelity loss: 0.028568\n"
105 | ]
106 | },
107 | {
108 | "data": {
109 | "image/png": "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JujW2Qr8AZju7k8llJ9L0C20BvgiMMHdd7YgfomQuesmKiKZYGYOHOTuS6KORVovtfRFRFoRJX0RkVZE3TsiIq2IWvoiIq1IRq8QuL+6devmAwYMiDoMEZEDxrx58z5x95KmymOd9AcMGEBFRVMjAkVEpCEzW763cnXviIi0IrFM+hbc6GHGxo0bow5FRCSrxDLpu/tsd5/asWPHqEMREckqse7TF5HsU1VVRWVlJTt27Ig6lANaYWEhffr0IT8/f98zJ1DSF5GMqqyspH379gwYMID6V9qW5nJ31q1bR2VlJQMHDtyvZWPZvaM+fZHstWPHDrp27aqEnwQzo2vXri3aW4pl0lefvkh2U8JPXku3YSyTvoiIpEdWJv2ps6fyvae+F3UYIhIz69atY9SoUYwaNYqePXtSWlpa93rXrl3NWscFF1zAokWLml3nHXfcweWXX97SkFMuKw/kLlrX/A9ERFqPrl27Mn/+fAB+8pOf0K5dO6688sp687g77k5OTuNt4rvvvjvtcaZTLFv6yR7I7VrUlfXb16c4KhHJVkuWLGH48OFccskljB49mlWrVjF16lTKy8s55JBD+NnPflY371FHHcX8+fOprq6mU6dOTJ8+nZEjRzJ+/HjWrFmz13o++OADjjvuOEaMGMGECROorAzuKz9r1iyGDx/OyJEjOe644wB48803GTt2LKNGjWLEiBEsXbo0Je81li19d58NzC4vL794nzM3oktRF9ZtS/ZWrCKSCZ+/5/N7TPvXQ/6VS8deyraqbZx8/8l7lJ8/6nzOH3U+n2z7hDMePKNe2Zzz57QojnfeeYe7776b2267DYBf/OIXdOnSherqao477jjOOOMMhg0bVm+ZjRs3cuyxx/KLX/yCK664grvuuovp06c3Wcell17KRRddxNlnn82MGTO4/PLLeeihh/jpT3/KnDlz6NGjBxs2BLdi/t3vfseVV17J1772NXbu3EmqLoMfy5Z+sroWdWXd9nUp20gikv0GDx7M2LFj617PnDmT0aNHM3r0aBYuXMg777yzxzJFRUWcdFJwG+QxY8awbNmyvdbx8ssvM2XKFADOO+88nn02uN/9kUceyXnnnccdd9zB7t27ATjiiCO45ppruOGGG1ixYgWFhYWpeJvxbOkna2i3oRxeeji7anZRkFcQdTgishd7a5kX5xfvtbxbcbcW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\n",
110 | "text/plain": [
111 | ""
112 | ]
113 | },
114 | "metadata": {
115 | "needs_background": "light"
116 | },
117 | "output_type": "display_data"
118 | },
119 | {
120 | "data": {
121 | "image/png": "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\n",
122 | "text/plain": [
123 | ""
124 | ]
125 | },
126 | "metadata": {
127 | "needs_background": "light"
128 | },
129 | "output_type": "display_data"
130 | }
131 | ],
132 | "source": [
133 | "dr, Sp = train(args)"
134 | ]
135 | },
136 | {
137 | "cell_type": "code",
138 | "execution_count": 5,
139 | "metadata": {},
140 | "outputs": [
141 | {
142 | "name": "stdout",
143 | "output_type": "stream",
144 | "text": [
145 | "Read from a DOSY-Toolbox exported file using h5py.\n",
146 | "step: 1, total loss: 379868.906250, fidelity loss: 379850.187500\n",
147 | "time cost: 0.03461419999996451\n",
148 | "step: 1001, total loss: 19.047012, fidelity loss: 17.793274\n",
149 | "time cost: 2.6477562000000034\n",
150 | "step: 2001, total loss: 15.529176, fidelity loss: 14.248285\n",
151 | "time cost: 5.1703884999999445\n",
152 | "step: 3001, total loss: 13.089105, fidelity loss: 11.733995\n",
153 | "time cost: 7.674497999999971\n",
154 | "step: 4001, total loss: 10.068211, fidelity loss: 8.601408\n",
155 | "time cost: 10.155943900000011\n",
156 | "step: 5001, total loss: 6.934855, fidelity loss: 5.317183\n",
157 | "time cost: 12.694168600000012\n",
158 | "step: 6001, total loss: 4.484639, fidelity loss: 2.695631\n",
159 | "time cost: 15.181555900000035\n",
160 | "step: 7001, total loss: 3.100656, fidelity loss: 1.167302\n",
161 | "time cost: 17.658634000000006\n",
162 | "step: 8001, total loss: 2.495976, fidelity loss: 0.458315\n",
163 | "time cost: 20.16507820000004\n",
164 | "step: 9001, total loss: 2.338868, fidelity loss: 0.231958\n",
165 | "time cost: 22.82288559999995\n",
166 | "step: 10001, total loss: 2.309683, fidelity loss: 0.176661\n",
167 | "time cost: 25.422677499999963\n",
168 | "step: 11001, total loss: 2.292607, fidelity loss: 0.151408\n",
169 | "time cost: 28.010366699999963\n",
170 | "step: 12001, total loss: 2.279970, fidelity loss: 0.130374\n",
171 | "time cost: 30.57205650000003\n",
172 | "step: 13001, total loss: 2.272907, fidelity loss: 0.117183\n",
173 | "time cost: 33.14687240000001\n",
174 | "step: 14001, total loss: 2.265995, fidelity loss: 0.108921\n",
175 | "time cost: 35.683316099999956\n",
176 | "step: 15001, total loss: 2.257128, fidelity loss: 0.101676\n",
177 | "time cost: 38.272844400000054\n",
178 | "step: 16001, total loss: 2.256473, fidelity loss: 0.101180\n",
179 | "time cost: 40.76439530000005\n",
180 | "step: 17001, total loss: 2.255429, fidelity loss: 0.100378\n",
181 | "time cost: 43.23417919999997\n",
182 | "step: 18001, total loss: 2.253794, fidelity loss: 0.099156\n",
183 | "time cost: 45.781824700000016\n",
184 | "step: 19001, total loss: 2.251328, fidelity loss: 0.097481\n",
185 | "time cost: 48.303958100000045\n",
186 | "step: 20001, total loss: 2.247818, fidelity loss: 0.095420\n",
187 | "time cost: 50.761437\n",
188 | "step: 21001, total loss: 2.244012, fidelity loss: 0.092711\n",
189 | "time cost: 53.243054099999995\n",
190 | "step: 22001, total loss: 2.240240, fidelity loss: 0.089961\n",
191 | "time cost: 55.697200599999974\n",
192 | "step: 23001, total loss: 2.234525, fidelity loss: 0.083973\n",
193 | "time cost: 58.14847420000001\n",
194 | "step: 24001, total loss: 2.228383, fidelity loss: 0.078165\n",
195 | "time cost: 60.64145529999996\n",
196 | "step: 25001, total loss: 2.221231, fidelity loss: 0.074741\n",
197 | "time cost: 63.17946889999996\n",
198 | "step: 26001, total loss: 2.216194, fidelity loss: 0.074528\n",
199 | "time cost: 65.72734579999997\n",
200 | "step: 27001, total loss: 2.214769, fidelity loss: 0.075680\n",
201 | "time cost: 68.18331479999995\n",
202 | "step: 28001, total loss: 2.214724, fidelity loss: 0.075697\n",
203 | "time cost: 70.65880570000002\n",
204 | "step: 29001, total loss: 2.214718, fidelity loss: 0.075675\n",
205 | "time cost: 73.16848600000003\n",
206 | "Loop over\n",
207 | "Total time cost: 75.74381519999997\n",
208 | "Final step: 30000, total loss: 2.214717, fidelity loss: 0.075652\n"
209 | ]
210 | },
211 | {
212 | "data": {
213 | "image/png": "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\n",
214 | "text/plain": [
215 | ""
216 | ]
217 | },
218 | "metadata": {
219 | "needs_background": "light"
220 | },
221 | "output_type": "display_data"
222 | },
223 | {
224 | "data": {
225 | "image/png": "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\n",
226 | "text/plain": [
227 | ""
228 | ]
229 | },
230 | "metadata": {
231 | "needs_background": "light"
232 | },
233 | "output_type": "display_data"
234 | }
235 | ],
236 | "source": [
237 | "# Case 2: GSP data\n",
238 | "#args.input_file = 'data/GSP/GSP_net_input.mat'# If you are using specially defined data\n",
239 | "args.input_file = 'data/GSP/GSP.mat'# If you are using data exported from DOSYToolbox\n",
240 | "args.threshold = 0.03 # for DOSYToolbox data, it requires a threshold to pick the effective spectral points and remove some noise\n",
241 | "args.output_path = 'Net_Results/GSP/'\n",
242 | "args.diff_range = []\n",
243 | "args.reg_A = 0.8\n",
244 | "args.max_iter = 30000\n",
245 | "dr, Sp = train(args)"
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": 6,
251 | "metadata": {},
252 | "outputs": [
253 | {
254 | "name": "stdout",
255 | "output_type": "stream",
256 | "text": [
257 | "Read from a DOSY-Toolbox exported file using scio.\n",
258 | "step: 1, total loss: 1873468.500000, fidelity loss: 1873463.875000\n",
259 | "time cost: 0.03286389999993844\n",
260 | "step: 1001, total loss: 46.424679, fidelity loss: 46.256847\n",
261 | "time cost: 3.804725699999949\n",
262 | "step: 2001, total loss: 40.914375, fidelity loss: 40.729000\n",
263 | "time cost: 7.4198370000000295\n",
264 | "step: 3001, total loss: 37.897068, fidelity loss: 37.697392\n",
265 | "time cost: 11.020241100000021\n",
266 | "step: 4001, total loss: 34.766743, fidelity loss: 34.550510\n",
267 | "time cost: 14.656383600000026\n",
268 | "step: 5001, total loss: 30.491745, fidelity loss: 30.249701\n",
269 | "time cost: 18.259357199999954\n",
270 | "step: 6001, total loss: 24.750563, fidelity loss: 24.466539\n",
271 | "time cost: 21.887038299999972\n",
272 | "step: 7001, total loss: 17.838589, fidelity loss: 17.487690\n",
273 | "time cost: 25.502283300000045\n",
274 | "step: 8001, total loss: 11.048425, fidelity loss: 10.599142\n",
275 | "time cost: 29.123696999999993\n",
276 | "step: 9001, total loss: 5.870003, fidelity loss: 5.304039\n",
277 | "time cost: 32.758672599999954\n",
278 | "step: 10001, total loss: 2.695915, fidelity loss: 2.001148\n",
279 | "time cost: 36.35703030000002\n",
280 | "step: 11001, total loss: 1.585349, fidelity loss: 0.781792\n",
281 | "time cost: 39.99549990000003\n",
282 | "step: 12001, total loss: 1.383488, fidelity loss: 0.527154\n",
283 | "time cost: 43.57943030000001\n",
284 | "step: 13001, total loss: 1.291920, fidelity loss: 0.401052\n",
285 | "time cost: 47.20659330000001\n",
286 | "step: 14001, total loss: 1.238040, fidelity loss: 0.320954\n",
287 | "time cost: 50.78191960000004\n",
288 | "step: 15001, total loss: 1.184224, fidelity loss: 0.254291\n",
289 | "time cost: 54.371337400000016\n",
290 | "step: 16001, total loss: 1.179851, fidelity loss: 0.249661\n",
291 | "time cost: 57.99742500000002\n",
292 | "step: 17001, total loss: 1.173117, fidelity loss: 0.242280\n",
293 | "time cost: 61.708329700000036\n",
294 | "step: 18001, total loss: 1.162502, fidelity loss: 0.230776\n",
295 | "time cost: 65.33669739999993\n",
296 | "step: 19001, total loss: 1.145896, fidelity loss: 0.213156\n",
297 | "time cost: 68.92110439999999\n",
298 | "step: 20001, total loss: 1.120451, fidelity loss: 0.187015\n",
299 | "time cost: 72.54228320000004\n",
300 | "step: 21001, total loss: 1.082913, fidelity loss: 0.150171\n",
301 | "time cost: 76.12337030000003\n",
302 | "step: 22001, total loss: 1.033352, fidelity loss: 0.104808\n",
303 | "time cost: 79.72273050000001\n",
304 | "step: 23001, total loss: 0.983294, fidelity loss: 0.064992\n",
305 | "time cost: 83.40751109999997\n",
306 | "step: 24001, total loss: 0.960333, fidelity loss: 0.042523\n",
307 | "time cost: 87.0191939\n",
308 | "step: 25001, total loss: 0.946796, fidelity loss: 0.031581\n",
309 | "time cost: 90.61195450000002\n",
310 | "step: 26001, total loss: 0.934800, fidelity loss: 0.026451\n",
311 | "time cost: 94.19121759999996\n",
312 | "step: 27001, total loss: 0.909408, fidelity loss: 0.019374\n",
313 | "time cost: 97.7912665\n",
314 | "step: 28001, total loss: 0.888857, fidelity loss: 0.018887\n",
315 | "time cost: 101.44366530000002\n",
316 | "step: 29001, total loss: 0.887166, fidelity loss: 0.019381\n",
317 | "time cost: 105.03210030000002\n",
318 | "Loop over\n",
319 | "Total time cost: 108.60605029999999\n",
320 | "Final step: 30000, total loss: 0.887158, fidelity loss: 0.019355\n"
321 | ]
322 | },
323 | {
324 | "data": {
325 | "image/png": "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8HNGWFtfQqPxA8BjwCx3fzms/XJC5ZxdwDnAee5+uIHxS4yZu27WIRJNZuZAP3ffHOtY5PilkbuISAJSchcRSUAqy4iIJCCN3EVEEtAxvdJdbTp27Oi9evWKdRgiInFlzZo1n7l7p5ramkVy79WrF6tX1zbTTkREamJmW2trU1lGRCQBKbmLiCSgmCb34G4xc4uKimIZhohIwolpzT24Zsbzubm5M750ZRGJK6WlpeTn51NcXBzrUOJeeno63bp1IzU19ctXDjSLE6oiknjy8/Np3bo1vXr1ouoVoKUh3J3du3eTn59P7969672dau4i0iSKi4vp0KGDEnuEzIwOHTo0+BOQkruINBkl9uhozL+jTqiKiCSgmCZ3d3/e3WdmZWU1avuF6xcy+vejKS7TCRsRqWr37t0MGzaMYcOG0aVLF7Kzsytfl5SU1GsfV199NZs2bar3MX/3u99x4403NjbkqIrrE6q7D+5mef5yCosL6dKqS6zDEZFmpEOHDqxduxaA22+/nVatWnHzzTdXWafylnRJNY9zH3nkkSaPs6nEdc29bXpbAAqLC79kTRGRkM2bNzN48GCuvfZahg8fzs6dO5k5cya5ubkMGjSIn//855XrjhkzhrVr11JWVkbbtm2ZNWsWQ4cOZdSoUezatavO43z00UecddZZnHLKKZx33nnk54fuMb5gwQIGDx7M0KFDOeusswB47733OPXUUxk2bBinnHIKeXl5EfczrkfuLVNbAqgsIxIHvvbo145adsmgS7ju1Os4WHqQiX+ceFT7VcOu4qphV/HZwc+YsnBKlbalVy1tdCwbNmzgkUce4cEHHwTg7rvvpn379pSVlXHWWWcxZcoUBg4cWGWboqIizjzzTO6++25++MMfMm/ePGbNmlXrMa677jq++93v8u1vf5u5c+dy4403smjRIu644w6WLl3KCSecQGFhaGD6wAMPcPPNNzN16lQOHz5MNC7FHtcj9xbJLQA4XKbbO4pI/fXt25dTTz218vX8+fMZPnw4w4cPZ+PGjWzYsOGobVq2bMmECaFb5Y4YMYItW7bUeYwVK1Ywbdo0AK644gpef/11AE4//XSuuOIKfve733HkyBEARo8ezX/9139xzz33sH37dtLT0yPuY1yP3DtkdGBk9kjSUyL/hxCRplXXSDsjNaPO9o4ZHSMaqVeXmZlZ+fzD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\n",
326 | "text/plain": [
327 | ""
328 | ]
329 | },
330 | "metadata": {
331 | "needs_background": "light"
332 | },
333 | "output_type": "display_data"
334 | },
335 | {
336 | "data": {
337 | "image/png": "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\n",
338 | "text/plain": [
339 | ""
340 | ]
341 | },
342 | "metadata": {
343 | "needs_background": "light"
344 | },
345 | "output_type": "display_data"
346 | }
347 | ],
348 | "source": [
349 | "# Case 3: QGC data\n",
350 | "# args.input_file = 'data/QGC/QGC_net_input.mat' # If you are using specially defined data\n",
351 | "args.input_file = 'data/QGC/QGC.mat'# If you are using data exported from DOSYToolbox\n",
352 | "args.threshold = 0.0138 # for DOSYToolbox data, it requires a threshold to pick the effective spectral points and remove some noise\n",
353 | "args.output_path = 'Net_Results/QGC/'\n",
354 | "args.diff_range = []\n",
355 | "args.reg_A = 0.1\n",
356 | "args.max_iter = 30000\n",
357 | "args.learning_rate_1 = 2e-4\n",
358 | "args.learning_rate_2 = 1e-5\n",
359 | "\n",
360 | "dr, Sp = train(args)"
361 | ]
362 | },
363 | {
364 | "cell_type": "code",
365 | "execution_count": null,
366 | "metadata": {},
367 | "outputs": [],
368 | "source": []
369 | }
370 | ],
371 | "metadata": {
372 | "kernelspec": {
373 | "display_name": "Python 3",
374 | "language": "python",
375 | "name": "python3"
376 | },
377 | "language_info": {
378 | "codemirror_mode": {
379 | "name": "ipython",
380 | "version": 3
381 | },
382 | "file_extension": ".py",
383 | "mimetype": "text/x-python",
384 | "name": "python",
385 | "nbconvert_exporter": "python",
386 | "pygments_lexer": "ipython3",
387 | "version": "3.6.9"
388 | }
389 | },
390 | "nbformat": 4,
391 | "nbformat_minor": 2
392 | }
393 |
--------------------------------------------------------------------------------
/model_DOSY_est.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Sat Apr 25 15:50:59 2021
4 |
5 | @author: Yu & Nannan & Enping
6 |
7 | """
8 |
9 | import tensorflow as tf
10 | import numpy as np
11 |
12 |
13 | def param_gen_dosy_simple(input_r, n_alphas, n_peaks, t_in, diff_range=[]):
14 | with tf.compat.v1.variable_scope('param_gen_dosy_experiment'):
15 | conv = tf.keras.layers.Conv1D(filters=16, kernel_size=8, strides=2, activation=tf.nn.leaky_relu)(input_r)
16 | conv_flat = tf.reshape(conv, [1, -1])
17 | par_a = tf.keras.layers.Dense(units=n_alphas)(conv_flat)
18 | par_A = tf.keras.layers.Dense(units=n_alphas*n_peaks)(conv_flat)
19 |
20 | Ak = tf.reshape(tf.cos(par_A)+1.0, [n_peaks, n_alphas])
21 |
22 | if not diff_range:
23 | z = tf.reshape(tf.nn.sigmoid(par_a), [n_alphas, 1])
24 | a = -tf.math.log(z)
25 | output, C = harmonic_gen_dosy_z(z, Ak, t_in)
26 | else:
27 | z_min = np.exp(-diff_range[1])
28 | z_max = np.exp(-diff_range[0])
29 | tmp = tf.nn.sigmoid(par_a)*(z_max-z_min)+z_min
30 | z = tf.reshape(tmp, [n_alphas, 1])
31 | a = -tf.math.log(z)
32 | output, C = harmonic_gen_dosy_z(z, Ak, t_in)
33 |
34 | norm_C = tf.square(tf.norm(C, axis=1, keepdims=True))
35 |
36 | return a, Ak, output, norm_C
37 |
38 |
39 | def harmonic_gen_dosy_z(z, Ak, t):
40 | # size of z: [n_decay, 1]
41 | # size of Ak: [n_freq, n_decay]
42 | # size of t: [1, n_grad]
43 | # size of output: [n_freq, n_grad]
44 | C = tf.math.pow(z, t)
45 | output = tf.matmul(Ak, C)
46 | return output, C
47 |
48 |
--------------------------------------------------------------------------------
/train_DOSYEst.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Sat Apr 25 16:10:19 2020
4 |
5 | @author: Yu
6 | """
7 |
8 | import time
9 | import tensorflow as tf
10 | import numpy as np
11 | #import random
12 | #from tensorflow import set_random_seed
13 | import matplotlib.pyplot as plt
14 | import argparse
15 |
16 | from model_DOSY_est import param_gen_dosy_simple
17 | import utils
18 |
19 |
20 | def options():
21 | # Set parameters
22 | parser = argparse.ArgumentParser()
23 | parser.add_argument("--learning_rate_1", default=1e-4, type=float, help='Learning rate for the first 15000 steps')
24 | parser.add_argument("--learning_rate_2", default=5e-6, type=float, help='Learning rate after 15000 steps')
25 | parser.add_argument("--input_file", default='data/simulation/testdataSigma0.015.mat', help='File path of the input data')
26 | parser.add_argument("--data_matrix_proc", default='real', type = str, help='real/abs')
27 | parser.add_argument("--threshold", default=0.01, type=float, help='Threshold for choosing the spectral points')
28 | parser.add_argument("--output_path", default='Net_Results/', help='Output file path')
29 | parser.add_argument("--diff_range", default=[], type=float, help='The range of the diffusion coefficient')
30 | parser.add_argument("--n_decay", default=3, type=int, help='The number of different decay components')
31 | parser.add_argument("--dim_in", default=100, type=int, help='Dimension of the input of the network')
32 | parser.add_argument("--reg_A", default=0.01, type=float, help='Regularization parameter')
33 | parser.add_argument("--max_iter", default=20000, type=int, help='Maximum iterations')
34 | parser.add_argument("--fidelity", default='norm-2', help='Fidelity term setting, could be norm-1 or norm-2')
35 | parser.add_argument("--save_process", default=True, help='If you want to save the intermediate process parameters?')
36 | parser.add_argument("--save_interval", default=500, type=int, help='The step size to save the intermediate results')
37 | parser.add_argument("--display", default=True, help='Do you want to show the convergence process in the terminal?')
38 |
39 | args = parser.parse_args()
40 | return args
41 |
42 |
43 | def train(args):
44 | # 1. load data & generate the input
45 | nmrdata_save = utils.read_mat_dosy(args)
46 | label_data0 = nmrdata_save['S']
47 | b = nmrdata_save['b']
48 | n_grad = b.shape[1]
49 | n_freq = label_data0.shape[0]
50 | input_r = np.random.randn(1,args.dim_in)
51 | input_r = np.expand_dims(input_r,axis=2)
52 |
53 | # 2. Build graph
54 | tf.compat.v1.reset_default_graph()
55 | net1_input = tf.compat.v1.placeholder(tf.float32, shape=[1, args.dim_in, 1], name="net_input")
56 | label1_output = tf.compat.v1.placeholder(tf.float32, shape=[n_freq, n_grad], name="output_label")
57 | b_in = tf.compat.v1.placeholder(tf.float32, shape=[1, n_grad], name='b_in')
58 |
59 | # load the neural network model
60 | dr1_out, sp1_out, X_output, normC_out = param_gen_dosy_simple(net1_input, args.n_decay, n_freq, b_in, args.diff_range) #
61 |
62 | norm_label_tensor = tf.math.reduce_max(label1_output,axis=1,keepdims=True)
63 |
64 | with tf.name_scope('loss'):
65 | err_tmp = label1_output - X_output
66 | if args.fidelity == 'norm-2':
67 | fidelity_loss = tf.square(tf.norm(err_tmp, ord='euclidean'))
68 | elif args.fidelity == 'norm-1':
69 | fidelity_loss = tf.reduce_sum(tf.math.abs(err_tmp))
70 | else:
71 | print('Undefined fidelity term. Please input: norm-1 or norm-2 for "fidelity"')
72 | return
73 |
74 | sp1_weighted = (sp1_out*norm_label_tensor)/tf.transpose(normC_out)
75 | LW1_out = tf.reduce_sum(tf.reshape(sp1_weighted,[-1]))
76 | train_loss = fidelity_loss + args.reg_A * LW1_out
77 |
78 | learning_rate = tf.compat.v1.placeholder(tf.float32, [])
79 | train_opt = tf.compat.v1.train.AdamOptimizer(learning_rate).minimize(train_loss)
80 |
81 | # 3. Prepare to save the results
82 | Folder_name = utils.make_folder(args.output_path)
83 | tf_log_dir = str(Folder_name)+'model'
84 |
85 | ### observed variabel #####
86 | loss_train = []
87 | dr_save = []
88 | Sp_save = []
89 | iter_steps = []
90 |
91 | # 4. Training: optimizing the output parameters
92 | config = tf.compat.v1.ConfigProto()
93 | config.gpu_options.allow_growth = True
94 | with tf.compat.v1.Session(config=config) as sess:
95 | sess.run(tf.compat.v1.global_variables_initializer())
96 | saver = tf.compat.v1.train.Saver(max_to_keep=5)
97 | saver.save(sess, tf_log_dir+'/model.ckpt-done')
98 |
99 | #checkpoint = tf.train.latest_checkpoint(checkpoint_dir)
100 | # t0 = time.perf_counter()
101 | t0 = time.clock()
102 |
103 | for i in range(args.max_iter):
104 | if i<1.5e4:
105 | lr = args.learning_rate_1
106 | else:
107 | lr = args.learning_rate_2
108 |
109 | feed_dict = {
110 | net1_input:input_r,
111 | label1_output:label_data0,
112 | b_in:b,
113 | learning_rate:lr,
114 | }
115 | gen_loss_tmp = sess.run(train_loss,feed_dict=feed_dict)
116 | loss_train.append(gen_loss_tmp)
117 |
118 | if args.display or args.save_process:
119 | if (i % args.save_interval) == 0:
120 | dr, Sp, fl = sess.run([dr1_out,sp1_out,fidelity_loss], feed_dict=feed_dict)
121 | if args.save_process:
122 | dr_save = np.concatenate([dr_save,np.reshape(dr,[-1])])
123 | Sp_save = np.concatenate([Sp_save,np.reshape(Sp,[-1])])
124 | iter_steps = np.append(iter_steps,i)
125 | if args.display:
126 | print('step: %d, total loss: %f, fidelity loss: %f' %(i + 1, gen_loss_tmp, fl))
127 | t1 = time.clock()
128 | print('time cost: %s' % (t1 - t0))
129 |
130 | _ = sess.run(train_opt, feed_dict=feed_dict)
131 |
132 |
133 | print("Loop over")
134 | traintime = time.clock() - t0
135 | print('Total time cost: %s' % traintime)
136 |
137 | dr, Sp, gen_loss_tmp, fl = sess.run([dr1_out,sp1_out,train_loss,fidelity_loss], feed_dict=feed_dict)
138 | print('Final step: %d, total loss: %f, fidelity loss: %f' %(i + 1, gen_loss_tmp, fl))
139 | dr_save = np.concatenate([dr_save,np.reshape(dr,[-1])])
140 | Sp_save = np.concatenate([Sp_save,np.reshape(Sp,[-1])])
141 | iter_steps = np.append(iter_steps,args.max_iter)
142 |
143 | utils.save_param_dosy(dr_save, Sp_save, nmrdata_save, args.n_decay, n_freq, Folder_name)
144 |
145 | if args.display:
146 | utils.draw_loss(i, loss_train)
147 | if args.save_process:
148 | dr_save = np.reshape(dr_save,[-1,args.n_decay])
149 | plt.figure()
150 | plt.plot(iter_steps, dr_save)
151 | plt.show()
152 |
153 | return dr, Sp
154 |
155 |
156 | if __name__ == '__main__':
157 | args = options()
158 | print(args)
159 |
160 | dr, Sp = train(args)
161 |
162 |
163 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on Sat Apr 25 16:22:19 2020
4 |
5 | @author: Yu
6 | """
7 | import numpy as np
8 | import scipy.io as scio
9 | import h5py
10 | import time
11 | import os
12 | import pandas as pd
13 | import matplotlib.pyplot as plt
14 | import warnings
15 |
16 |
17 | def __clear_env():
18 | for key in list(globals().keys()):
19 | if not key.startswith("__"):
20 | globals().pop(key)
21 |
22 |
23 | def read_mat_dosy(args):
24 | """
25 | Input:
26 | pathname: the path of the .mat file
27 | thr: threshold value (only effective when the input is an DOSYToolbox exported .mat file)
28 | Output:
29 | label_data: DOSY spectral data with size [N_freq, N_grad]
30 | b_data: vector related to the gradients with size [1, N_grad]
31 | ppm: the chemical shift coordinates (in ppm) of the original data
32 | idx_peaks: the indices of the selected spectral points
33 | """
34 | pathname = args.input_file
35 | thr = args.threshold
36 |
37 | nmrdata_save = dict()
38 | try:
39 | read_data = scio.loadmat(str(pathname))
40 | if read_data.__contains__('S') & read_data.__contains__('b'):
41 | print('Read from a user-defined Mat file.')
42 | nmrdata_save['S'] = read_data['S']
43 | nmrdata_save['b'] = read_data['b'].astype(float)
44 | if read_data.__contains__('ppm'):
45 | nmrdata_save['ppm'] = np.squeeze(read_data['ppm'])
46 | if read_data.__contains__('idx_peaks'):
47 | nmrdata_save['idx_peaks'] = read_data['idx_peaks']
48 |
49 | elif read_data.__contains__('NmrData'):
50 | print('Read from a DOSY-Toolbox exported file using scio.')
51 | nmrdata = read_data['NmrData']
52 | # b-vector
53 | expfactor = np.squeeze(nmrdata['dosyconstant'][0,0])*1e-10
54 | ngrad = int(nmrdata['ngrad'][0,0])
55 | g = nmrdata['Gzlvl'][0,0]
56 | if g.shape[0]>g.shape[1]:
57 | g = np.transpose(g)
58 | nmrdata_save['b'] = expfactor*g**2
59 |
60 | # Spectrum data
61 | if args.data_matrix_proc == 'real':
62 | specdata = np.real(nmrdata['SPECTRA'][0,0])
63 | elif args.data_matrix_proc == 'abs':
64 | specdata = np.abs(nmrdata['SPECTRA'][0,0])
65 | else:
66 | specdata = np.abs(nmrdata['SPECTRA'][0,0])
67 | warnings.warn('Unrecognized input setting: data_matrix_proc. Apply ''abs'' instead.')
68 |
69 | if specdata.shape[1] != ngrad:
70 | specdata = np.transpose(specdata)
71 | specdata = specdata/specdata.max()
72 |
73 | thr = max(thr, 0.0)
74 | spec0 = specdata[:,0]
75 | idx_peaks = np.asarray(np.where(spec0>thr))
76 | nmrdata_save['idx_peaks'] = idx_peaks
77 | nmrdata_save['S'] = specdata[np.squeeze(idx_peaks),:]
78 | nmrdata_save['ppm'] = np.squeeze(nmrdata['Specscale'][0,0])
79 |
80 | else:
81 | print('Reading File Error: make sure there are ''S'', ''b'' or ''NmrData'' in your .mat file')
82 |
83 | except NotImplementedError:
84 | read_data = h5py.File(str(pathname),'r')
85 | nmrdata = read_data.get('NmrData')
86 | print('Read from a DOSY-Toolbox exported file using h5py.')
87 | # b-vector
88 | expfactor = nmrdata.get('dosyconstant')[0,0]*1e-10
89 | ngrad = int(nmrdata.get('ngrad')[0,0])
90 | g = nmrdata.get('Gzlvl')[()]
91 | if g.shape[0]>g.shape[1]:
92 | g = np.transpose(g)
93 | nmrdata_save['b'] = expfactor*g**2
94 |
95 | # Spectrum data
96 | if args.data_matrix_proc == 'real':
97 | specdata = nmrdata['SPECTRA']['real']
98 | else:
99 | specdata_r = nmrdata['SPECTRA']['real']
100 | specdata_i = nmrdata['SPECTRA']['imag']
101 | specdata = np.sqrt(specdata_r**2 + specdata_i**2)
102 | if args.data_matrix_proc != 'abs':
103 | warnings.warn('Unrecognized input setting: data_matrix_proc. Apply ''abs'' instead.')
104 |
105 | if specdata.shape[1] != ngrad:
106 | specdata = np.transpose(specdata)
107 | specdata = specdata/specdata.max()
108 |
109 | thr = max(thr, 0.0)
110 | spec0 = specdata[:,0]
111 | idx_peaks = np.asarray(np.where(spec0>thr))
112 | nmrdata_save['idx_peaks'] = idx_peaks
113 | nmrdata_save['S'] = specdata[np.squeeze(idx_peaks),:]
114 | nmrdata_save['ppm'] = np.squeeze(nmrdata['Specscale'])
115 |
116 |
117 | return nmrdata_save
118 |
119 |
120 | def make_folder(BaseDir):
121 | Name_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())
122 | subsubFolderName = str(Name_time)
123 | FolderName = '%s%s/' % (BaseDir,subsubFolderName)
124 | if not os.path.isdir(BaseDir):
125 | os.makedirs(FolderName)
126 | else:
127 | os.mkdir(FolderName)
128 |
129 | os.mkdir('%smodel/' % (FolderName))
130 | return FolderName
131 |
132 |
133 | def save_csv(data, FolderName, file_name, shape, is_real):
134 | if is_real == 0:
135 | y2 = np.concatenate([np.squeeze(np.real(data)),np.squeeze(np.imag(data))])
136 | y2 = np.reshape(y2,[2,-1])
137 | df = pd.DataFrame(y2)
138 | df.to_csv(str(FolderName) + str(file_name),index_label='0rl\\1im')
139 | else:
140 | data = np.reshape(data, shape)
141 | df = pd.DataFrame(data)
142 | df.to_csv(str(FolderName) + str(file_name), index=0,header=0)
143 |
144 |
145 | def save_param_dosy(aout1, Akout1, nmrdata, alpha_num, peak_num, FolderName):
146 | save_csv(aout1, FolderName, file_name='diffusion_coeffs.csv', shape=[-1,alpha_num], is_real=1)
147 | save_csv(Akout1, FolderName, file_name='Sp.csv', shape=[-1,peak_num * alpha_num], is_real=1)
148 | matfile = '%sdata_org.mat'%FolderName
149 | scio.savemat(file_name=matfile, mdict=nmrdata)
150 |
151 |
152 | def draw_netoutput_DOSY(aout, Akout, step=0, save=0, FolderName=None):
153 | n_peaks = Akout.shape[0]
154 | n_dr = Akout.shape[1]
155 | # Akout = np.transpose(Akout)
156 | index = np.arange(1,n_peaks+1)
157 | fig = plt.figure()
158 | ax = fig.add_subplot(1,2,1,projection='3d')
159 |
160 | for i in range(n_peaks):
161 | x = aout
162 | y = np.ones(Akout.shape[1])*(i+1)
163 | z = Akout[i,:]
164 | ax.scatter(x, y, z, c='red',s=20)
165 | #ax.plot3D(x,y,z,'red')
166 |
167 | plt.subplot(122)
168 |
169 | for j in range(len(aout)):
170 | indexj = index[Akout[:, j] > 1e-3]
171 | plt.plot(indexj, [aout[j]] * len(indexj), 'ro')
172 |
173 | if save == 1:
174 | fnm = '%sPara_%s.png' % (FolderName,step)
175 | plt.savefig(fnm)
176 | plt.close()
177 |
178 | plt.show()
179 |
180 |
181 | def draw_loss(i,loss_train, save=0, FolderName=None):
182 | plt.figure()
183 | plt.plot(loss_train, 'g--', label='Train loss')
184 | plt.yscale('log')
185 | plt.legend()
186 | plt.title('MSE loss: step=%s' % (i))
187 | if save == 1:
188 | fnm = '%sloss_%s.png' % (FolderName,i)
189 | plt.savefig(fnm)
190 | plt.close()
191 | else:
192 | plt.show()
193 |
194 |
195 | def draw_recDOSY(diff_data, Sp):
196 | n_peaks = Sp.shape[0]
197 | y = np.squeeze(diff_data)
198 | x = np.arange(1,n_peaks+1,1)
199 | plt.figure()
200 | plt.contour(x,y,Sp.transpose())
201 | plt.show()
202 |
203 |
--------------------------------------------------------------------------------