Минимизация нормы L1 Ax-b с помощью scipy.optimize.linprog

Известно, что задачу минимизации 1-нормы Ax - b над x можно записать как задачу линейного программирования. Я хотел бы использовать scipy.optimize.linprog для решения этой проблемы минимизации, но у меня возникли проблемы с интерпретацией выходного массива linprog. Ниже приведена реализация моего кода метода решения Ax - b с помощью линейного программирования:

import numpy as np
import scipy.optimize as op
from scipy.linalg import lstsq

def build_A_ub(A):
    Nrow, Ncol = A.shape
    I          = np.identity(Nrow)

    return np.block([
        [A , -I],
        [-A, -I]
    ])

def build_b_ub(b):
    return np.concatenate([b, -b])

def build_c(A):
    Nrow, Ncol = A.shape
    return np.concatenate([np.zeros(Ncol), np.ones(Nrow)])

def obtain_l1norm_minimizer(A, b):
    """
    return op_result which contains parameters x which minimizes |Ax - b|_1
    """
    A_ub, b_ub, c = build_A_ub(A), build_b_ub(b), build_c(A)
    
    return op.linprog(c, A_ub=A_ub, b_ub=b_ub)

Иногда это работает. Например, в следующем случае, когда минимальная 1-норма равна 0, мы имеем:

#
#    example implementation of L1 norm minimization of Ax - b for n x m matrix 
#    with n < m and rank(n) (i.e: exact solution to Ax - b)
#

from numpy.random import uniform

np.random.seed(2)
Nrow = 3
Ncol = 4
A    = uniform(-1, 1, [Nrow,Ncol])
b    = uniform(-1, 1, [Nrow])

assert np.linalg.matrix_rank(A) == min([Nrow, Ncol])

op_result  = obtain_l1norm_minimizer(A, b)
difference = A @ op_result.x[:Ncol] - b

print("Solution vector (x,y):")
print(np.round(op_result.x, 6))
print("Ax - b:")
print(np.round(difference, 6))

производит продукцию

Solution vector (x,y):
[1.366741 0.373728 0.       1.557993 0.       0.       0.      ]
Ax - b:
[ 0. -0.  0.]

Однако в следующей задаче, где нет точного решения, не создается вектор с минимальной 1-нормой:

np.random.seed(2)
Nrow = 6
Ncol = 3
A    = uniform(-1, 1, [Nrow,Ncol])
b    = uniform(-1, 1, [Nrow])

assert np.linalg.matrix_rank(A) == min([Nrow, Ncol])

op_result        = obtain_l1norm_minimizer(A, b)
x_lstsq, _, _, _ = lstsq(A, b)

# compare 1-norm of original, solution obtained from 1-norm minimization, and least squares solution

print(f'original 1-norm : {np.linalg.norm(b, 1)}')
print(f'1-norm from 1-norm minimization: {np.linalg.norm(A @ op_result.x[:Ncol] - b, 1)}')
print(f'1-norm from least-squares minimization : {np.linalg.norm(A @ x_lstsq - b, 1)}')

с выходом

original 1-norm : 3.364444701650462
1-norm from 1-norm minimization: 3.2226922553095174
1-norm from least-squares minimization : 2.374926794178961

Поэтому моя реализация лишь иногда дает правильный ответ. Эмпирически я обнаружил, что для систем, в которых существует точное решение, ответ правильный тогда и только тогда, когда все ненулевые записи выходных данных op_result.x находятся в первых Ncol записях. Когда это не так, кажется, что нет никакого способа получить решение от op_result.x; Я попробовал, например, переставить все записи и посмотреть, дадут ли переставленные параметры решение. Мой вопрос: почему моя реализация работает только иногда и что я могу изменить, чтобы она работала всегда?

«пример реализации минимизации нормы L1 Ax - b для матрицы n x m с n > m и рангом (m) (т. е.: точное решение Ax - b)» - в вашем примере n (количество строк) < m ( количество столбцов) и имеет ранг m. Вы имели в виду матрицу m x n?

Matt Haberland 15.05.2024 06:02

Спасибо, что уловили это. Я отредактировал это.

Solarflare0 15.05.2024 06:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
84
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Поскольку у вас меньше уравнений, чем неизвестных, вы можете изменить его, чтобы он всегда работал, используя вместо него scipy.linalg.lstsq.

import numpy as np
from numpy.random import uniform
from scipy.linalg import lstsq
np.random.seed(0)
Nrow = 3
Ncol = 4
A = uniform(-1, 1, [Nrow,Ncol])
b = uniform(-1, 1, [Nrow])
x, _, _, _ = lstsq(A, b)
print(A@x - b)
# [-2.77555756e-17  3.33066907e-16 -2.22044605e-16]

Если вас интересуют задачи, не имеющие точного решения, приведите пример с такой функцией.

Спасибо за ответ! Я включил пример, в котором мой код дает сбой из-за проблемы, не имеющей точного решения, и это тот случай, который меня интересует.

Solarflare0 15.05.2024 06:30

Вы пробовали мой второй ответ, устанавливая границы (None, None)? Кажется, это тоже решает эту проблему.

Matt Haberland 15.05.2024 13:07
Ответ принят как подходящий

Альтернативно, вы можете удалить границы из ваших переменных решения. По умолчанию они должны быть неотрицательными. Вероятно, вы хотели, чтобы они были неограниченными, поэтому:

op.linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=(None, None))

Я не проверял внимательно ваш код, так как есть лучший способ решить такую ​​проблему, но, похоже, он работает.

Кажется, это работает в том смысле, что ответ, который я получаю, всегда имеет более низкую 1-норму по сравнению с решением методом наименьших квадратов.

Solarflare0 15.05.2024 19:05

Другие вопросы по теме