Пакет aloh
позволяет выбрать наиболее выгодные заказы и оптимизировать дискретное производство по дням.
Мы решаем задачу смешанного линейного целочисленного программирования (MILP).
- Полученным заказам присваивается бинарная переменная (взяли-не взяли), объемы производства - это непрерывные переменные от нуля до максимального выпуска.
- Оптимизация ведется с целью максимизации прибыли и уменьшения запасов продукции на складе.
- Мы также учитываем ограничения на срок хранения продукта на складе.
Для формулировки задачи линейного программирования используется пакет PuLP, решение ищется с помощью открытых солверов, таких как CBC.
Возможные альтернативы PuLP:
Название пакета aloh
- сокращение от химической формулы гидроксида алюминия, демонстрационного примера оптимизации производства.
Один продукт ("A"), планирование на три дня, пять заказов. Необходимо выбрать выгодные заказы и объем производства по дням.
В портфеле есть заказы, которые невозможно выполнить (недостаточно производственных мощностей), невыгодные заказы (цена ниже себестоимости) и заказы с разной маржинальностью. С помощью алгоритма мы выбираем те заказы, которые, во-первых, возможно и, во-вторых, выгодно выполнить.
from aloh import OptModel, Product
pa = Product(name="A", capacity=10, unit_cost=0.2, storage_days=1)
# Ожидаемые результаты:
pa.add_order(day=0, volume=7, price=0.3) # - менее выгодный заказ
pa.add_order(day=0, volume=7, price=0.5) # - более выгодный заказ, берем
pa.add_order(day=1, volume=9, price=0.1) # - убыточный заказ, не берем
pa.add_order(day=2, volume=6, price=0.3) # } - если есть возможность хранения,
pa.add_order(day=2, volume=6, price=0.3) # } - берем оба заказа
m = OptModel(products=[pa], model_name="model_0", inventory_weight=0)
ac, xs = m.evaluate()
Solved in 0.099 sec
В результате отбора мы получаем значения бинарных переменных, соответствующих рекомендации по заказу (принять/не принять).
>>> ac
{'A': [0, 1, 0, 1, 1]}
В расширенном представлении, мы видим исходные данные заказов.
accept=1 показывает принятый заказ, 0
- отклоненный.
>>> m.accepted_orders_full()
{'A': [{'order': {'day': 0, 'volume': 7, 'price': 0.3}, 'accepted': 0},
{'order': {'day': 0, 'volume': 7, 'price': 0.5}, 'accepted': 1},
{'order': {'day': 1, 'volume': 9, 'price': 0.1}, 'accepted': 0},
{'order': {'day': 2, 'volume': 6, 'price': 0.3}, 'accepted': 1},
{'order': {'day': 2, 'volume': 6, 'price': 0.3}, 'accepted': 1}]}
Та же информация в виде датафрейма:
>>> v = DataframeViewer(m)
>>> v.orders_dataframe("A")
day volume price accept
n
0 0 7 0.3 0
1 0 7 0.5 1
2 1 9 0.1 0
3 2 6 0.3 1
4 2 6 0.3 1
Производство (x), поставки (ship), суммарная потребность c с учетом внутреннего потребления (req), запасы (inv) - в натуральном выражении, продажи (sales) and затраты (costs) - в денежном выражении.
Краткий результат:
>>> xs
{'A': [7.0, 2.0, 10.0]}
Расширенное представление результата:
>>> v.product_dataframe("A")
x ship req inv sales costs
day
0 7.0 7.0 7.0 0.0 3.5 1.4
1 2.0 0.0 0.0 2.0 0.0 0.4
2 10.0 12.0 12.0 0.0 3.6 2.0
https://epogrebnyak.github.io/aloh/
Напрямую из репо:
pip install --upgrade git+https://github.com/epogrebnyak/aloh.git
Для разработки:
git clone https://github.com/epogrebnyak/aloh.git
cd aloh
pip install -e .
Для Windows может понадобиться команда:
set PYTHONIOENCODING=utf8