diff --git a/CDTools/datasets/ptycho_2d_dataset.py b/CDTools/datasets/ptycho_2d_dataset.py index 6e224b0..fbd5f9a 100644 --- a/CDTools/datasets/ptycho_2d_dataset.py +++ b/CDTools/datasets/ptycho_2d_dataset.py @@ -62,7 +62,7 @@ class Ptycho2DDataset(CDataset): self.translations = t.tensor(translations) self.patterns = t.tensor(patterns) if self.mask is None: - self.mask = t.ones(self.patterns.shape[-2:]).to(dtype=t.uint8) + self.mask = t.ones(self.patterns.shape[-2:]).to(dtype=t.bool) self.mask.masked_fill_(t.isnan(t.sum(self.patterns,dim=(0,))),0) self.patterns.masked_fill_(t.isnan(self.patterns),0) @@ -181,7 +181,7 @@ class Ptycho2DDataset(CDataset): cdtdata.add_ptycho_translations(cxi_file, self.translations) - def inspect(self): + def inspect(self, logarithmic=True): """Launches an interactive plot for perusing the data This launches an interactive plotting tool in matplotlib that @@ -249,9 +249,12 @@ class Ptycho2DDataset(CDataset): axes[0].set_ylabel('Translation y (um)') cb1 = plt.colorbar(nanomap, ax=axes[0], orientation='horizontal',format='%.2e',ticks=ticker.LinearLocator(numticks=5),pad=0.15,fraction=0.1) cb1.ax.tick_params(labelrotation=20) - - meas = axes[1].imshow(np.log(meas_data) / np.log(10) * mask) + if logarithmic: + meas = axes[1].imshow(np.log(meas_data) / np.log(10) * mask) + else: + meas = axes[1].imshow(meas_data * mask) + cb2 = plt.colorbar(meas, ax=axes[1], orientation='horizontal',format='%.2e',ticks=ticker.LinearLocator(numticks=5),pad=0.15,fraction=0.1) cb2.ax.tick_params(labelrotation=20) cb2.ax.callbacks.connect('xlim_changed', lambda ax: update_colorbar(meas)) @@ -277,7 +280,11 @@ class Ptycho2DDataset(CDataset): meas = axes[1].images[-1] - meas.set_data(np.log(meas_data) / np.log(10) * mask) + if logarithmic: + meas.set_data(np.log(meas_data) / np.log(10) * mask) + else: + meas.set_data(meas_data * mask) + update_colorbar(meas) diff --git a/CDTools/models/__init__.py b/CDTools/models/__init__.py index 1eb7802..8dc91a7 100644 --- a/CDTools/models/__init__.py +++ b/CDTools/models/__init__.py @@ -131,13 +131,17 @@ class CDIModel(t.nn.Module): scheduler : torch.optim.lr_scheduler._LRScheduler Optional, a learning rate scheduler to use """ - + # First, calculate the normalization + normalization = 0 + for inputs, patterns in data_loader: + normalization += t.sum(patterns).cpu().numpy() + + for it in range(iterations): loss = 0 N = 0 for inputs, patterns in data_loader: N += 1 - def closure(): optimizer.zero_grad() sim_patterns = self.forward(*inputs) @@ -151,14 +155,14 @@ class CDIModel(t.nn.Module): loss += optimizer.step(closure).detach().cpu().numpy() - loss /= N + loss /= normalization if scheduler is not None: scheduler.step(loss) - + yield loss - def Adam_optimize(self, iterations, dataset, batch_size=15, lr=0.005, schedule=False): + def Adam_optimize(self, iterations, dataset, batch_size=15, lr=0.005, schedule=False, amsgrad=False): """Runs a round of reconstruction using the Adam optimizer This is generally accepted to be the most robust algorithm for use @@ -184,7 +188,7 @@ class CDIModel(t.nn.Module): shuffle=True) # Define the optimizer - optimizer = t.optim.Adam(self.parameters(), lr = lr) + optimizer = t.optim.Adam(self.parameters(), lr = lr, amsgrad=amsgrad) # Define the scheduler @@ -234,6 +238,46 @@ class CDIModel(t.nn.Module): return self.AD_optimize(iterations, data_loader, optimizer) + def SGD_optimize(self, iterations, dataset, batch_size=None, + lr=0.01, momentum=0, dampening=0, weight_decay=0, + nesterov=False): + """Runs a round of reconstruction using the SGDoptimizer + + This algorithm is often less stable that Adam, but it is simpler + and is the basic workhorse of gradience descent. + + Parameters + ---------- + iterations : int + How many epochs of the algorithm to run + dataset : CDataset + The dataset to reconstruct against + batch_size : int + Optional, the size of the minibatches to use + lr : float + Optional, the learning rate to use + momentum : float + Optional, the length of the history to use. + """ + + # Make a dataloader + if batch_size is not None: + data_loader = torchdata.DataLoader(dataset, batch_size=batch_size, + shuffle=True) + else: + data_loader = torchdata.DataLoader(dataset) + + + # Define the optimizer + optimizer = t.optim.SGD(self.parameters(), + lr = lr, momentum=momentum, + dampening=dampening, + weight_decay=weight_decay, + nesterov=nesterov) + + return self.AD_optimize(iterations, data_loader, optimizer) + + # By default, the plot_list is empty plot_list = [] diff --git a/CDTools/models/fancy_ptycho.py b/CDTools/models/fancy_ptycho.py index fe2246c..4f64030 100644 --- a/CDTools/models/fancy_ptycho.py +++ b/CDTools/models/fancy_ptycho.py @@ -46,7 +46,7 @@ class FancyPtycho(CDIModel): if mask is None: self.mask = mask else: - self.mask = t.ByteTensor(mask) + self.mask = t.BoolTensor(mask) # We rescale the probe here so it learns at the same rate as the # object @@ -175,7 +175,7 @@ class FancyPtycho(CDIModel): weights = t.ones(len(dataset)) if hasattr(dataset, 'mask') and dataset.mask is not None: - mask = dataset.mask.to(t.uint8) + mask = dataset.mask.to(t.bool) else: mask = None diff --git a/CDTools/models/simple_ptycho.py b/CDTools/models/simple_ptycho.py index 5e5deb1..937389c 100644 --- a/CDTools/models/simple_ptycho.py +++ b/CDTools/models/simple_ptycho.py @@ -98,7 +98,7 @@ class SimplePtycho(CDIModel): if hasattr(dataset, 'mask') and dataset.mask is not None: - mask = dataset.mask.to(t.uint8) + mask = dataset.mask.to(t.bool) else: mask = None diff --git a/CDTools/tools/analysis.py b/CDTools/tools/analysis.py index da24b7a..dc4ee22 100644 --- a/CDTools/tools/analysis.py +++ b/CDTools/tools/analysis.py @@ -148,15 +148,13 @@ def standardize(probe, obj, obj_slice=None, correct_ramp=False): if correct_ramp: - # Need to check if this is actually working and, if noy, why not - center_freq = ip.centroid_sq(cmath.fftshift(t.fft(probe[0],2)),comp=True) + # Need to check if this is actually working and, if not, why not + center_freq = ip.centroid(cmath.cabssq(cmath.fftshift(t.fft(probe[0],2)))) center_freq -= (t.tensor(probe[0].shape[:-1]) // 2).to(t.float32) center_freq /= t.tensor(probe[0].shape[:-1]).to(t.float32) - - Is, Js = np.mgrid[:probe[0].shape[0],:probe[0].shape[1]] - probe_phase_ramp = cmath.expi(2*np.pi * + probe_phase_ramp = cmath.expi(2 * np.pi * (center_freq[0] * t.tensor(Is).to(t.float32) + center_freq[1] * t.tensor(Js).to(t.float32))) probe = cmath.cmult(probe, cmath.cconj(probe_phase_ramp)) @@ -165,7 +163,6 @@ def standardize(probe, obj, obj_slice=None, correct_ramp=False): (center_freq[0] * t.tensor(Is).to(t.float32) + center_freq[1] * t.tensor(Js).to(t.float32))) obj = cmath.cmult(obj, obj_phase_ramp) - # Then, we set them to consistent absolute phases @@ -475,7 +472,7 @@ def calc_frc(im1, im2, basis, im_slice=None, nbins=None, snr=1.): (im1.shape[1]//8)*3:(im1.shape[1]//8)*5] if nbins is None: - nbins = np.max(synth_obj[im_slice].shape) // 4 + nbins = np.max(im1[im_slice].shape) // 4 cor_fft = cmath.cmult(cmath.fftshift(t.fft(im1[im_slice],2)), @@ -490,7 +487,7 @@ def calc_frc(im1, im2, basis, im_slice=None, nbins=None, snr=1.): i_freqs = fftpack.fftshift(fftpack.fftfreq(cor_fft.shape[0],d=di)) j_freqs = fftpack.fftshift(fftpack.fftfreq(cor_fft.shape[1],d=dj)) - + Js,Is = np.meshgrid(j_freqs,i_freqs) Rs = np.sqrt(Is**2+Js**2) diff --git a/CDTools/tools/data.py b/CDTools/tools/data.py index 8ee8849..6f955b6 100644 --- a/CDTools/tools/data.py +++ b/CDTools/tools/data.py @@ -278,7 +278,7 @@ def get_mask(cxi_file): mask = np.array(i1['detector_1/mask']).astype(np.uint32) mask_on = np.equal(mask,np.uint32(0)) mask_has_signal = np.equal(mask,np.uint32(0x00001000)) - return np.logical_or(mask_on,mask_has_signal).astype(np.uint8) + return np.logical_or(mask_on,mask_has_signal).astype(np.bool) else: return None diff --git a/CDTools/tools/initializers.py b/CDTools/tools/initializers.py index 52326e8..961752d 100644 --- a/CDTools/tools/initializers.py +++ b/CDTools/tools/initializers.py @@ -73,6 +73,7 @@ def exit_wave_geometry(det_basis, det_shape, wavelength, distance, center=None, # In some edge cases this shape can be smaller than the detector shape full_shape = t.max(full_shape, det_shape) + if opt_for_fft: full_shape = t.Tensor([next_fast_len(dim) for dim in full_shape]).to(t.int32) diff --git a/CDTools/tools/losses.py b/CDTools/tools/losses.py index 2f92a45..f49ac8f 100644 --- a/CDTools/tools/losses.py +++ b/CDTools/tools/losses.py @@ -50,11 +50,11 @@ def amplitude_mse(intensities, sim_intensities, mask=None): if mask is None: return t.sum((t.sqrt(sim_intensities) - - t.sqrt(intensities))**2) / intensities.view(-1).shape[0] + t.sqrt(intensities))**2) else: masked_intensities = intensities.masked_select(mask) return t.sum((t.sqrt(sim_intensities.masked_select(mask)) - - t.sqrt(masked_intensities))**2) / masked_intensities.shape[0] + t.sqrt(masked_intensities))**2) diff --git a/examples/example_reconstructions/gold_balls.pickle b/examples/example_reconstructions/gold_balls.pickle index 8db6ff1..2410051 100644 Binary files a/examples/example_reconstructions/gold_balls.pickle and b/examples/example_reconstructions/gold_balls.pickle differ diff --git a/examples/example_reconstructions/lab_ptycho.pickle b/examples/example_reconstructions/lab_ptycho.pickle index 395ae77..25e6ab5 100644 Binary files a/examples/example_reconstructions/lab_ptycho.pickle and b/examples/example_reconstructions/lab_ptycho.pickle differ diff --git a/examples/specular_ptycho.py b/examples/specular_ptycho.py index d85d7ba..f8615a3 100644 --- a/examples/specular_ptycho.py +++ b/examples/specular_ptycho.py @@ -2,6 +2,7 @@ from __future__ import division, print_function, absolute_import import CDTools from matplotlib import pyplot as plt +import numpy as np # This file is too large to be distributed via Github.