メインコンテンツ

名前付きインデックス変数による最適化の初期点の作成

R2026b

この例では、名前付きインデックス変数がある最適化問題の初期点の作成方法を説明します。名前付きインデックス変数の場合、通常、初期点を指定する最も簡単な方法は、関数 findindex を使用することです。

問題は、原料となる油と精製された油のブレンドを含む、複数期間の在庫問題です。目的は、生産能力と在庫能力、およびオイル ブレンドの "硬度" への制約下において、収益を最大化することです。この問題は、Williams [1] からの引用です。

問題の説明

問題には、メーカーの精製食用油の原料となる植物油 2 種類と非植物油 3 種類が含まれます。メーカーは、毎月、最大 200 トンの植物油と最大 250 トンの非植物油を精製できます。メーカーは、各原料油を 1000 トンずつ保管できます。これは、原料油の購入コストがその月と油の種類によって異なるため、有益です。"硬度" と呼ばれる品質が、各油に関連付けられています。ブレンドされた油の硬度は、成分となる油の硬度を線形に重み付けしたものになります。

処理の限界のため、メーカーは 1 か月に精製する油を 3 種類以下に制限しています。また、1 種類の油を精製した月は、その油を少なくとも 20 トンは精製しなければなりません。最後に、1 種類の植物油を精製した月は、3 番目の非植物油も精製しなければなりません。

収益は、販売された油の各トンに対する定数です。コストは油の購入コストであり、油と月に応じて異なります。また、月ごとに各油を保管するためのトンあたりの固定コストがあります。油の精製に関するコストはありませんが、メーカーは精製後の油を保管できません (販売しなければなりません)。

問題のデータの入力

計画期間と油の名前付きインデックス変数を作成します。

months = ["January","February","March","April","May","June"];
oils = ["veg1","veg2","non1","non2","non3"];
vegoils = ["veg1","veg2"];
nonveg = ["non1","non2","non3"];

ストレージと使用量のパラメーターで変数を作成します。

maxstore = 1000; % Maximum storage of each type of oil
maxuseveg = 200; % Maximum vegetable oil use
maxusenon = 250; % Maximum nonvegetable oil use
minuseraw = 20; % Minimum raw oil use
maxnraw = 3; % Maximum number of raw oils in a blend
saleprice = 150; % Sale price of refined and blended oil
storecost = 5; % Storage cost per period per oil quantity
stockend = 500; % Stock at beginning and end of problem
hmin = 3; % Minimum hardness of refined oil
hmax = 6; % Maximum hardness of refined oil

原料油の硬度を次のベクトルとして指定します。

h = [8.8,6.1,2,4.2,5.0];

原料油のコストを次の配列として指定します。各配列の各行は、月あたりの原料油のコストを表します。最初の行は 1 月のコストを表し、最終行は 6 月のコストを表しています。

costdata = [...
110 120 130 110 115
130 130 110  90 115
110 140 130 100  95
120 110 120 120 125
100 120 150 110 105
 90 100 140  80 135];

変数の作成

これらの問題の変数を作成します。

  • sell、各月に販売された各油の数量

  • store、各月の末に保管されていた各油の数量

  • buy、各月に購入された各油の数量

さらに、各月に精製および販売される油の種類の数と、最小生産量に対する制約を説明するため、1 種類の油が 1 か月に販売される場合に必ず 1 となる補助 2 値変数 induse を作成します。

sell = optimvar("sell", months, oils, LowerBound=0);
buy = optimvar("buy", months, oils, LowerBound=0);
store = optimvar("store", months, oils, LowerBound=0, UpperBound=maxstore);

induse = optimvar("induse", months, oils, Type="integer", ...
    LowerBound=0, UpperBound=1);

各月に販売された油の合計量を、produce と名付けます。

produce = sum(sell,2);

目的関数の作成

問題の目的関数を作成するには、収益を計算し、油の購入と保管のコストを減算します。

最大化を目的とする最適化問題を作成し、目的関数を Objective プロパティとして含めます。

prob = optimproblem(ObjectiveSense="maximize");
prob.Objective = sum(saleprice*produce) - sum(sum(costdata.*buy)) - sum(sum(storecost*store));

目的関数の式はかなり長くなります。必要に応じて、showexpr(prob.Objective) コマンドを使用して、それを確認することができます。

制約の作成

問題には、設定が必要な制約がいくつかあります。

6 月に保管された各油の量は 500 です。下限と上限を使用して、この制約を設定します。

store("June", :).LowerBound = 500;
store("June", :).UpperBound = 500;

