Gurobi學習筆記——tuplelist和tupledict


本文將介紹Gurobi中常用的兩種數據結構:tuplelisttupledict,並以案例文件中的網絡流問題進行講解

Gurobi的tuplelist類是Python中list的子類,tupledictdict的子類。

在使用Gurobi建模時,推薦使用這兩種類型,方便約束的編寫,同時可以加快模型的讀取速度。接下來將進行詳細介紹:

本文主要參考了Gurobi 9.0.0目錄中的refman.pdf

以下案例代碼,不顯式說明from gurobipy import *

tuplelist

構造函數

在構造函數中傳入list對象可以將其轉化為tuplelist類型

l = tuplelist(list)
例子:
l = tuplelist([(1,2),(1,3),(2,4)])
<gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
 ( 2 , 4 )

篩選元素

select(pattern)函數返回一個根據pattern篩選的tuplelist對象。
tuplelist可以通過下標訪問,但只能通過數字索引訪問

> l = tuplelist([(1,2),(1,3),(2,4)])
<gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
 ( 2 , 4 )
 > l.select() #返回所有的對象
<gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
 ( 2 , 4 )
 # 可使用通配符
 > l.select(1,'*') # 返回第一位為1,第二為任意符號的元素
 <gurobi.tuplelist (2 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
# 通過索引訪問
l[0] # 訪問第一個元素(1,2)

tuplelist也可用in對其內部是否包含該元素進行判斷(重寫了__contains__()

> l = tuplelist([(1,2),(1,3),(2,4)])
<gurobi.tuplelist (3 tuples, 2 values each):
 ( 1 , 2 )
 ( 1 , 3 )
 ( 2 , 4 )
 # 判斷是否有(1,2)
 if (1,2) in l:
 	print("Tuple (1,2) is in tuplelist l")

tupledict

tupledict是Python類dict的子類,由鍵值兩部分組成。key為上文提到的tuplelist,value為Gurobi的變量Var類型

tupledict可以方便地索引下標以及創建表達式

創建tupledict

  1. 構造函數

    # 一個list,內部一個個元組,按照key,value先排好
    dd = [((1,1),'a'), ((1,2),'b'),((2,1),'c'),((2,2),'d')]
    # 相當於二元變量d_(i,j)
    d = tupledict(dd)
    {(1, 1): 'a', (1, 2): 'b', (2, 1): 'c', (2, 2): 'd'}
    
  2. 批量轉換

    multidict(data)函數提供將一個dict類型的對象data轉化為tupledict。如果data的value包含了N個元素,則該函數返回的N+1個對象,第1個對象為data中的keys,后續對象為將N個value打散的tupledict

    一次輸入關於該元素的多組數據,並自動拆分為具有相同keys的tupledict

    keys, dict1, dict2 = multidict({'k1':[1,2],
    								'k2':[3,4],
    								'k3':[5,6]})
    # 生成結果
    # 原data中的鍵 tuplelist類型
     keys = ['k1', 'k2', 'k3']
    # 第一列元素
    dict1 = {'k1': 1, 'k2': 3, 'k3': 5}
    # 第二列元素
    dict2 = {'k1': 2, 'k2': 4, 'k3': 6}
    
  3. 多元決策變量

    在創建模型后,調用addVars()函數,創建多維決策變量,該決策變量為tupledict類型

    m = Model()
    x = m.addVars(2,3) # 創建2*3的決策變量
    # 使用下標方式進行訪問
    x[0,0] #<gurobi.Var C0>
    

篩選元素

  1. 與tuplelist相同,使用select()函數可以篩選出符合條件的key的value
    dict一樣,使用[]進行訪問,但不可使用通配符,也就是每次只能選出一個元素
d = tupledict([((1,1),'a'), ((1,2),'b'),((2,1),'c'),((2,2),'d')])
{(1, 1): 'a', (1, 2): 'b', (2, 1): 'c', (2, 2): 'd'}
# 顯示所有元素
d.select()
# pattern篩選元素
d.select(1,'*')
['a', 'b']
# 下標訪問
d[1,1]
'a'
d[1,'*'] #錯誤的使用

集合運算(求和,連乘)

tupledict對象可進行求和sum(),乘積運算prod()。運算過后將會生成Gurobi內置的LinExpr()表達式對象,可作為約束添加至模型中。

sum(pattern)

pattern參數類似select的用法,可以為求和增加篩選條件

如果沒有符合條件的pattern,則返回0

x = m.addVars(2,2)
expr = x.sum() # LinExpr: x[0,0] + x[0,1] + x[1,0] + x[1,1]
expr = x,sum(1, '*') # LinExpr: x[1,0] + x[1,1]

keys, dict1, dict2 = multidict({'k1':[1,2],
								'k2':[3,4],
								'k3':[5,6]})
dict1.sum() # LinExpr: 1 + 3 +5 = 9

prod(coeff,pattern)

coeff為一個dict類型,指定待計算的元素的系數。coeff的key要與待計算的集合中的key能對應

x = m.addVars(2,2)
coeff = {(0,0):1, (0,1):2,(1,0):3,(1,1):4}
expr = x.prod(coeff) # x[0,0] + 2*x[0,1] + 3*x[1,0] + 4*x[1,1]
expr = x.prod(coeff, 1,"*") # 3*x[1,0] + 4*x[1,1]

網絡流案例詳解

案例源文件根目錄\example\python\netflow.py

該問題涉及到2種商品,2個發貨地,3個收貨地的配置問題,提供有各節點來往的成本,各地的最大庫存量(流量)以及各節點的供、求關系,求滿足供應條件的最小成本配置。

目標函數:

目標函數

約束1:對於每種商品而言,不超過最大每個節點最大的容納量

約束1

約束2:對於每種商品而言,滿足每個節點的供應需求(本案例數據中,供給方為正,需求方為負)
約束2

將約束2拆開來看,則為:

約束2.1 :供給方j的供應量=從j點流出的量
約束2.1

約束2.2:匯聚到需求方j的量+j點的需求(負數)=0
約束2.2

中文注釋一下的代碼粘貼如下:

#!/usr/bin/env python3.7

# Copyright 2019, Gurobi Optimization, LLC

# Solve a multi-commodity flow problem.  Two products ('Pencils' and 'Pens')
# are produced in 2 cities ('Detroit' and 'Denver') and must be sent to
# warehouses in 3 cities ('Boston', 'New York', and 'Seattle') to
# satisfy demand ('inflow[h,i]').
#
# Flows on the transportation network must respect arc capacity constraints
# ('capacity[i,j]'). The objective is to minimize the sum of the arc
# transportation costs ('cost[i,j]').

import gurobipy as gp
from gurobipy import GRB

# Base data 
# 商品種類
commodities = ['Pencils', 'Pens'] 
# 所有的節點,作為key
nodes = ['Detroit', 'Denver', 'Boston', 'New York', 'Seattle']

arcs, capacity = gp.multidict({
    ('Detroit', 'Boston'):   100,
    ('Detroit', 'New York'):  80,
    ('Detroit', 'Seattle'):  120,
    ('Denver',  'Boston'):   120,
    ('Denver',  'New York'): 120,
    ('Denver',  'Seattle'):  120})
# arcs為tuplelist,表示節點間的連通關系
# capacity為tupledict,表示節點間的流量

# Cost for triplets commodity-source-destination
cost = {
    ('Pencils', 'Detroit', 'Boston'):   10,
    ('Pencils', 'Detroit', 'New York'): 20,
    ('Pencils', 'Detroit', 'Seattle'):  60,
    ('Pencils', 'Denver',  'Boston'):   40,
    ('Pencils', 'Denver',  'New York'): 40,
    ('Pencils', 'Denver',  'Seattle'):  30,
    ('Pens',    'Detroit', 'Boston'):   20,
    ('Pens',    'Detroit', 'New York'): 20,
    ('Pens',    'Detroit', 'Seattle'):  80,
    ('Pens',    'Denver',  'Boston'):   60,
    ('Pens',    'Denver',  'New York'): 70,
    ('Pens',    'Denver',  'Seattle'):  30}

# Demand for pairs of commodity-city
inflow = {
    ('Pencils', 'Detroit'):   50,
    ('Pencils', 'Denver'):    60,
    ('Pencils', 'Boston'):   -50,
    ('Pencils', 'New York'): -50,
    ('Pencils', 'Seattle'):  -10,
    ('Pens',    'Detroit'):   60,
    ('Pens',    'Denver'):    40,
    ('Pens',    'Boston'):   -40,
    ('Pens',    'New York'): -30,
    ('Pens',    'Seattle'):  -30}

# Create optimization model
m = gp.Model('netflow')

# Create variables
# 創建以commodities,arcs為下標的三維決策變量 flow_h,i,j
# obj=cost這種寫法在創建變量時,設定好了目標函數
flow = m.addVars(commodities, arcs, obj=cost, name="flow")

# 添加約束1
# Arc-capacity constraints
m.addConstrs(
    (flow.sum('*', i, j) <= capacity[i, j] for i, j in arcs), "cap")

# 約束1的等價寫法,將生成器改為for循環,逐個添加
# Equivalent version using Python looping
# for i, j in arcs:
#   m.addConstr(sum(flow[h, i, j] for h in commodities) <= capacity[i, j],
#               "cap[%s, %s]" % (i, j))

# 添加約束2
# Flow-conservation constraints
m.addConstrs(
    (flow.sum(h, '*', j) + inflow[h, j] == flow.sum(h, j, '*')
        for h in commodities for j in nodes), "node")

# 約束2的等價寫法,將生成器改為for循環,逐個添加
# Alternate version:
# m.addConstrs(
#   (gp.quicksum(flow[h, i, j] for i, j in arcs.select('*', j)) + inflow[h, j] ==
#     gp.quicksum(flow[h, j, k] for j, k in arcs.select(j, '*'))
#     for h in commodities for j in nodes), "node")

# Compute optimal solution
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    solution = m.getAttr('x', flow)
    for h in commodities:
        print('\nOptimal flows for %s:' % h)
        for i, j in arcs:
            if solution[h, i, j] > 0:
                print('%s -> %s: %g' % (i, j, solution[h, i, j]))
                


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM