boxpacking.gms : 컨테슬롯 나라 포장 문제

설명

이 모델은 컨테이너에 있는 상자의 최적 구성을 찾으려고 시도합니다.
두 물체의 3차원 측정값을 유지하면서
기하학적 제한. 모델은 순서를 결정하기 위해 다양한 방법을 사용합니다.
상자를 포장해야 하는 곳. 슬롯 나라 Connect는 br1.csv 파일에서 데이터를 읽는 데 사용됩니다.
데이터는 컨테이너 포장에 관한 Bischoff/Ratcliff 논문에서 나온 것입니다.
ORLIB 인스턴스 컬렉션에서 사용할 수 있습니다.http://people.brunel.슬롯 나라uk/~mastjjb/jeb/orlib/thpackinfo.html

대형 모델 유형 :MIP


카테고리 : 슬롯 나라 모델 라이브러리


메인 파일 : 슬롯 나라gms   포함: br1.csv

$title 상자 포장 문제(상자 포장,SEQ=434)

$onText
이 모델은 컨테이너 내 상자의 최적 구성을 찾으려고 시도합니다.
두 물체의 3차원 측정값을 유지하면서
기하학적 제한. 모델은 순서를 결정하기 위해 다양한 방법을 사용합니다.
상자를 포장해야 하는 곳. GAMS Connect는 br1.csv 파일에서 데이터를 읽는 데 사용됩니다.
데이터는 컨테이너 포장에 관한 Bischoff/Ratcliff 논문에서 나온 것입니다.
ORLIB 인스턴스 컬렉션(http://people.brunel.ac.uk/~mastjjb/jeb/orlib/thpackinfo.html)에서 사용할 수 있습니다.

이 공식은 다음에 자세히 설명되어 있습니다.
오클루, 발렌티나 & 푸겐슈, 아르민 & 파멘, 올리비에. (2020).
3D 컨테이너 포장 문제에 대한 새로운 수학적 모델. 10.26127/btuopen-5088. 

키워드: 혼합 정수 선형 계획법, 컨테이너 패킹 문제,
          3D-bin 포장 문제, 박스 포장 문제
$offText

$eolCom !!

$설정되지 않은 경우 DEBUG $set DEBUG 0 !! DEBUG=1은 추가 출력을 활성화합니다.
$METHOD를 설정하지 않은 경우 $set METHOD 일괄 처리 !! [표준, 탐욕, 배치]
$설정되지 않은 경우 FIXLOCDIM $set FIXLOCDIM 0 !! FIXLOCDIM=1은 컨테이너에 배치된 상자의 위치를 고정합니다.
$설정되지 않은 경우 MAXCOPIES $set MAXCOPIES 50 !! 박스 유형당 최대 복사본 수
$설정되지 않은 경우 BATCH_SIZE $set BATCH_SIZE 5 !! 배치당 상자 수
$설정되지 않은 경우 TIMELIMIT $set TIMELIMIT 30 !! 총 시간 제한

* 컨테이너의 3D 측정값을 정의합니다.
$설정되지 않은 경우 X_CONT $set X_CONT 587
$설정되지 않은 경우 Y_CONT $설정 Y_CONT 233
$설정되지 않은 경우 Z_CONT $set Z_CONT 220

* 원시 입력 데이터
box_id '입력 데이터 상자 ID' 설정
    box_id_hdr '입력 데이터 헤더' / x, rot_x, y, rot_y, z, rot_z, nb, Weight/
    mult_box '동일한 유형의 여러 상자에 대한 입력 데이터 상자 복사 ID 색인' / cp1*cp%MAXCOPIES% /;

매개변수
    box_data(box_id<, box_id_hdr)
    희미한_raw(box_id,box_id_hdr);

* 데이터 읽기
$onEmbeddedCode 연결:
- CSV리더:
    파일: br1.csv
    이름: box_data
    헤더: 참
    indexColumns: 1
    valueColumns: "2:lastCol"
- GAMS작성기:
    기호: 모두
$offEmbeddedCode

* 상자 수를 줄이고,
* 전체 규모 문제는 계산적으로 어려워진다
box_data(box_id, 'nb') = ceil(box_data(box_id, 'nb')/2.5);

표시$%DEBUG% box_data

$eval MAXB 카드(box_id)*%MAXCOPIES%

* 원시 데이터 일관성 검사
abort$[smax(box_id, box_data(box_id, 'nb')) > %MAXCOPIES%] '입력 데이터에 지원되는 것보다 더 많은 상자 복사본이 포함되어 있습니다. MAXCOPIES 늘리기';

* 내부 데이터 구조
Set bb 슈퍼 박스 세트 / b1*b%MAXB%, vbox 'virtual box' /
    b_exist(bb) 기존 상자 세트
    b(bb) 상자의 동적 하위 집합
    bmap(box_id,mult_box,bb) 복사 ID가 있는 입력 데이터 상자 ID를 내부 상자 ID로 매핑
    bmap_red(bb,box_id) 입력 데이터 상자 ID를 내부 상자 ID로 매핑    
    d(box_id_hdr) 치수 / x, y, z /
    r(box_id_hdr) 상자 회전 가능성 / rot_x, rot_y, rot_z /    
    o 상자의 6가지 가능한 방향 / o1*o6 /                         
    입력 차원과 방향 차원의 매핑을 정의하는 rmap(o,d,d) 회전
                            / o1.(x.x,y.y,z.z) 'x->x y->y z->z를 입력하세요'                                              
                              o2.(x.x,y.z,z.y) 'x 축을 따라 입력을 90도 회전 x->x y->z z->y (rot_x = 0인 경우 불가능)'               
                              o3.(x.z,y.y,z.x) 'y 축을 따라 입력을 90도 회전 x->z y->y z->x (rot_y = 0인 경우 불가능)'               
                              o4.(x.y,y.x,z.z) 'z 축을 따라 입력을 90도 회전 x->y y->x z->z (rot_z = 0인 경우 불가능)'               
                              o5.(x.z,y.x,z.y) 'x+z 축을 따라 입력을 90도 회전 x->z y->x z->y (rot_x = 0 또는 rot_z = 0인 경우 불가능)'  
                              o6.(x.y,y.z,z.x) 'x+y 축을 따라 입력을 90도 회전 x->y y->z z->x (rot_x = 0 또는 rot_y = 0인 경우 불가능)' /
    ro(r,o) 회전 제한 / rot_x.(o2,o5,o6), rot_y.(o3,o6), rot_z.(o4,o5) /
    슬롯 나라(bb,o) 사용 가능한 방향 / #bb.#o /;

별칭 (bb,bb1,bb2), (b,b1,b2), (d,d1);

매개변수
    희미한_cont(d) '컨테이너 크기' / x %X_CONT%, y %Y_CONT%, z %Z_CONT% /
    희미한_o(bb,o,d) '방향 o에 대한 상자 bb의 크기'
    rot(bb, r) '허용된 회전'
    Box_vol(bb) '각 상자의 부피';

* 원시 상자 ID를 내부 상자 ID에 매핑
b('b1') = 예;
루프((box_id,mult_box)$[ord(mult_box) <= box_data(box_id,'nb')],
  bmap(box_id,mult_box,b) = 예;
  b(bb) = b(bb-1);
);
옵션 클리어=b;
옵션 bmap_red<bmap, b_exist<bmap;

* 모든 회전에 대한 상자 크기 계산
Dim_o(bb,o,d) = sum((bmap_red(bb,box_id),rmap(o,d1,d)), box_data(box_id,d1));

* 사용 가능한 회전 계산 
rot(bb, r) = sum(bmap_red(bb,box_id), box_data(box_id, r));

* 각 상자의 부피를 m3으로 확장하여 계산합니다.
Box_vol(bb) = prod(d, 희미한_o(bb,'o1', d)/100);

* 회전 제한으로 인해 상자의 불가능한 방향을 비활성화합니다.
loop(r, 슬롯 나라(bb,o)$(ro(r,o) 및 rot(bb,r)) = no);

바이너리 변수
    OMEGA(bb) '컨테이너에 있는 상자(1) 여부(0)'
    ALPHA(bb,o) '방향이 o (1)이거나 (0)이 아닌 컨테이너에 있는 상자'
    RELPOS(bb,bb,d) '상대 위치: 첫 번째 상자 위치 + 치수 <= 두 번째 상자 위치인 경우 1';

음수가 아닌 변수
    LOC(bb,d) '(x,y,z) 컨테이너에 있는 bb의 왼쪽 하단 모서리 위치'
    DIM(bb,d) '(x,y,z) 방향을 고려한 컨테이너에 있는 상자 bb의 크기';

변수
    VOL '목표 값 - 컨테이너에 있는 모든 상자의 총 부피';

방정식
    eq_def_DIM(bb,d) '방향을 고려하여 컨테이너의 상자 크기 정의'
    eq_커플링_ALPHA_OMEGA(bb) '컨테이너의 상자에 대해서만 방향 선택' 
    eq_inside_container(bb,d) '컨테이너 크기 존중'
    eq_deactivate_RELPOS(bb,bb,d) 'RELPOS는 컨테이너의 첫 번째 상자인 경우 0이 아닌 값만 사용해야 합니다.'
    eq_no_overlap(bb,bb, d) '컨테이너의 상자는 겹쳐서는 안 됩니다.' 
    eq_def_RELPOS(bb,bb) 'RELPOS 정의, 즉 첫 번째 상자 왼쪽/뒤/아래 두 번째 상자 (1), 그렇지 않으면 (0)'
    eq_def_VOL '목적 함수 - 컨테이너의 볼륨 활용도 극대화';

* (23/24/25) [숫자는 논문의 방정식 번호와 일치합니다]
eq_def_DIM(b,d)$[not sameas(b,'vbox')].. DIM(b,d) =e= sum(o, 희미한_o(b,o,d)*ALPHA(b,o)); 

* (26)
eq_connect_ALPHA_OMEGA(b)$[not sameas(b,'vbox')].. sum(슬롯 나라(b,o), ALPHA(b,o)) =e= OMEGA(b);

* (27 a/b/c)
eq_inside_container(b,d).. LOC(b,d) + DIM(b,d) =l= 희미한_cont(d) * OMEGA(b);

* (28 a/b/c)
eq_deactivate_RELPOS(b1(bb1),b2(bb2),d)$[ord(bb1)<ord(bb2)].. RELPOS(b1,b2,d) + RELPOS(b2,b1,d) =l= OMEGA(b1);

* (29)
eq_def_RELPOS(b1(bb1),b2(bb2))$[ord(bb1)<ord(bb2)].. sum(d, RELPOS(b1,b2,d) + RELPOS(b2,b1,d)) =g= OMEGA(b1) + OMEGA(b2) - 1;

* (30 a/b/c)
eq_no_overlap(b1,b2,d)$[not sameas(b1,b2)].. LOC(b1,d) + DIM(b1,d) =l= LOC(b2,d) + 희미한_cont(d)*(1-RELPOS(b1,b2,d));

* (31)
eq_def_VOL.. VOL =e= sum(b, Box_vol(b)*OMEGA(b));

모델 cpp / 모두 /;

cpp.reslim = %TIMELIMIT%;

$ifThenI %system.mip%==cplex
* Cplex 옵션 파일 생성
    $$onEcho > cplex.opt
    해결 최종 0
    밉스타트 1
    $$off에코
* 옵션 파일 사용
    cpp.opt파일 = 1;    
$elseIfI %system.mip%==구로비
* 구로비 옵션 파일 생성
    $$onEcho > gurobi.opt
    해결 고정 0
    밉스타트 1
    norelheurtime %TIMELIMIT%
    $$off에코
* 옵션 파일 사용
    cpp.opt파일 = 1;    
$endIf

스칼라 num_of_bat;
num_of_bat = ceil(카드(b_exist)/%BATCH_SIZE%);

옵션 limrow=0, limcol=0, solprint=off,solvlink=5;
$ifE %DEBUG%==1 옵션 limrow=1e6, limcol=1e6, solprint=on;

* 기존의 모든 상자를 한 번에 해결
$ifThenI.방법 %METHOD%==표준
    b(b_exist) = 예;
    cpp max VOL은 MIP를 사용하여 해결합니다.

* 각 상자에 대해 반복적으로 해결
$elseIfI.method %METHOD%==탐욕스러운
루프(bb$b_exist(bb),
    b(bb) = 그렇습니다;
    cpp max VOL은 MIP를 사용하여 해결합니다.
    OMEGA.fx(bb) = 라운드(OMEGA.l(bb));
    $$ifE %FIXLOCDIM%=1 ALPHA.fx(bb,o) = round(ALPHA.l(bb,o)); LOC.fx(bb,d) = LOC.l(bb,d); DIM.fx(bb,d) = DIM.l(bb,d);    
  );

* n개의 상자를 일괄 처리하고 모델을 해결합니다.
$elseIfI.method %METHOD%==배치
스칼라 i;
for(i = 1 ~ num_of_bat,
    b(bb)$(ord(bb) <= %BATCH_SIZE%*i) = 예;
    cpp max VOL은 MIP를 사용하여 해결합니다.
    OMEGA.fx(b) = 라운드(OMEGA.l(b));
    $$ifE %FIXLOCDIM%=1 ALPHA.fx(b,o) = round(ALPHA.l(b,o)); LOC.fx(b,d) = LOC.1(b,d); DIM.fx(b,d) = DIM.l(b,d);  
);
$endIf.방법

매개변수 담당자(*) 보고서;
rep('총 상자 수') = 카드(b_exist);
rep('컨테이너에 있는 상자 수') = sum(b_exist, OMEGA.l(b_exist));
rep('컨테이너의 부피 [m3]') = prod(d, 희미한_cont(d)/100);
rep('가장 작은 상자의 부피 [m3]') = smin(b_exist, Box_vol(b_exist));
rep('가장 큰 상자의 부피 [m3]') = smax(b_exist, Box_vol(b_exist));
rep('모든 상자의 총 부피 [m3]') = sum(b_exist, Box_vol(b_exist));
rep('컨테이너에 있는 상자의 총 부피 [m3]') = sum(b_exist, Box_vol(b_exist)*OMEGA.L(b_exist));
rep('컨테이너에 있는 상자의 비율') = (rep('컨테이너에 있는 상자의 수') / rep('총 상자의 수'));
rep('컨테이너에 들어 있는 상자의 총 부피 비율') = (rep('컨테이너에 들어 있는 상자의 총 부피 [m3]') / rep('모든 상자의 총 부피 [m3]'));
rep('사용된 컨테이너 부피 비율') = (rep('컨테이너에 들어 있는 상자의 총 부피 [m3]') / rep('컨테이너의 부피 [m3]'));
옵션 담당자:2:0:1; 디스플레이 담당자;