Gurobi學習筆記—矩陣變量及八皇后問題案例


Gurobi學習筆記—矩陣變量及八皇后問題案例

本節將介紹Gurobi中的矩陣變量MVar,並且以Gurobi案例目錄下的八皇后案例進行解讀

矩陣變量MVartupledict有所區別。

矩陣變量Mvar是NumPy ndarray形式的變量,只能使用下標索引,通過Numpy的矩陣與MVar相乘得到線性/多項式矩陣表達式MLinExprMQuadExpr

矩陣變量的創建

  1. Model.addMVar() 常用

    addMVar ( shape, lb=0.0, ub=GRB.INFINITY, obj=0.0, vtype=GRB.CONTINUOUS, name="" ) 
    shape:矩陣向量的維度
    lb, ub:分別為變量的上下限(可選)
    obj:變量在目標函數表達式中的系數(可選)
    vtype:變量類型(可選)
    x = model.addMVar(10) # 包含10個變量的一維矩陣變量
    y = model.addMVar((3,4), vtype GRB.BINARY) # 3x4的0-1變量矩陣
    

矩陣變量的運算

Gurobi中的矩陣變量可以當做Numpy中的矩陣,對其進行運算以創建表達式,需要注意的是維數一定要兼容。例如表示如下式子:

x + 2 y + 3 z <= 4
x +   y       >= 1
x, y, z binary

可寫作

# 一維矩陣變量
x = m.addMVar(shape=3, vtype=GRB.BINARY, name="x")
# 左端項系數矩陣
A = np.array([[1,2,3],[1,1,0]])
# 右端項
rhs = np.array([4.0, -1.0])
m.addConstr(A @ x <= rhs, name ="c")

矩陣變量支持切片操作,使用[],:選取矩陣中特定的元素,這一點與tupledict對象(即addVars())的select()不同,后者采用*表示通配符運算

矩陣變量支持求和操作,但與tupledict變量不同。矩陣變量采用切片選取元素,后直接調用sum()函數

y = model.addMVar((3,4), vtype GRB.BINARY) # 3x4的0-1變量矩陣
y[:,1]#選取第一列元素

與tuplelist對象的對比

tupledict變量由model.addVars()或者multidict()函數創建,通過創建時使用的indices進行訪問,同時具有select(), sum(), prod() 的功能篩選元素並快速構建表達式。

model.addVars(*indices)方式創建的對象,本質上是多種集合的笛卡爾乘積的結果

x = m.addVars(2,3, vtype=GRB.BINARY)
# 生成的元素是
x(0,0) x(0,1) x(0,2)
x(1,0) x(1,1) x(1,2)

# 三個組的笛卡爾積
y = m.addVars((1,2),(1,3),(2,3))
x(1,1,2) x(1,1,3) x(1,3,2) x(1,3,3)
x(2,1,2) x(2,1,3) x(2,3,2) x(2,3,3)

tupledict對象支持采用字符串類型創建索引(字符串作為key),並且索引可使用通配符(select方法)
注意:方括號索引時不能使用*

MVar僅能通過數字按行列索引

commodities = ['Pencils', 'Pens']
nodes = ['Detroit', 'Denver']
flow = m.addVars(commodities, nodes, obj=cost, name="flow")
flow.select('*','*')
flow[*,*] # 錯誤的引用方式
flow['pencil','Detroit']
flow['Pencils','Denver']

八皇后案例

該案例存在於Gurobi目錄下案例文件夾中根目錄\examples\python\matrix2.py

八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。(摘自百度百科

假設決策變量x(i,j)為0-1變量,變量為1代表在第i行第j列放置皇后,需滿足下列條件:

(1) 每行只放置一個皇后

(2)每列只放置一個皇后

(3)每條正對角線只放一個皇后

(4)每個斜對角線只放一個皇后

約束3,4由生成器生成,在代碼中可能一下子看不懂,本人以n=4為例,推了一下正對角線的計算,斜對角線同理

#!/usr/bin/env python3.7

# Copyright 2019, Gurobi Optimization, LLC

# This example uses the Python matrix API to formulate the n-queens
# problem; it maximizes the number queens placed on an n x n
# chessboard without threatening each other.
#
# This example demonstrates NumPy slicing.

import numpy as np
import scipy.sparse as sp
import gurobipy as gp
from gurobipy import GRB


# Size of the n x n chess board
n = 8

try:
    # Create a new model
    m = gp.Model("matrix2")

    # Create a 2-D array of binary variables
    # x[i,j]=1 means that a queen is placed at square (i,j)
    x = m.addMVar((n, n), vtype=GRB.BINARY, name="x")

    # Set objective - maximize number of queens
    m.setObjective(x.sum(), GRB.MAXIMIZE)

    # Add row and column constraints
    # 為每一行、列添加約束
    for i in range(n):

        # At most one queen per row
        # 每一行最多放置一個皇后
        m.addConstr(x[i, :].sum() <= 1, name="row"+str(i))

        # At most one queen per column
        # 每一列最多放置一個皇后
        m.addConstr(x[:, i].sum() <= 1, name="col"+str(i))

    # Add diagonal constraints
    # 添加對角線約束
    for i in range(1, 2*n):

        # At most one queen per diagonal
        # 每個正對角線最多防止一個皇后
        # 生成器生成對矩陣的切片索引
        diagn = (range(max(0, i-n), min(n, i)), range(min(n, i)-1, max(0, i-n)-1, -1))
        m.addConstr(x[diagn].sum() <= 1, name="diag"+str(i))

        # At most one queen per anti-diagonal
        # 每個斜對角線最多防止一個皇后
        # 生成器生成對矩陣的切片索引
        adiagn = (range(max(0, i-n), min(n, i)), range(max(0, n-i), min(n, 2*n-i)))
        m.addConstr(x[adiagn].sum() <= 1, name="adiag"+str(i))

    # Optimize model
    # 開始優化模型
    m.optimize()

    print(x.X)
    print('Queens placed: %g' % m.objVal)

except gp.GurobiError as e:
    print('Error code ' + str(e.errno) + ": " + str(e))

except AttributeError:
    print('Encountered an attribute error')


免責聲明!

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



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