Ejemplo n.º 1
0
def simplex_step(cf: CanonicalForm,
                 xkN: np.array) -> Tuple[np.array, np.array, bool]:
    """
    Performs one step of simplex algorithm.
    Parameters names corresponds to those in a book by Petuhov (Петухов) et al,
    p. 88. Algorithm is based on the same source.
    :param cf: parameters of problem in canonical form
    :param xkN: a starting vector: any support vector for a set defined by `cf`
    :return:
        1. The next approximation - also a support vector (the value of target
        function is not increased)
        2. Nk - a list of basis vectors of this support vector in A[M,N]
        3. A boolean equal to True if iterations are to be stopped,
         otherwise False
    """
    AMN, cN = cf.A, cf.c
    _, _, Nk0, Nk_plus = split_xkN(
        xkN)  # Nk0 - indices of zero components of xk, Nk+ - positive
    binomGrid = binomial_grid(
        len(Nk0), cf.m -
        len(Nk_plus))  # auxiliary structure for building combinations that are
    # added to A[M,Nk+]
    for binom_idx in range(binomGrid[-1, -1]):
        # augment Nk+ to Nk so that A[M, Nk] is square
        AMNk, Nk, Lk = new_AMNk(AMN, xkN, binomGrid, binom_idx)
        # if determinant of the square matrix is 0, continue
        if np.linalg.det(AMNk) == 0:
            continue
        BNkM = calc_BNkM(AMNk)  # calculating inverse matrix for A[M, Nk]
        cNk = np.array([cN[i] for i in Nk])
        ykM = np.matmul(BNkM.T, cNk)
        dkN = cN - np.matmul(AMN.T, ykM)
        dkLk = np.array([dkN[int(i)] for i in Lk])
        if np.min(dkLk) >= -1e-4:  # xkN is already the optimal vector
            logging.info("solution found at iteration " + str(binom_idx + 1))
            return xkN, Nk, True
        # index of first negative components in dkLk
        jk = Lk[list(filter(lambda j: dkLk[j] < -1e-4, range(len(Lk))))[0]]
        xkNk0, xkNk_plus, Nk0, Nk_plus = split_xkN(xkN)
        ukNk = np.matmul(BNkM, AMN[:, jk])
        if np.max(ukNk) <= -1e-4:  # target function is not lower bounded
            logging.info("solution does not exist")
            return np.array([np.inf for _ in range(cf.n)]), Nk, True
        if len(Nk_plus) == len(Nk) or max([
                ukNk[i]
                for i in filter(lambda j: Nk[j] not in Nk_plus, range(len(Nk)))
        ]) < 0:
            ukN = [
                ukNk[list(Nk).index(i)] if i in Nk else 0 for i in range(cf.n)
            ]
            ukN[jk] = -1
            theta_k = min(
                [xkN[i] / ukN[i] for i in filter(lambda j: ukN[j] > 0, Nk)])
            return xkN - np.multiply(theta_k, ukN), Nk, False
    logging.info("iterations ended with no result, something went wrong.")
    return xkN, np.array([]), True  # something went wrong
Ejemplo n.º 2
0
def find_all_svs(cf: NpCanonicalForm):
    binom_table = binomial_grid(cf.n, cf.m)
    N = np.array(list(range(cf.n)))
    for idx in range(binom_table[-1, -1]):
        Nk = subset_by_index(N, binom_table, idx)
        AMNk = np.array([cf.A[:, i] for i in Nk]).T
        if np.abs(np.linalg.det(AMNk)) > 1e-3:
            xNk = np.matmul(np.linalg.inv(AMNk), cf.b)
            if np.min(xNk) < 0:
                continue
            xN = np.zeros(cf.n)
            for i in range(len(Nk)):
                xN[Nk[i]] = xNk[i]
            yield xN
Ejemplo n.º 3
0
def starting_vector(cf: CanonicalForm) -> np.array:
    binom_table = binomial_grid(cf.n, cf.m)
    N = np.array(list(range(cf.n)))
    for idx in range(binom_table[-1, -1]):
        Nk = subset_by_index(N, binom_table, idx)
        AMNk = np.array([cf.A[:, i] for i in Nk]).T
        if np.linalg.det(AMNk) != 0:
            xNk = np.matmul(np.linalg.inv(AMNk), cf.b)
            if np.min(xNk) < 0:
                continue  # we need only vectors with all positive components
            xN = np.zeros(cf.n)
            for i in range(len(Nk)):
                xN[Nk[i]] = xNk[i]
            return xN
    logging.info("error, initial vector not found!")
    return np.zeros(cf.n)