メーカーは、どの月であっても、maxuseveg を超える植物油は精製できません。これ以降のすべての制約を、制約の式と方程式を使用して設定します。

vegoiluse = sell(:, vegoils);
vegused = sum(vegoiluse, 2) <= maxuseveg;

メーカーは、どの月であっても、maxusenon を超える非植物油は精製できません。

nonvegoiluse = sell(:,nonveg);
nonvegused = sum(nonvegoiluse,2) <= maxusenon;

ブレンドされた油の硬度は、hmin through hmax から取得しなければなりません。

hardmin = sum(repmat(h, 6, 1).*sell, 2) >= hmin*produce;
hardmax = sum(repmat(h, 6, 1).*sell, 2) <= hmax*produce;

月末に保管されていた各油の量は、月初の量に購入した油の量を加え、販売した量を引いた量に等しくなります。

initstockbal = 500 + buy(1, :) == sell(1, :) + store(1, :);
stockbal = store(1:5, :) + buy(2:6, :) == sell(2:6, :) + store(2:6, :);

1 か月の間にいずれかの油を精製する場合は、その油を少なくとも minuseraw 精製し、販売しなければなりません。

minuse = sell >= minuseraw*induse;

対応する油が精製される場合は、変数 induse が必ず 1 になるようにします。

maxusev = sell(:, vegoils) <= maxuseveg*induse(:, vegoils);
maxusenv = sell(:, nonveg) <= maxusenon*induse(:, nonveg);

メーカーが各月に販売できる油の量は、maxnraw 以下です。

maxnuse = sum(induse, 2) <= maxnraw;

1 種類の植物油を精製する場合は、油 non3 も精製および販売しなければなりません。

deflogic1 = sum(induse(:,vegoils), 2) <= induse(:,"non3")*numel(vegoils);

問題に制約式を含めます。

prob.Constraints.vegused = vegused;
prob.Constraints.nonvegused = nonvegused;
prob.Constraints.hardmin = hardmin;
prob.Constraints.hardmax = hardmax;
prob.Constraints.initstockbal = initstockbal;
prob.Constraints.stockbal = stockbal;
prob.Constraints.minuse = minuse;
prob.Constraints.maxusev = maxusev;
prob.Constraints.maxusenv = maxusenv;
prob.Constraints.maxnuse = maxnuse;
prob.Constraints.deflogic1 = deflogic1;

実行可能な初期点の作成と使用

正しい次元の初期点を作成します。

x0.buy = zeros(size(buy));
x0.induse = zeros(size(induse));
x0.store = zeros(size(store));
x0.sell = zeros(size(sell));

植物油 veg2 と非植物油 non3 のみを販売するための初期点を設定します。この初期点を適切に設定するには、関数 findindex を使用します。

numMonths = size(induse,1);
[idxMonths,idxOils] = findindex(induse,1:numMonths,["veg2","non3"]);
x0.induse(idxMonths,idxOils) = 1;

植物油と非植物油の最大の制約をそれぞれ満たします。

x0.sell(:,idxOils) = repmat([200,250],numMonths,1)
x0 = struct with fields:
       buy: [6×5 double]
    induse: [6×5 double]
     store: [6×5 double]
      sell: [6×5 double]

最初の月に油を購入しないように初期点を設定します。

x0.buy(1,:) = 0;

油の種類ごとの 500 の初期保管量、最初の月の購入なし、および veg2 と non3 の一定の使用量に基づいて、最初の月の制約 initstockbal を満たします。

x0.store(1,:) = [500 300 500 500 250];

関数 findindex を使用して、残りの在庫残量の制約 stockbal を満たします。

[idxMonths,idxOils] = findindex(store,2:6,"veg2");
x0.store(idxMonths,idxOils) = [100;0;0;0;500];

[idxMonths,idxOils] = findindex(store,2:6,["veg1","non1","non2"]);
x0.store(idxMonths,idxOils) =  500;

[idxMonths,idxOils] = findindex(store,2:6,"non3");
x0.store(idxMonths,idxOils) = [0;0;0;0;500];

[idxMonths,idxOils] = findindex(buy,2:6,"veg2");
x0.buy(idxMonths,idxOils) = [0;100;200;200;700];

[idxMonths,idxOils] = findindex(buy,2:6,"non3");
x0.buy(idxMonths,idxOils) = [0;250;250;250;750];

初期点が実行可能であることを確認します。

issatisfied(prob,x0)
ans = logical
   1

すべての問題の制約が x0 で満たされていて、初期点が実行可能であることを示しています。

初期点を使用して問題を解きます。

