分段模型线性化(PWL)【Python|Gurobi实现】

简介: 分段模型线性化(PWL)【Python|Gurobi实现】

1 概述

在许多问题中,可能既包含着纯线性函数,也包含着分段线性函数。在这一节,我们使用运输问题解释处理分段线性模型的各种方法。


从几何的观点,下图为一个经典的分段线性函数。这个函数由四条线段构成,分段点为4、5 和 7。这三个分段点,将整个函数分为(-∞, 4) 、 [4, 5) 、 [5, 7) 和 [7, ∞) 四个区间。


每个区间内线段的斜率是一个常数,但不同区间之间的线段斜率是不同的。斜率的变化的点就是分段点。


分段线性函数常用于表示或逼近非线性一元函数。

2 算例及Python|Gurobi实现

2.1 算例

考虑经典的运输问题,即:有一些供应商(节点i)和一些需求(节点j),确定每个供应商的产量()  、用户的需求量()  ,以及在给定每条路径运输成本的情况下,最小化运输成本。


2.2  Python|Gurobi实现

# -*- coding: utf-8 -*-
from gurobipy import *
import numpy as np
N_i=3
N_j=4
k=1.8
C=np.array([
    [0.0755,0.0655,0.0498,0.0585],
    [0.0276,0.0163,0.0960,0.0224],
    [0.0680,0.0119,0.0340,0.0751]
    ])
Pmin=np.array([100,50,30])
Pmax=np.array([450,350,500])
demand=np.array([217,150,145,244])
# 创建模型
M_PWL=Model("PWL-1")
# 变量声明
x  =M_PWL.addVars(N_i,N_j,lb=0,ub=100, name="x")
P  =M_PWL.addVars(N_i,lb=0,ub=Pmax, name="P")
U  =M_PWL.addVars(N_i,vtype=GRB.BINARY,name="U")  #采用0-1变量对分段约束进行线性化处理。即:通过定义0-1变量“U”,
# 设置目标函数
M_PWL.setObjective(quicksum(C[i,j]*x[i,j]*x[i,j] for i in range(N_i) for j in range(N_j)),GRB.MINIMIZE)
# 添加约束
M_PWL.addConstrs((P[i]<=Pmax[i]*U[i] for i in range(N_i)),"Con_P1")
M_PWL.addConstrs((P[i]>=Pmin[i]*U[i] for i in range(N_i)),"Con_P2")
M_PWL.addConstrs((sum(x[i,j] for i in range(N_i))>=demand[j] for j in range(N_j)),"Con_x1")
M_PWL.addConstrs((sum(x[i,j] for j in range(N_j))==P[i] for i in range(N_i)),"Con_x2")
# Optimize model
M_PWL.optimize()
M_PWL.write("PWL1.lp")
P_c=np.zeros(N_i)
x_c=np.zeros((N_i,N_j))
for i in range(N_i):
    P_c[i]=P[i].x
    for j in range(N_j):
        x_c[i,j]=x[i,j].x
print('P_c is',P_c)
print('x_c is',x_c)

2.3 运行结果

Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)
Thread count: 8 physical cores, 16 logical processors, using up to 16 threads
Optimize a model with 13 rows, 18 columns and 39 nonzeros
Model fingerprint: 0xc29ac6cf
Model has 12 quadratic objective terms
Variable types: 15 continuous, 3 integer (3 binary)
Coefficient statistics:
  Matrix range     [1e+00, 5e+02]
  Objective range  [0e+00, 0e+00]
  QObjective range [2e-02, 2e-01]
  Bounds range     [1e+00, 5e+02]
  RHS range        [1e+02, 2e+02]
Presolve removed 7 rows and 6 columns
Presolve time: 0.00s
Presolved: 6 rows, 12 columns, 20 nonzeros
Presolved model has 12 quadratic objective terms
Variable types: 12 continuous, 0 integer (0 binary)
Root relaxation presolve time: 0.00s
Root relaxation presolved: 6 rows, 12 columns, 20 nonzeros
Root relaxation presolved model has 12 quadratic objective terms
Root barrier log...
Ordering time: 0.00s
Barrier statistics:
 AA' NZ     : 8.000e+00
 Factor NZ  : 2.100e+01
 Factor Ops : 9.100e+01 (less than 1 second per iteration)
 Threads    : 1
                  Objective                Residual
