Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

coneprog アルゴリズムの速度の比較

この例では、さまざまな問題のサイズ、および LinearSolver オプションのすべてのアルゴリズムについて、coneprog の求解時間を示します。この問題は、点から楕円までの距離を明らかにするというものです (ただし点は n 次元で表現され、楕円は制約行列が m 行の錐制約で表現される)。i が 1 ~ 10 の場合、(n,m) = i*(100,20) を選択します。この例の最後に記載されている補助関数 define_problem は、指定された mn、そして乱数発生器のシードで問題を生成します。この関数は、行列の各行に 1 が 10 個、各列に 1 が 2 個以上ある疑似ランダム錐を生成し、行列の開始列がいくつもの 1 を含む (1 が密集した) 列になるようにします。

問題のデータの準備

問題を生成する関数のパラメーターを設定します。

n = 100;
m = 20;
seed = 0;

10 通りのサイズの問題に対して実行する実験を設定します。

numExper = 10;

LinearSolver オプションの値をすべて列挙します。

linearSolvers = {'auto','augmented','normal','schur','prodchol'};

このデータの場合、'auto' 設定により、coneprog'prodchol' 線形ソルバーを使用することになるため、これら 2 つの値からは実質的に同じ結果が取得されます。

結果として取得できたタイミング データと各実験の反復回数を保持するための構造体を作成します。

time = struct();
s = " ";
time.probsize = repmat(s,numExper,1);
% Initialize time struct to zeros.
for solver_i = linearSolvers
    time.(solver_i{1}) = zeros(numExper, 1);
end

iter = struct();
iter.probsize = repmat(s,numExper,1);
for solver_i = linearSolvers
    iter.(solver_i{1}) = zeros(numExper, 1);
end

ソルバーのウォームアップ

有意義なタイミングの比較を行うには、結果が出るまでにかかる時間の計測を行わない状態で solve (これにより coneprog が呼び出される) を数回実行します。この "ウォームアップ" により、ソルバーは効率的にデータを使用できるようになり、内部の Just-In-Time コンパイラへのデータ入力が自動で実行されます。

[prob, x0] = define_problem(m, n, seed);
options = optimoptions('coneprog','Display','off');
for i = 1 : 4
    sol = solve(prob,x0,'options',options);
end

ソルバーの実行

すべての問題に対し、ソルバーを実行し、ソルバーにかかった求解時間と反復回数を記録します。

for i = 1:numExper
    % Generate problems of increasing size.
    [prob, x0] = define_problem(m*i, n*i, seed);
    time.probsize(i) = num2str(m*i)+"x"+num2str(n*i);
    iter.probsize(i) = num2str(m*i)+"x"+num2str(n*i);
    % Solve the generated problem for each algorithm and measure time.
    for solver_i = linearSolvers
        options.LinearSolver = solver_i{1};
        tic
        [~,~,~,output] = solve(prob,x0,'options',options);
        time.(solver_i{1})(i) = toc;
        iter.(solver_i{1})(i) = output.iterations;
    end
end

結果の表示

タイミング結果を表示します。probsize 列は問題のサイズを "m x n" で示します。ここで、m は錐制約の数、n は変数の数です。

timetable = struct2table(time)
timetable=10×6 table
     probsize       auto      augmented     normal      schur      prodchol
    __________    ________    _________    ________    ________    ________

    "20x100"      0.020335    0.042185     0.022258    0.018266    0.019167
    "40x200"      0.028701     0.21417     0.063392     0.01956    0.030663
    "60x300"      0.026849     0.38047      0.11627     0.02042    0.027778
    "80x400"      0.032513     0.65735      0.23975    0.023377    0.034159
    "100x500"     0.040358      1.2081      0.42095    0.026024    0.038788
    "120x600"     0.089219      2.8035      0.92355    0.033922      0.0909
    "140x700"     0.098881      7.4664       2.1049    0.046021     0.10043
    "160x800"      0.11053      8.7302        2.908    0.054712     0.11306
    "180x900"      0.11439      10.485       3.5668    0.056406     0.11708
    "200x1000"    0.099195      6.7833       3.6698    0.053792    0.097791

最短時間は autoschur、および prodchol 列で確認できます。auto および prodchol のアルゴリズムはすべての問題でまったく同じであることから、あらゆるタイミングの違いは変量効果によるものです。augmented 列では最長時間が確認でき、normal 列の時間は中間に相当します。

タイミング結果の違いは、各反復の速度の違いによるものでしょうか。それとも各ソルバーの反復回数によるものでしょうか。対応する反復回数の一覧を表示してみましょう。

itertable = struct2table(iter)
itertable=10×6 table
     probsize     auto    augmented    normal    schur    prodchol
    __________    ____    _________    ______    _____    ________

    "20x100"        8         8           8        8          8   
    "40x200"       11        11          11       11         11   
    "60x300"        8         8           8        8          8   
    "80x400"        8         8           8        8          8   
    "100x500"       8         8           8        8          8   
    "120x600"      19        11          11       11         19   
    "140x700"      17        18          17       15         17   
    "160x800"      16        16          16       16         16   
    "180x900"      14        14          14       13         14   
    "200x1000"     10        10          10       10         10   

この問題に関しては、反復回数と問題のサイズとの間に明確な相関は見られません。これは coneprog が使用する内点法アルゴリズムにみられる代表的な特徴です。いずれのアルゴリズムでも、各行の反復回数はほぼ同じです。この問題では、schurprodchol の反復は、ほかのアルゴリズムの反復より高速で実行されています。

補助関数

次のコードは補助関数 define_problem を作成します。

function [prob, x0] = define_problem(m,n,seed)
%%% Generate the following optimization problem
%%%
%%% min t
%%% s.t.
%%%     || Ax - b ||   <= gamma
%%%     || x - xbar || <= t
%%%
%%% which finds the closest point of a given ellipsoid (||Ax-b||<= gamma)
%%% to a given input point xbar.
%%%

rng(seed);

% The targeted total number of nonzeros in matrix A is
% 10 nonzeros in each row
% plus 2 nonzeros in each column
% plus a dense first column.
numNonZeros = 10*m + 2*n + m;
A = spalloc(m,n,numNonZeros);

% For each row generate 10 nonzeros.
for i = 1:m
    p = randperm(n,10);
    A(i,p) = 1;
end

% For each column generate 2 nonzeros.
for j = 2:n
    p = randperm(m,2);
    A(p,j) = 1;
end

% The first column is dense.
A(:,1) = 1;

b = ones(m, 1);
gamma = 10;

% Find a point outside of the ellipsoid.
xbar = randi([-10,10],n,1);
while norm(A*xbar - b) <= gamma
    xbar = xbar + randi([-10,10],n,1);
end

% Define the cone problem.
prob = optimproblem('Description', 'Minimize Distance to Ellipsoid');
x = optimvar('x',n);
t = optimvar('t');

prob.Objective = t;
prob.Constraints.soc1 = norm(x - xbar) <= t;
prob.Constraints.soc2 = norm(A*x - b) <= gamma;

x0.x = sparse(n,1);
x0.t = 0;

end

参考

関連するトピック