CPLEX는 상용 최적화 프로그램으로 학생 또는 연구자들에게는 무료로 제공되는 프로그램이다.
CPLEX를 활용하면 복잡하고 어려운 최적화 문제를 풀 수 있다.
물론 경우에 따라 NP-hard에 속하는 문제라면 시간이 오래 걸리지만, CPLEX를 통해서 언젠가는
최적해를 구할 수 있다.
CPLEX를 이용해서 최적화 문제를 풀기 위해서는 최적화 문제의 수리모델을 CPLEX의 OPL을 이용해서 코딩하거나
아니면 JAVA, Python API를 이용해서 코딩을 할 수도 있다.
본 포스팅에서는 Python API를 이용해서 최적화 문제를 코딩하는 방법을 작성해보려고 한다.
먼저 CPLEX를 통해서 풀려고 하는 최적화 문제를 아래와 같이 정의 하겠다.
(본 포스팅에서는 MILP문제를 다루려고 한다.)
$$ Min \; \; \sum_{i=0}^2 f_i \cdot y_i + \sum_{i=0}^2 \sum_{j=0}^3 c_{ij} \cdot x_{ij}$$
$$ s.t. \quad \sum_{i=0}^2 x_{ij} = d_j \quad \forall j$$
$$ \quad \; \; \quad \sum_{j=0}^3x_{ij} = M \cdot y_i \quad \forall i$$
$$\quad y_i \; \text{is binary,} \; \; x_{ij} \geq 0 \quad \forall i,j $$
위의 MILP 모형에서 결정변수(Decision variable)은 $ x_{ij},\; y_i $이고 나머지 변수들은 전부 파라미터이다.
이번 포스팅에서는 MILP 수리모형을 코딩을 어떤식으로 해야하는지를 중점적으로 다루기 때문에
MILP모형에 대한 자세한 설명은 생략하겠다.
코딩을 위해서 먼저 선행되어야 하는것으로 (1) CPLEX설치, (2) docplex library 설치인데, 관련된 사항은
아래 유튜브 동영상을 참고하길 바란다. 친절하게 설명되어 있으므로 쉽게 설치할 수 있을 것이다.
참고 영상 : https://www.youtube.com/watch?v=q7iVDGartf4&t=26s
다음과 같은 순서로 MILP 수리모형을 코딩하면 된다.
1. docplex import 하기
CPLEX를 Python으로 코딩을 하기 위해서는 docplex의 Model 모듈을 import해야 한다.
아래 예제 코드를 참고하길 바란다.
import numpy as np
import pandas as pd
from docplex.mp.model import Model
docplex는 numpy와 친화적이기 때문에 같이 import하면 좋을 것이다.
2. Object 생성 및 파라미터 설정
MILP 모델을 풀기 위해서 가장 먼저 코딩해야하는 것이, Model() 모듈을 이용해서 객체를 생성하고,
파라미터를 설정하는 것이다.
위에서 정의한 MILP 모형의 파라미터를 코딩하면 아래의 예제 코드와 같다.
model = Model('Problem 11-30') # Problem 11-30이라는 modeling object를 정의
f = [200, 400, 225] # $f_i$를 정의
d = [11, 18, 15, 25] # $ d_i$를 정의
c = [[6,5,9,3], [4,3,5,6], [5,8,2,4]] # $ c_{ij}$를 정의.
3. 결정변수 정의
세번째는 결정변수를 정의하는 것이다. docplex를 이용하면 다양한 방식으로 결정변수를 정의할 수 있는데,
필자는 아래와 같은 방식으로 코딩하는 것을 추천한다.
왜냐하면 제안하는 방식을 활용하면 결정변수의 차원이 4차원 이상이 되어도 쉽게 코딩이 가능하기 때문이다.
먼저 예제 코드를 살펴보자.
N = [0,1,2] # i의 범위를 의미
N1 = [0,1,2,3] # j의 범위를 의미
M = 200
Y = ((i) for i in N ) # Y는 i에 대한 결정변수, 아래 결정변수를 정의하기 위한 dictionary를 설정하는 것임.
X = ((i,j) for i in N for j in N1) # X는 i,j에 대한 결정변수, 아래 결정변수를 정의하기 위한 dictionary를 설정하는 것임.
y = model.binary_var_dict(Y, name = 'y', lb = 0) # 위에서 정의한 Y를 이용하여 결정변수를 정의 (이진변수)
x = model.continuous_var_dict(X, name = 'x', lb = 0) # 위에서 정의한 Y를 이용하여 결정변수를 정의
만약 $f_{ijvp}$를 위와같은 방식으로 정의하려면 어떻게 해야할까? 아래와 같이 작성하면 된다.
I = [0,1,2] # i의 범위를 의미
J = [0,1,2,3] # j의 범위를 의미
V = [0,1,2,3,4] # v의 범위를 의미
P = [0,1,2] # p의 범위를 의미
F = ((i,j,v,p) for i in I for j in J for v in V for p in P )
f = model.binary_var_dict(f, name = 'y', lb = 0) # 위에서 정의한 F를 이용하여 결정변수를 정의(binary variable)
f = model.integer_var_dict(X, name = 'x', lb = 0) # 위에서 정의한 F를 이용하여 결정변수를 정의 (integer variable)
4. 목적함수 및 제약식 정의
결정변수를 정의했고, 그를 바탕으로 제약식 및 결정변수를 정의하면 된다.
여기서 사용되는 것은 add_constraints와 set_objective()인데 아래 예제 코드를 통해서 살펴보자.
어렵지 않으니 아래 코드의 주석을 참고하면 쉽게 이해할 수 있을 것이다.
obj = sum(f[i]*y[i] for i in N) + sum(c[i][j]*x[i,j] for i in N for j in N1) # 목적함수 정의
model.add_constraints(sum(x[i,j] for i in N) == d[j] for j in N1) # 제약식 정의
model.add_constraints(sum(x[i,j] for j in N1) <= M*y[i] for i in N) #제약식 정의
model.set_objective('min', obj) # model의 목적함수를 위에서 정의한 obj로 설정하고, 최소화 문제인 것을 정의
model.solve(log_output=True) # model object를 위에서 정의한 제약식과 목적함수를 이용해서 푸는 코드(log_output은 solver가 문제를 풀때, 진행 상태를 보여주는 것을 의미
print(model.solve_details) # 문제가 풀렸을때 그 해에 대한 detail한 정보를 출력
print(model.export_as_lp_string()) # 추가한 제약식 및 목적함수를 LP형태로 출력
print(model.solution) # 문제의 solution을 출력
print("Solution status : " , model.get_solve_status()) # 찾은 해가 optimal, feasible, infeasible인지 출력
print("Objective value : " , model.objective_value ) # 목적함수값 출력
5. 실제 코드 실행 결과
아래는 파이참에 실제로 코드를 입력한 내용이다.
아래는 파이참에 실제로 코드를 입력하고 얻어진 결과창이다.
'Python' 카테고리의 다른 글
Numpy 사용법 (6) - np.argmax(), np.argsort() (0) | 2022.04.23 |
---|---|
Numpy 사용법 (5) - np.where() (2) | 2022.04.22 |
Numpy 사용법 정리(4) (0) | 2022.04.21 |
Numpy 사용법 정리(3) (0) | 2022.04.21 |
Numpy 사용법 정리(2) (0) | 2022.04.20 |