Source code for desdeo_emo.utilities.symmetric_vectors

from desdeo_emo.utilities.plotlyanimate import animate_init_, animate_next_
import numpy as np
from itertools import product


[docs]def shear(vectors, degrees: float = 5): """ Shear a set of vectors lying on the plane z=0 towards the z-axis, such that the resulting vectors 'degrees' angle away from the z axis. z is the last element of the vector, and has to be equal to zero. Parameters ---------- vectors : numpy.ndarray The final element of each vector should be zero. degrees : float, optional The angle that the resultant vectors make with the z axis. Unit is radians. (the default is 5) """ angle = degrees * np.pi / 180 m = 1 / np.tan(angle) norm = np.linalg.norm(vectors, axis=1) vectors[:, -1] += norm * m return normalize(vectors)
[docs]def normalize(vectors): """ Normalize a set of vectors. The length of the returned vectors will be unity. Parameters ---------- vectors : np.ndarray Set of vectors of any length, except zero. """ if len(vectors.shape) == 1: return vectors / np.linalg.norm(vectors) norm = np.linalg.norm(vectors, axis=1) return vectors / norm[:, np.newaxis]
[docs]def rotate(initial_vector, rotated_vector, other_vectors): """Calculate the rotation matrix that rotates the initial_vector to the rotated_vector. Apply that rotation on other_vectors and return. Uses Householder reflections twice to achieve this.""" init_vec_norm = normalize(initial_vector) rot_vec_norm = normalize(rotated_vector) middle_vec_norm = normalize(init_vec_norm + rot_vec_norm) first_reflector = init_vec_norm - middle_vec_norm second_reflector = middle_vec_norm - rot_vec_norm Q1 = householder(first_reflector) Q2 = householder(second_reflector) reflection_matrix = np.matmul(Q2, Q1) rotated_vectors = np.matmul(other_vectors, np.transpose(reflection_matrix)) return rotated_vectors
[docs]def householder(vector): """Return reflection matrix via householder transformation.""" identity_mat = np.eye(len(vector)) v = vector[np.newaxis] denominator = np.matmul(v, v.T) numerator = np.matmul(v.T, v) rot_mat = identity_mat - (2 * numerator / denominator) return rot_mat
[docs]def rotate_toward(initial_vector, final_vector, other_vectors, degrees: float = 5): """ Rotate other_vectors (with the centre at initial_vector) towards final_vector by an angle degrees. Parameters ---------- initial_vector : np.ndarray Centre of the vectors to be rotated. final_vector : np.ndarray The final position of the center of other_vectors. other_vectors : np.ndarray The array of vectors to be rotated degrees : float, optional The amount of rotation (the default is 5) Returns ------- rotated_vectors : np.ndarray The rotated vectors reached: bool True if final_vector has been reached """ final_vector = normalize(final_vector) initial_vector = normalize(initial_vector) cos_phi = np.dot(initial_vector, final_vector) theta = degrees * np.pi / 180 cos_theta = np.cos(theta) phi = np.arccos(cos_phi) if phi < theta: return (rotate(initial_vector, final_vector, other_vectors), True) cos_phi_theta = np.cos(phi - theta) A = np.asarray([[cos_phi, 1], [1, cos_phi]]) B = np.asarray([cos_phi_theta, cos_theta]) x = np.linalg.solve(A, B) rotated_vector = x[0] * initial_vector + x[1] * final_vector return (rotate(initial_vector, rotated_vector, other_vectors), False)
[docs]def main(): initial = np.array(list(product([0, 1, -1], [0, 1, -1])))[1:] initial = normalize(initial) initial = np.hstack((initial, np.zeros((initial.shape[0], 1)))) filename = "smooth_transition.html" figure = animate_init_(initial, filename) final = shear(initial) generation = 1 figure = animate_next_(final, figure, filename, generation) complete = False while not complete: center = np.average(final, axis=0) final, complete = rotate_toward(center, np.array([1, 1, 1]), final) figure = animate_next_(final, figure, filename, generation) generation = generation + 1 complete = False while not complete: center = np.average(final, axis=0) final, complete = rotate_toward(center, np.array([1, 0, 1]), final) figure = animate_next_(final, figure, filename, generation) generation = generation + 1 complete = False while not complete: center = np.average(final, axis=0) final, complete = rotate_toward(center, np.array([0, 0, 1]), final) figure = animate_next_(final, figure, filename, generation) generation = generation + 1 complete = False while not complete: center = np.average(final, axis=0) final, complete = rotate_toward(center, np.array([1, 0, 0]), final) figure = animate_next_(final, figure, filename, generation) generation = generation + 1
if __name__ == "__main__": main()