Iter       Primal          Dual         Primal    Dual     Compl     Time
   0   3.49914954e+06 -3.87161263e+06  3.49e+03 8.83e+02  1.01e+06     0s
   1   1.27698128e+05 -5.62657044e+05  3.70e+02 1.04e+02  1.31e+05     0s
   2   4.07264925e+03 -3.53732018e+05  0.00e+00 1.04e-04  1.19e+04     0s
   3   4.02551091e+03 -4.00359989e+03  0.00e+00 2.04e-06  2.68e+02     0s
   4   2.47855475e+03 -8.87063350e+02  0.00e+00 2.03e-12  1.12e+02     0s
   5   2.20928849e+03  1.86235277e+03  0.00e+00 1.07e-14  1.16e+01     0s
   6   2.16290222e+03  2.16128475e+03  0.00e+00 3.55e-15  5.39e-02     0s
   7   2.16264766e+03  2.16264604e+03  0.00e+00 1.78e-15  5.40e-05     0s
   8   2.16264740e+03  2.16264740e+03  0.00e+00 1.78e-15  5.40e-08     0s
   9   2.16264740e+03  2.16264740e+03  0.00e+00 7.11e-15  5.40e-11     0s
Barrier solved model in 9 iterations and 0.00 seconds (0.00 work units)
Optimal objective 2.16264740e+03
Root relaxation: objective 2.162647e+03, 0 iterations, 0.00 seconds (0.00 work units)
    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
*    0     0               0    2162.6474028 2162.64740  0.00%     -    0s
Explored 1 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)
Thread count was 16 (of 16 available processors)
Solution count 1: 2162.65 
Optimal solution found (tolerance 1.00e-04)
Best objective 2.162647402807e+03, best bound 2.162647402807e+03, gap 0.0000%
P_c is [199.24499511 282.49440796 274.26059693]
x_c is [[ 55.44250871  14.2550231   48.60135551  80.94610778]
 [100.          57.28245479  25.21195317 100.        ]
 [ 61.55749129  78.46252211  71.18669131  63.05389222]]
Process finished with exit code 
Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (win64)
Thread count: 8 physical cores, 16 logical processors, using up to 16 threads
Optimize a model with 13 rows, 18 columns and 39 nonzeros
Model fingerprint: 0xc29ac6cf
Model has 12 quadratic objective terms
Variable types: 15 continuous, 3 integer (3 binary)
Coefficient statistics:
  Matrix range     [1e+00, 5e+02]
  Objective range  [0e+00, 0e+00]
  QObjective range [2e-02, 2e-01]
  Bounds range     [1e+00, 5e+02]
  RHS range        [1e+02, 2e+02]
Presolve removed 7 rows and 6 columns
Presolve time: 0.00s
Presolved: 6 rows, 12 columns, 20 nonzeros
Presolved model has 12 quadratic objective terms
Variable types: 12 continuous, 0 integer (0 binary)
Root relaxation presolve time: 0.00s
Root relaxation presolved: 6 rows, 12 columns, 20 nonzeros
Root relaxation presolved model has 12 quadratic objective terms
Root barrier log...
Ordering time: 0.00s
Barrier statistics:
 AA' NZ     : 8.000e+00
 Factor NZ  : 2.100e+01
 Factor Ops : 9.100e+01 (less than 1 second per iteration)
 Threads    : 1
                  Objective                Residual
Iter       Primal          Dual         Primal    Dual     Compl     Time
   0   3.49914954e+06 -3.87161263e+06  3.49e+03 8.83e+02  1.01e+06     0s
   1   1.27698128e+05 -5.62657044e+05  3.70e+02 1.04e+02  1.31e+05     0s
   2   4.07264925e+03 -3.53732018e+05  0.00e+00 1.04e-04  1.19e+04     0s
   3   4.02551091e+03 -4.00359989e+03  0.00e+00 2.04e-06  2.68e+02     0s
   4   2.47855475e+03 -8.87063350e+02  0.00e+00 2.03e-12  1.12e+02     0s
   5   2.20928849e+03  1.86235277e+03  0.00e+00 1.07e-14  1.16e+01     0s
   6   2.16290222e+03  2.16128475e+03  0.00e+00 3.55e-15  5.39e-02     0s
   7   2.16264766e+03  2.16264604e+03  0.00e+00 1.78e-15  5.40e-05     0s
   8   2.16264740e+03  2.16264740e+03  0.00e+00 1.78e-15  5.40e-08     0s
   9   2.16264740e+03  2.16264740e+03  0.00e+00 7.11e-15  5.40e-11     0s
