def test_operator_fourier_transform(self): # Define "true" FFTs Ft = lambda x: np.fft.fftshift(np.fft.fft2(np.fft.fftshift(x, axes=(0, 1)), axes=(0, 1), norm='ortho'), axes=(0, 1)) iFt = lambda x: np.fft.fftshift(np.fft.ifft2(np.fft.fftshift(x, axes=(0, 1)), axes=(0, 1), norm='ortho'), axes=(0, 1)) eps_fft = yp.precision(self.x, for_sum=True) if global_backend == 'numpy': fft_backends = ['scipy', 'numpy'] else: fft_backends = ['af'] for fft_backend in fft_backends: # Create Operator F = ops.FourierTransform(image_size, dtype=global_dtype, axes=(0, 1), fft_backend=fft_backend, center=True, backend=global_backend) # Check forward model assert yp.sumb(yp.abs(Ft(self.x).reshape(image_size) - yp.changeBackend(F * self.x, 'numpy').reshape(image_size))) < eps_fft, '%f' % yp.sumb(yp.abs(Ft(x).reshape(image_size) - yp.changeBackend(F * vec(self.x), 'numpy').reshape(image_size))) assert yp.sumb(yp.abs(iFt(self.x).reshape(image_size) - yp.changeBackend((F.H * self.x), 'numpy').reshape(image_size))) < eps_fft # Check reciprocity assert yp.sumb(yp.abs(F * F.H * self.x - self.x)) < eps_fft, "%.4e" % yp.sumb(yp.abs(F * F.H * vec(self.x) - vec(self.x))) # Check Gradient F.gradient_check()
def test_methanical_condition_number(self): ''' Mechanical test of condition number calculation ''' # Unitary Matrix F = ops.FourierTransform(image_size, dtype=global_dtype, backend=global_backend) assert F.condition_number == 1 assert not F.condition_number_is_upper_bound # Matrix with a condition number hh = yp.changeBackend((np.random.rand(image_size[0], image_size[1]) + 0.1).astype(np.complex64), global_backend) D = ops.Diagonalize(hh, dtype=global_dtype, backend=global_backend) assert not D.condition_number_is_upper_bound # Product of two unitary matricies assert (F * F).condition_number == 1 assert not (F * F).condition_number_is_upper_bound # Product of one unitary and one non-singular matrix assert (F * D).condition_number == D.condition_number assert not (F * D).condition_number_is_upper_bound # because one matrix is unitary, this condition number is NOT an upper bound. This can be checked numerically. # Product of two non-singular matricies. hh_2 = yp.changeBackend((np.random.rand(image_size[0], image_size[1]) + 0.1).astype(np.complex64), global_backend) D2 = ops.Diagonalize(hh_2, dtype=global_dtype, backend=global_backend) assert (D * D2).condition_number >= D.condition_number assert (D * D2).condition_number_is_upper_bound
def test_mechanical_smooth_flag(self): F = ops.FourierTransform(image_size, dtype=global_dtype, axes=(0, 1)) # Linear Operator L2 = ops.L2Norm(image_size[0] * image_size[1], dtype=global_dtype, backend=global_backend) # Non-linear operator assert F.linear assert not L2.linear assert not (L2 * F).linear assert (F + F).linear assert not (L2 * F + L2 * F).linear
def test_mechanical_gradient_2(self): ''' Mechanical test for calculating the gradient of chained linear operators ''' F = ops.FourierTransform(image_size, dtype=global_dtype, axes=(0, 1)) D = ops.Diagonalize(self.h, dtype=global_dtype, backend=global_backend) A = F.H * D * F A.label = 'A' # Check gradient numerically A.gradient_check()
def test_mechanical_sum_of_norms(self): L2 = ops.L2Norm(image_size[0] * image_size[1], dtype=global_dtype, backend=global_backend) F = ops.FourierTransform(image_size, dtype=global_dtype, axes=(0, 1)) D = ops.Diagonalize(self.h, dtype=global_dtype, backend=global_backend) O_1 = L2 * ((F.H * D * F) - self.y) O_2 = 1e-3 * L2 * F O = O_1 + O_2 # Check gradient operator (adjoint form) O.gradient_check()
def test_mechanical_gradient_4(self): ''' Mechanical test for an outer non-linear operator with outer non-linear operator and a linear operator in-between''' shift_true = np.asarray((-5,3)).astype(yp.getNativeDatatype(global_dtype, 'numpy')) # Inner non-linear operator, linear operator in middle, and norm on outside F = ops.FourierTransform(image_size, dtype=global_dtype, backend=global_backend) D_object = ops.Diagonalize((F * yp.vec(self.x)).reshape(image_size), label='D_{object}', dtype=global_dtype, backend=global_backend) R = ops.PhaseRamp(image_size, dtype=global_dtype, backend=global_backend) A_shift = F.H * D_object * R y = A_shift(shift_true) L2 = ops.L2Norm(image_size[0] * image_size[1], dtype=global_dtype, backend=global_backend) objective = L2 * (A_shift - self.y) # Check gradient objective.gradient_check()
def setup_method(self, test_method): # Load object and crop to size x_0 = yp.rand(image_size) # Convert object to desired backend self.x = yp.changeBackend(x_0, global_backend) # Generate convolution kernel h h_size = np.array([4, 4]) self.h = yp.zeros(image_size, global_dtype, global_backend) self.h[image_size[0] // 2 - h_size[0] // 2:image_size[0] // 2 + h_size[0] // 2, image_size[1] // 2 - h_size[1] // 2:image_size[1] // 2 + h_size[1] // 2] = yp.randn((h_size[0], h_size[1]), global_dtype, global_backend) # A = ops.Convolution(image_size, h, dtype=global_dtype, fft_backend='numpy', backend=global_backend) self.A = ops.FourierTransform(image_size, dtype=global_dtype, backend=global_backend, center=True) self.y = self.A(yp.vectorize(self.x))
def test_operator_exponential(self): L2 = ops.L2Norm(image_size) F = ops.FourierTransform(image_size) EXP = ops.Exponential(image_size) # Forward model assert yp.sumb(yp.abs(yp.changeBackend(EXP * self.x, 'numpy') - np.exp(yp.changeBackend(self.x, 'numpy')))) < eps # Check gradient EXP.gradient_check() # Generate composite operator D = ops.Diagonalize(self.h) L2 = ops.L2Norm(image_size) EXP_COMP = L2 * F * EXP EXP_COMP.gradient_check() EXP_COMP_2 = L2 * F * EXP * D EXP_COMP_2.gradient_check()
def test_operator_stacking_linear(self): # Create list of operators op_list_linear = [ ops.FourierTransform(image_size, dtype=global_dtype, backend=global_backend), ops.Identity(image_size, dtype=global_dtype, backend=global_backend), ops.Exponential(image_size, dtype=global_dtype, backend=global_backend) ] # Horizontally stacked operators H_l = ops.Hstack(op_list_linear) # Vertically stack x for forward operator x_np = yp.changeBackend(self.x, 'numpy') x3 = yp.changeBackend(np.vstack((x_np, x_np, x_np)), global_backend) # Check forward operation y2 = yp.zeros(op_list_linear[0].N, op_list_linear[0].dtype, op_list_linear[0].backend) for op in op_list_linear: y2 = y2 + op * self.x assert yp.sumb(yp.abs(yp.changeBackend(H_l(x3) - y2, 'numpy'))) < eps, "%.4e" % yp.sumb(yp.abs(H_l(x3) - y2)) # Check gradient H_l.gradient_check() # Create vertically stacked operator V_l = ops.Vstack(op_list_linear) # Check forward operator y3 = np.empty((0,image_size[1]), dtype=yp.getNativeDatatype(global_dtype, 'numpy')) for index, op in enumerate(op_list_linear): y3 = np.append(y3, (op * self.x), axis=0) y3 = yp.changeBackend(y3, global_backend) assert yp.sumb(yp.abs(V_l * self.x - y3)) < eps, "%.4e" % yp.sumb(yp.abs(V_l * vec(x) - y3)) # Check gradient V_l.gradient_check()
def test_operator_phase_ramp(self): eps_phase_ramp = 1e-4 shift = yp.changeBackend(np.asarray((-5,3)).astype(yp.getNativeDatatype(global_dtype, 'numpy')), global_backend) # Generate phase ramp R = ops.PhaseRamp(image_size) r = R * shift F = ops.FourierTransform(image_size, dtype=global_dtype, normalize=False, backend=global_backend) D_R = ops.Diagonalize(r, dtype=global_dtype) S_R = F.H * D_R * F # Pixel-wise shift operator S = ops.Shift(image_size, shift) # Check that phase ramp is shifting by correct amount assert yp.sumb(yp.abs(yp.changeBackend(S_R * self.x, 'numpy') - yp.changeBackend(S * self.x, 'numpy'))) < 1e-3 # Check gradient of phase ramp convolution S_R.gradient_check() # Check gradient of phase ramp R.gradient_check(eps=1e-1)