[sol,fval,exitstatus,output] = solve(prob,x0)
Solving problem using intlinprog.
Running HiGHS 1.14.0: Copyright (c) 2026 under MIT licence terms
MIP has 126 rows; 120 cols; 373 nonzeros; 30 integer variables (30 binary)
Coefficient ranges:
  Matrix  [1e-01, 2e+02]
  Cost    [5e+00, 2e+02]
  Bound   [1e+00, 1e+03]
  RHS     [3e+00, 5e+02]
Assessing feasibility of MIP using primal feasibility and integrality tolerance of       1e-06
Solution has               num          max          sum
Col     infeasibilities      0            0            0
Integer infeasibilities      0            0            0
Row     infeasibilities      0            0            0
Row     residuals            0            0            0
Presolving model
126 rows, 115 cols, 368 nonzeros 0s
96 rows, 85 cols, 508 nonzeros 0s
96 rows, 85 cols, 508 nonzeros 0s
Presolve reductions: rows 96(-30); columns 85(-35); nonzeros 508(+135) 

MIP start solution is feasible, objective value is 39250

Solving MIP model with:
   96 rows
   85 cols (30 binary, 0 integer, 0 implied int., 55 continuous, 0 domain fixed)
   508 nonzeros

Src: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic;
     I => Shifting; J => Feasibility jump; L => Sub-MIP; P => Empty MIP; R => Randomized rounding;
     S => Solve LP; T => Evaluate node; U => Unbounded; X => User solution; Y => HiGHS solution;
     Z => ZI Round; l => Trivial lower; p => Trivial point; u => Trivial upper; z => Trivial zero

        Nodes      |    B&B Tree     |            Objective Bounds              |  Dynamic Constraints |       Work      
Src  Proc. InQueue |  Leaves   Expl. | BestBound       BestSol              Gap |   Cuts   InLp Confl. | LpIters     Time

         0       0         0   0.00%   1074750         39250           2638.22%        0      0      0         0     0.0s
 R       0       0         0   0.00%   107512.962963   57078.571429      88.36%        0      0      0        80     0.0s
 C       0       0         0   0.00%   105387.340109   94225             11.85%      124     25      0       145     0.0s
 L       0       0         0   0.00%   104512.040457   100278.703704      4.22%      219     38      0       196     0.2s

10.0% inactive integer columns, restarting
Model after restart has 90 rows, 82 cols (27 bin., 0 int., 0 impl., 55 cont., 0 dom.fix.), and 481 nonzeros

         0       0         0   0.00%   104305.737187   100278.703704      4.02%       18      0      0      1808     0.3s
         0       0         0   0.00%   104305.737187   100278.703704      4.02%       18     18      0      1856     0.3s
 L       0       0         0   0.00%   103031.310519   100278.703704      2.74%      141     24      0      5716     1.2s
       312       0       154 100.00%   100286.797463   100278.703704      0.01%      412     28    376     12005     1.6s

Solving report
  Status            Optimal
  Primal bound      100278.703704
  Dual bound        100286.797463
  Gap               0.00807% (tolerance: 0.01%)
  P-D integral      0.1361481361
  Solution status   feasible
                    100278.703704 (objective)
                    0 (bound viol.)
                    2.4499513529e-13 (int. viol.)
                    0 (row viol.)
  Timing            1.58
  Max sub-MIP depth 4
  Nodes             312
  Repair LPs        0
  LP iterations     12005
                    1351 (strong br.)
                    448 (separation)
                    7249 (heuristics)

Optimal solution found.

Intlinprog stopped because the objective value is within a gap tolerance of the optimal value, options.RelativeGapTolerance = 0.0001. The intcon variables are integer within tolerance, options.ConstraintTolerance = 1e-06.
sol = struct with fields:
       buy: [6×5 double]
    induse: [6×5 double]
      sell: [6×5 double]
     store: [6×5 double]

fval = 
1.0028e+05
exitstatus = 
    OptimalSolution

output = struct with fields:
        relativegap: 8.0713e-05
        absolutegap: 8.0938
      numfeaspoints: 5
           numnodes: 312
    constrviolation: 7.9581e-11
            message: 'Optimal solution found.↵↵Intlinprog stopped because the objective value is within a gap tolerance of the optimal value, options.RelativeGapTolerance = 0.0001. The intcon variables are integer within tolerance, options.ConstraintTolerance = 1e-06.'
             solver: 'intlinprog'

参考文献

[1] Williams, H. Paul.Model Building in Mathematical Programming.Fourth edition.J. Wiley, Chichester, England.Problem 12.1, "Food Manufacture1."1999.

参考

|

トピック