Barrier solved model in 9 iterations and 0.00 seconds (0.00 work units)
Optimal objective 2.16264740e+03
Root relaxation: objective 2.162647e+03, 0 iterations, 0.00 seconds (0.00 work units)
    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
*    0     0               0    2162.6474028 2162.64740  0.00%     -    0s
Explored 1 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)
Thread count was 16 (of 16 available processors)
Solution count 1: 2162.65 
Optimal solution found (tolerance 1.00e-04)
Best objective 2.162647402807e+03, best bound 2.162647402807e+03, gap 0.0000%
P_c is [199.24499511 282.49440796 274.26059693]
x_c is [[ 55.44250871  14.2550231   48.60135551  80.94610778]
 [100.          57.28245479  25.21195317 100.        ]
 [ 61.55749129  78.46252211  71.18669131  63.05389222]]
Process finished with exit code 0


3 运输问题——包含分段线性约束的优化问题(Python+Cplex实现)

这个博主写得很好:

包含分段线性约束的优化问题(Python+Cplex实现)

import sys
import cplex
from cplex.exceptions import CplexError
def transport(convex):
    # 定义工厂供给量
    supply = [1000.0, 850.0, 1250.0]
    nbSupply = len(supply)
    # 定义展厅的需求量向量
    demand = [900.0, 1200.0, 600.0, 400.0]
    nbDemand = len(demand)
    # 
    n = nbSupply * nbDemand
    # 判断成本斜率的凹凸性
    if convex:
        pwl_slope = [120.0, 80.0, 50.0]
    else:
        pwl_slope = [30.0, 80.0, 130.0]
    def varindex(m, n):
        return m * nbDemand + n
    # 设置分段线性函数的断点 x 坐标
    k = 0
    pwl_x = [[0.0] * 4] * n
    pwl_y = [[0.0] * 4] * n
    for i in range(nbSupply):
        for j in range(nbDemand):
            if supply[i] < demand[j]:
                midval = supply[i]
            else:
                midval = demand[j]
            pwl_x[k][1] = 200.0
            pwl_x[k][2] = 400.0
            pwl_x[k][3] = midval
            pwl_y[k][1] = pwl_x[k][1] * pwl_slope[0]
            pwl_y[k][2] = pwl_y[k][1] + \
                pwl_slope[1] * (pwl_x[k][2] - pwl_x[k][1])
            pwl_y[k][3] = pwl_y[k][2] + \
                pwl_slope[2] * (pwl_x[k][3] - pwl_x[k][2])
            k = k + 1
    # 建立数学模型
    model = cplex.Cplex()
    model.set_problem_name("transport_py")
    model.objective.set_sense(model.objective.sense.minimize)
    # 定义决策变量 x_{ij} 表示工厂 i 到展厅 j 的运输量
    colname_x = ["x{0}".format(i + 1) for i in range(n)]
    model.variables.add(obj=[0.0] * n, lb=[0.0] * n,
                        ub=[cplex.infinity] * n, names=colname_x)
    # y(varindex(i, j)) is used to model the PWL cost associated with
    # this shipment.
    colname_y = ["y{0}".format(j + 1) for j in range(n)]
    model.variables.add(obj=[1.0] * n, lb=[0.0] * n,
                        ub=[cplex.infinity] * n, names=colname_y)
    # 供给必须满足需求约束
    for i in range(nbSupply):
        ind = [varindex(i, j) for j in range(nbDemand)]
        val = [1.0] * nbDemand
        row = [[ind, val]]
        model.linear_constraints.add(lin_expr=row,
                                     senses="E", rhs=[supply[i]])
    # 需求必须符合供给约束
    for j in range(nbDemand):
        ind = [varindex(i, j) for i in range(nbSupply)]
        val = [1.0] * nbSupply
        row = [[ind, val]]
        model.linear_constraints.add(lin_expr=row,
                                     senses="E", rhs=[demand[j]])
    # 添加分段线性约束
    for i in range(n):
        # preslope is the slope before the first breakpoint.  Since the
        # first breakpoint is (0, 0) and the lower bound of y is 0, it is
        # not meaningful here.  To keep things simple, we re-use the
        # first item in pwl_slope.
        # Similarly, postslope is the slope after the last breakpoint.
        # We just use the same slope as in the last segment; we re-use
        # the last item in pwl_slope.
        model.pwl_constraints.add(vary=n + i,
                                  varx=i,
                                  preslope=pwl_slope[0],
                                  postslope=pwl_slope[-1],
                                  breakx=pwl_x[i],
                                  breaky=pwl_y[i],
                                  name="p{0}".format(i + 1))
    # solve model
    model.solve()
    model.write('transport_py.lp')
    # Display solution
    print()
    print("Solution status :", model.solution.get_status())
    print("Cost            : {0:.2f}".format(
        model.solution.get_objective_value()))
    print()
    print("Solution values:")
    for i in range(nbSupply):
        print("   {0}: ".format(i), end='')
        for j in range(nbDemand):
            print("{0:.2f}\t".format(
                model.solution.get_values(varindex(i, j))),
                end='')
        print()