Ejemplo n.º 4
0
def find_min_sv(cf: CanonicalForm,
                target_function: Callable[[np.array], float],
                abs_tolerance: float = 1e-3) -> BruteForceResult:
    binom_table = binomial_grid(cf.n, cf.m)
    N = np.array(list(range(cf.n)))
    min_sv, inv_AMNk_min, Nk_min = None, None, None
    for idx in range(binom_table[-1, -1]):
        Nk = subset_by_index(N, binom_table, idx)
        AMNk = np.array([cf.A[:, i] for i in Nk]).T
        if np.abs(np.linalg.det(AMNk)) > abs_tolerance:
            inv_AMNk = np.linalg.inv(AMNk)
            xNk = np.matmul(inv_AMNk, cf.b)
            if np.min(xNk) < 0:
                continue
            xN = np.zeros(cf.n)
            for i in range(len(Nk)):
                xN[Nk[i]] = xNk[i]
            if min_sv is None or target_function(xN) < target_function(min_sv):
                min_sv, inv_AMNk_min, Nk_min = xN, inv_AMNk, Nk
    return BruteForceResult(min_sv, inv_AMNk_min, Nk_min)
Ejemplo n.º 5
0
def simplex_step(cf: NpCanonicalForm, xkN: np.array) -> (np.array, np.array, bool):
    """
    Совершает один шаг алгоритма симплекс-метода. Обозначения и процедура взяты из пособия
    Петухов и др., стр. 88
    :param cf: параметры задачи, поставленной в канонической форме
    :param xkN: начальное приближение - некий опорный вектор к множеству, заданному `cf`
    :return:
        1. следующее приближение - тоже опорный вектор, причём с ним значение целевой функции не возрастает
        2. Nk - список индексов базисных векторов опорного вектора в матрице A[M,N]
        3. булеву переменную, равную `true`, если итерирование нужно прекратить и `false` иначе
    """
    AMN, cN = cf.A, cf.c
    _, _, Nk0, Nk_plus = split_xkN(xkN) # Nk0 - индексы нулевых компонент xk, Nk+ - положительных
    binomGrid = binomial_grid(len(Nk0), cf.m - len(Nk_plus))  # вспомог. структура для построения комбинаций столбцов, присоединяемых к A[M,Nk+]
    #for binom_idx in range(binomGrid[-1, -1] - 1, -1, -1):  # итерируемся по комбинациям векторов, присоединяемых к A[M,Nk+]
    for binom_idx in range(binomGrid[-1, -1]):
    # В первом порядке алг-м слишком быстро находит решение нашей задачи
        AMNk, Nk, Lk = new_AMNk(AMN, xkN, binomGrid, binom_idx)  # дополняем Nk+ до Nk так, что A[M, Nk] квадратная
        if np.linalg.det(AMNk) == 0:  # если определитель построенной квадратной матрицы 0, пропускаем комбинацию
            continue
        BNkM = calc_BNkM(AMNk)  # вычисление матрицы, обратной к A[M, Nk]
        cNk = np.array([cN[i] for i in Nk])
        ykM = np.matmul(BNkM.T, cNk)
        dkN = cN - np.matmul(AMN.T, ykM)
        dkLk = np.array([dkN[int(i)] for i in Lk])
        if np.min(dkLk) >= -1e-4:  # xkN уже является оптимальным вектором
            print("solution found at iteration " + str(binom_idx + 1))
            return xkN, Nk, True
        jk = Lk[list(filter(lambda j: dkLk[j] < -1e-4, range(len(Lk))))[0]]  # индекс первой негативной компоненты в dkLk
        xkNk0, xkNk_plus, Nk0, Nk_plus = split_xkN(xkN)
        ukNk = np.matmul(BNkM, AMN[:, jk])
        if np.max(ukNk) <= -1e-4:  # целевая функция не ограничена снизу
            print("solution does not exist")
            return np.array([np.inf for _ in range(cf.n)]), Nk, True
        if len(Nk_plus) == len(Nk) or max([ukNk[i] for i in filter(lambda j: Nk[j] not in Nk_plus, range(len(Nk)))]) < 0:
            ukN = [ukNk[list(Nk).index(i)] if i in Nk else 0 for i in range(cf.n)]
            ukN[jk] = -1
            theta_k = min([xkN[i]/ukN[i] for i in filter(lambda j: ukN[j] > 0, Nk)])
            return xkN - np.multiply(theta_k, ukN), Nk, False
    print("iterations ended with no result, something went wrong.")
    return xkN, np.array([]), True  # что-то пошло не так