# Beta min value if latent_dim == 2: beta_min = 0.6 elif latent_dim == 3: beta_min = 0.2 elif latent_dim == 5: beta_min = 0.25 elif latent_dim == 7: beta_min = 0.22 elif latent_dim == 10: beta_min = 0.2 elif latent_dim == 12: beta_min = 0.16 # Instantiate the manifolds spd_manifold = pyman_man.PositiveDefinite(dim) latent_spd_manifold = pyman_man.PositiveDefinite(latent_dim) # Update the random function of the manifold (the original one samples only eigenvalues between 1 and 2). # We need to specify the minimum and maximum eigenvalues of the random matrices. This is done when defining bounds. spd_manifold.rand = types.MethodType(spd_sample, spd_manifold) # Define the test function # Parameters for the nested test function grassmann_manifold = pyman_man.Grassmann(dim, latent_dim) projection_matrix_test = torch.from_numpy( grassmann_manifold.rand()).double() # Define the nested test function test_function = functools.partial( projected_function_spd,
# Define the dimension # If the dimension is changed: # - the optimization domain must be updated as one domain per dimension is required by gpflowopt dim = 2 # Define the dimension of the Mandel vector notation dim_vec = int(dim + dim * (dim - 1) / 2) # True to display sphere figures (possible only if the dimension is 3 (3D graphs)) display_figures = True # Number of BO iterations nb_iter_bo = 25 # Instantiate the manifold (for the objective function and for initial observations, to have the same as GaBO) spd_manifold = pyman_man.PositiveDefinite(dim) # Bounding eigenvalues min_eigenvalue = 0.001 max_eigenvalue = 5. # Define minimum and maximum eigenvalue constraints def minimum_eigenvalue_constraint(x_chol): indices = np.tril_indices(dim) xL = np.zeros((dim, dim)) xL[indices] = x_chol x = np.dot(xL, xL.T) eig, _ = np.linalg.eig(x) return np.min(eig) - min_eigenvalue
def optimize_reconstruction_parameters_nested_spd( x_data, x_data_projected, projection_matrix, inner_solver, cost_function=min_affine_invariant_distance_reconstruction_cost, nb_init_candidates=100, maxiter=50): """ This function computes the parameters of the mapping "projection_from_nested_spd_to_spd" from nested SPD matrices Y = W'XW to SPD matrices Xrec, so that the distance between the original data X and the reconstructed data Xrec is minimized. To do so, we consider that the nested SPD matrix Y = W'XW is the d x d upper-left part of the rotated matrix Xr = R'XR, where R = [W, V] and Xr = [Y B; B' C]. In order to recover X, we assume a constant SPD matrix C, and B = Y^0.5*K*C^0.5 to ensure the PDness of Xr, with K a contraction matrix (norm(K) <=1). We first reconstruct Xr, and then Xrec as X = RXrR'. We are minimizing the squared distance between X and Xrec, by optimizing the complement to the projection matrix V, the bottom SPD matrix C, and the contraction matrix K. The contraction matrix K is described here as a norm-1 matrix multiplied by a factor in [0,1] (unconstraintly optimized by transform it with a sigmoid function). The augmented Lagrange optimization method on Riemannian manifold is used to optimize the parameters on the product of manifolds G(D,D-d), SPD(D-d), S(d*(D-d)) and Eucl(1), while respecting the constraint W'V = 0. Parameters ---------- :param x_data: set of high-dimensional SPD matrices (N x D x D) :param x_data_projected: set of low-dimensional SPD matrices (projected from x_data) (N x d x d) :param projection_matrix: element of the Grassmann manifold (D x d) :param inner_solver: inner solver for the ALM on Riemnannian manifolds Optional parameters ------------------- :param nb_init_candidates: number of initial candidates for the optimization :param maxiter: maximum iteration of ALM solver Returns ------- :return: projection_complement_matrix: element of the Grassmann manifold (D x D-d) so that torch.mm(projection_complement_matrix.T, projection_matrix) = 0. :return: bottom_spd_matrix: bottom-right part of the rotated SPD matrix (D-d, D-d) :return: contraction_matrix: matrix whose norm is <=1 (d x D-d) """ # Dimensions dim = x_data.shape[1] latent_dim = projection_matrix.shape[1] # Product of manifolds for the optimization manifolds_list = [ pyman_man.Grassmann(dim, dim - latent_dim), pyman_man.PositiveDefinite(dim - latent_dim), pyman_man.Sphere(latent_dim * (dim - latent_dim)), pyman_man.Euclidean(1) ] product_manifold = pyman_man.Product(manifolds_list) # Constraint on the norm of the contraction matrix contraction_norm_constraint = gpytorch.constraints.Interval(0., 1.) # Constraint W'V = 0 def constraint_fct(parameters): cost = torch.norm(torch.mm(parameters[0].T, projection_matrix)) zero_element_needed_for_correct_grad = 0. * torch.norm(parameters[1]) + 0. * torch.norm(parameters[2]) + \ 0. * torch.norm(parameters[3]) return cost + zero_element_needed_for_correct_grad # Reconstruction cost def reconstruction_cost(parameters): projection_complement_matrix = parameters[0] bottom_spd_matrix = parameters[1] contraction_norm = contraction_norm_constraint.transform(parameters[3]) contraction_matrix = contraction_norm * parameters[2].view( latent_dim, dim - latent_dim) return cost_function(x_data, x_data_projected, projection_matrix, projection_complement_matrix, bottom_spd_matrix, contraction_matrix) # Generate candidate for initial data x0_candidates = [ product_manifold.rand() for i in range(nb_init_candidates) ] x0_candidates_torch = [] for x0 in x0_candidates: x0_candidates_torch.append([torch.from_numpy(x) for x in x0]) y0_candidates = [ reconstruction_cost(x0_candidates_torch[i]) for i in range(nb_init_candidates) ] # Initialize with the best of the candidates y0, x_init_idx = torch.Tensor(y0_candidates).min(0) x0 = x0_candidates[x_init_idx] # Define the optimization problem reconstruction_problem = Problem(manifold=product_manifold, cost=reconstruction_cost, arg=torch.Tensor(), verbosity=0) # Define ALM solver solver = AugmentedLagrangeMethod(maxiter=maxiter, inner_solver=inner_solver, lambdas_fact=0.05) # Solve spd_parameters_np = solver.solve(reconstruction_problem, x=x0, eq_constraints=constraint_fct) # Parameters to torch data projection_complement_matrix = torch.from_numpy(spd_parameters_np[0]) bottom_spd_matrix = torch.from_numpy(spd_parameters_np[1]) contraction_norm = contraction_norm_constraint.transform( torch.from_numpy(spd_parameters_np[3])) contraction_matrix = contraction_norm * torch.from_numpy( spd_parameters_np[2]).view(latent_dim, dim - latent_dim) return projection_complement_matrix, bottom_spd_matrix, contraction_matrix