@staticmethod
def find_min_norm_element(vecs: list):
    '\n        Given a list of vectors (vecs), this method finds the minimum norm\n        element in the convex hull as min |u|_2 st. u = \\sum c_i vecs[i]\n        and \\sum c_i = 1. It is quite geometric, and the main idea is the\n        fact that if d_{ij} = min |u|_2 st u = c x_i + (1-c) x_j; the solution\n        lies in (0, d_{i,j})Hence, we find the best 2-task solution , and\n        then run the projected gradient descent until convergence\n        '
    dps = {}
    (init_sol, dps) = MGDASolver._min_norm_2d(vecs, dps)
    n = len(vecs)
    sol_vec = np.zeros(n)
    sol_vec[init_sol[0][0]] = init_sol[1]
    sol_vec[init_sol[0][1]] = (1 - init_sol[1])
    if (n < 3):
        return (sol_vec, init_sol[2])
    iter_count = 0
    grad_mat = np.zeros((n, n))
    for i in range(n):
        for j in range(n):
            grad_mat[(i, j)] = dps[(i, j)]
    while (iter_count < MGDASolver.MAX_ITER):
        grad_dir = ((- 1.0) * np.dot(grad_mat, sol_vec))
        new_point = MGDASolver._next_point(sol_vec, grad_dir, n)
        v1v1 = 0.0
        v1v2 = 0.0
        v2v2 = 0.0
        for i in range(n):
            for j in range(n):
                v1v1 += ((sol_vec[i] * sol_vec[j]) * dps[(i, j)])
                v1v2 += ((sol_vec[i] * new_point[j]) * dps[(i, j)])
                v2v2 += ((new_point[i] * new_point[j]) * dps[(i, j)])
        (nc, nd) = MGDASolver._min_norm_element_from2(v1v1.item(), v1v2.item(), v2v2.item())
        new_sol_vec = ((nc * sol_vec) + ((1 - nc) * new_point))
        change = (new_sol_vec - sol_vec)
        if (np.sum(np.abs(change)) < MGDASolver.STOP_CRIT):
            return (sol_vec, nd)
        sol_vec = new_sol_vec
Given a list of vectors (vecs), this method finds the minimum norm
element in the convex hull as min |u|_2 st. u = \sum c_i vecs[i]
and \sum c_i = 1. It is quite geometric, and the main idea is the
fact that if d_{ij} = min |u|_2 st u = c x_i + (1-c) x_j; the solution
lies in (0, d_{i,j})Hence, we find the best 2-task solution , and
then run the projected gradient descent until convergence
@staticmethod
def find_min_norm_element(vecs: list):
    '\n        Given a list of vectors (vecs), this method finds the minimum norm\n        element in the convex hull as min |u|_2 st. u = \\sum c_i vecs[i]\n        and \\sum c_i = 1. It is quite geometric, and the main idea is the\n        fact that if d_{ij} = min |u|_2 st u = c x_i + (1-c) x_j; the solution\n        lies in (0, d_{i,j})Hence, we find the best 2-task solution , and\n        then run the projected gradient descent until convergence\n        '
    dps = {}
    (init_sol, dps) = MGDASolver._min_norm_2d(vecs, dps)
    n = len(vecs)
    sol_vec = np.zeros(n)
    sol_vec[init_sol[0][0]] = init_sol[1]
    sol_vec[init_sol[0][1]] = (1 - init_sol[1])
    if (n < 3):
        return (sol_vec, init_sol[2])
    iter_count = 0
    grad_mat = np.zeros((n, n))
    for i in range(n):
        for j in range(n):
            grad_mat[(i, j)] = dps[(i, j)]
    while (iter_count < MGDASolver.MAX_ITER):
        grad_dir = ((- 1.0) * np.dot(grad_mat, sol_vec))
        new_point = MGDASolver._next_point(sol_vec, grad_dir, n)
        v1v1 = 0.0
        v1v2 = 0.0
        v2v2 = 0.0
        for i in range(n):
            for j in range(n):
                v1v1 += ((sol_vec[i] * sol_vec[j]) * dps[(i, j)])
                v1v2 += ((sol_vec[i] * new_point[j]) * dps[(i, j)])
                v2v2 += ((new_point[i] * new_point[j]) * dps[(i, j)])
        (nc, nd) = MGDASolver._min_norm_element_from2(v1v1.item(), v1v2.item(), v2v2.item())
        new_sol_vec = ((nc * sol_vec) + ((1 - nc) * new_point))
        change = (new_sol_vec - sol_vec)
        if (np.sum(np.abs(change)) < MGDASolver.STOP_CRIT):
            return (sol_vec, nd)
        sol_vec = new_sol_vec