if __name__ == "__main__":
    transport(True)



相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
2天前
|
机器学习/深度学习 数据采集 算法
Python信贷风控模型:Adaboost,XGBoost,SGD, SVC,随机森林, KNN预测信贷违约支付|数据分享
Python信贷风控模型:Adaboost,XGBoost,SGD, SVC,随机森林, KNN预测信贷违约支付|数据分享
10 1
Python信贷风控模型:Adaboost,XGBoost,SGD, SVC,随机森林, KNN预测信贷违约支付|数据分享
|
2天前
|
数据采集 机器学习/深度学习 搜索推荐
使用Python实现推荐系统模型
使用Python实现推荐系统模型
15 1
|
3天前
|
人工智能 自然语言处理 Python
使用Python实现自然语言处理模型
使用Python实现自然语言处理模型
13 1
|
3天前
|
机器学习/深度学习 算法 搜索推荐
Python用机器学习算法进行因果推断与增量、增益模型Uplift Modeling智能营销模型
Python用机器学习算法进行因果推断与增量、增益模型Uplift Modeling智能营销模型
30 12
|
3天前
|
机器学习/深度学习 算法 vr&ar
PYTHON用时变马尔可夫区制转换(MARKOV REGIME SWITCHING)自回归模型分析经济时间序列
PYTHON用时变马尔可夫区制转换(MARKOV REGIME SWITCHING)自回归模型分析经济时间序列
16 4
|
4天前
|
API vr&ar Python
Python 用ARIMA、GARCH模型预测分析股票市场收益率时间序列(上)
Python 用ARIMA、GARCH模型预测分析股票市场收益率时间序列
31 5
|
4天前
|
人工智能 Python
【AI大模型应用开发】【LangChain系列】实战案例1:用LangChain写Python代码并执行来生成答案
【AI大模型应用开发】【LangChain系列】实战案例1:用LangChain写Python代码并执行来生成答案
9 0
|
7天前
|
数据可视化 Python
Python模型评估与选择:面试必备知识点
【4月更文挑战第17天】本文深入探讨了Python模型评估与选择在面试中的关键点,包括性能度量、过拟合与欠拟合识别、模型比较与选择、模型融合和偏差-方差权衡。强调了避免混淆评估指标、忽视模型验证和盲目追求高复杂度模型的常见错误,并提供相关代码示例,如交叉验证、网格搜索和超参数调优。通过理解这些概念和技巧,可在面试中展示出色的数据科学能力。
31 12
|
9天前
|
机器学习/深度学习 数据可视化 Linux
python用ARIMA模型预测CO2浓度时间序列实现
python用ARIMA模型预测CO2浓度时间序列实现
22 0
|
9天前
|
Python 数据可视化 索引
PYTHON用GARCH、离散随机波动率模型DSV模拟估计股票收益时间序列与蒙特卡洛可视化
PYTHON用GARCH、离散随机波动率模型DSV模拟估计股票收益时间序列与蒙特卡洛可视化
24 0
PYTHON用GARCH、离散随机波动率模型DSV模拟估计股票收益时间序列与蒙特卡洛可视化