業界別最適化Example集: 養殖エリアの最適配置問題 (水産養殖業)
nAG数値計算ライブラリ > 業界別最適化Example集 > 養殖エリアの最適配置問題 (水産養殖業)

Keyword: 水産養殖業, グローバル最適化, 養殖エリア配置, 生産効率, PSO, NLP

水産養殖業分野の最適化問題:養殖エリアの最適配置

問題の概要

養殖エリア

水産養殖業において、限られた海域や湖沼の中でいかに効率的に養殖エリアを配置するかは重要な課題です。 ここでは、与えられた条件下で養殖エリアの総面積を最大化する配置を求めます。

養殖エリアの最適配置

ある湖沼で、トラフグの養殖を行うための最適なエリア配置を決定したいと考えています。湖沼は長方形で、縦60m、横40mの大きさです。全ての養殖エリアは円形で、共通の半径rの大きさとします。養殖エリアの中心座標を(x, y)とし、これらを最適化の決定変数とします。

目的は、養殖エリアの総面積を最大化することです。ただし、以下の制約条件を満たす必要があります。

  1. 養殖エリアは湖沼の領域内に収まること
  2. 養殖エリア同士が重ならないこと
  3. 養殖エリアの半径rは3m以上25m以下であること

以下のパラメータを使用します。

  • 湖沼のサイズ:縦60m、横40m
  • 養殖エリアの数:10個
  • 養殖エリアの半径r:3m以上15m以下の連続値
養殖エリアの総面積を最大化したい

問題の定式化

パラメータ

パラメータ 説明
W 湖沼の横幅 [m] 40
L 湖沼の縦幅 [m] 60
N 養殖エリアの数 10
rmin 養殖エリアの最小半径 [m] 3
rmax 養殖エリアの最大半径 [m] 15

決定変数

変数 説明 範囲
xi i番目の養殖エリアの中心x座標 [m] [r,Wr]
yi i番目の養殖エリアの中心y座標 [m] [r,Lr]
r 養殖エリアの半径 [m] [rmin,rmax]

目的関数

目的
養殖エリアの総面積最大化 maxNπr2

養殖エリアの総面積を最大化することで、限られた湖沼の面積を最大限に活用し、トラフグの生産量を向上させることを目指します。

制約条件

制約 説明
湖沼内制約 rxiWr, i 養殖エリアが湖沼の横方向に収まる
ryiLr, i 養殖エリアが湖沼の縦方向に収まる
重複制約 (xixj)2+(yiyj)24r2, ij 養殖エリア同士が重ならない
半径制約 rminrrmax 養殖エリアの半径が適切な範囲内にある

これらの制約条件により、養殖エリアが湖沼内に適切に配置され、エリア同士の干渉が避けられます。また、半径制約によって、養殖エリアの大きさが現実的な範囲内に保たれます。

コード例

以下にこの問題を nAG Library for Python の大域的最適化関数の一つで粒子群最適化手法を実装した nlp_pso を用いて解くコード例を示します。

from naginterfaces.library import glopt
import numpy as np

def monmod(x, xb, fb, cb, xbest, fbest, cbest, itt, data):
    # モニタリング関数:最適化の過程で各反復の情報を表示
    r = xb[-1]
    print(f"反復:{itt[0]} 現時点でのベスト 総面積:{fb:.5f}平方メートル 半径:{r:.4f}メートル")
    print(xb)
    return x

def objfun(mode, x, objf, vecout, nstate, data=None):
    # 目的関数:養殖エリアの総面積を最大化
    # x: [x1, y1, x2, y2, ..., xn, yn, r] の順序で変数が並んでいる
    # xi, yi: 養殖エリアiの中心座標
    # r: 養殖エリアの半径
    r = x[-1]
    if mode in [0, 5, 2, 7]:
        # 目的関数値の計算
        objf = n * np.pi * r**2
    if mode in [1, 2, 6, 7]:
        # 目的関数の勾配(導関数)の計算
        vecout[-1] = 2 * n * np.pi * r
        vecout[:-1] = 0
    return objf, vecout

def confun(mode, needc, x, cjac, nstate, data=None):
    # 制約条件:養殖エリアが湖の境界内に収まること、および養殖エリア同士が重ならないこと
    # x: [x1, y1, x2, y2, ..., xn, yn, r] の順序で変数が並んでいる
    # xi, yi: 養殖エリアiの中心座標
    # r: 養殖エリアの半径
    r = x[-1]
    c = []
    if mode in [0, 2]:
        # 制約条件の計算
        for i in range(n):
            xi, yi = x[2*i], x[2*i+1]
            c.append(xi - r)
            c.append(lake_width - (xi + r))
            c.append(yi - r)
            c.append(lake_height - (yi + r))
        for i in range(n):
            for j in range(i + 1, n):
                xi, yi, xj, yj = x[2*i], x[2*i+1], x[2*j], x[2*j+1]
                c.append((xi - xj)**2 + (yi - yj)**2 - 4*r**2)
    if mode in [1, 2]:
        # 制約条件のヤコビアン(導関数)の計算
        cjac.fill(0)
        idx = 0
        for i in range(n):
            xi_idx, yi_idx = 2*i, 2*i + 1
            cjac[idx, xi_idx] = 1
            cjac[idx+1, xi_idx] = -1
            cjac[idx+2, yi_idx] = 1
            cjac[idx+3, yi_idx] = -1
            cjac[idx, -1] = -1   # r の導関数 for xi - r
            cjac[idx+1, -1] = -1 # r の導関数 for lake_width - (xi + r)
            cjac[idx+2, -1] = -1 # r の導関数 for yi - r
            cjac[idx+3, -1] = -1 # r の導関数 for lake_height - (yi + r)
            idx += 4
        idx = 4 * n
        for i in range(n):
            for j in range(i + 1, n):
                xi, yi, xj, yj = x[2*i], x[2*i+1], x[2*j], x[2*j+1]
                cjac[idx, 2*i] = 2 * (xi - xj)
                cjac[idx, 2*j] = -2 * (xi - xj)
                cjac[idx, 2*i+1] = 2 * (yi - yj)
                cjac[idx, 2*j+1] = -2 * (yi - yj)
                cjac[idx, -1] = -8 * r # r の導関数 for the overlap constraint
                idx += 1
    return c, cjac

# 問題の定義
n = 10            # 養殖エリアの数
lake_width = 40   # 湖沼の幅
lake_height = 60  # 湖沼の高さ
r_min = 3         # 養殖エリアの半径下限値 
r_max = 15        # 養殖エリアの半径上限値 

# 問題に依存する各種パラメータ
nvar = 2 * n + 1  # 変数の数(各養殖エリアのx,y座標 + 半径)
ncon = 4 * n + n * (n - 1) // 2  # 制約式の数
npar = nvar * 10  # パーティクルの数

# 変数の下限と上限
# [x1, y1, x2, y2, ..., xn, yn, r] の順序で変数が並んでいる
bl_var = []
bu_var = []
for i in range(n):
    bl_var.extend([0,0])
    bu_var.extend([lake_width, lake_height])
bl_var.append(r_min)
bu_var.append(r_max)

# 湖の境界に関する制約条件の下限と上限
bl_con_lake = [0] * (4 * n)
bu_con_lake = [np.inf] * (4 * n)

# 養殖エリアの重なりに関する制約条件の下限と上限
bl_con_overlap = [0] * (n * (n - 1) // 2)
bu_con_overlap = [np.inf] * (n * (n - 1) // 2)

bl = bl_var + bl_con_lake + bl_con_overlap
bu = bu_var + bu_con_lake + bu_con_overlap

xbest = np.random.rand(nvar, npar) * (np.array(bu_var) - np.array(bl_var))[:, np.newaxis] + np.array(bl_var)[:, np.newaxis]
fbest = np.zeros(npar)
cbest = np.zeros((ncon, npar))

comm = {}
glopt.optset('Initialize = nlp_pso', comm)
glopt.optset('Optimize = Maximize', comm)
#glopt.optset('Maximum Iterations Completed = 10000', comm)

# PSO アルゴリズムを使用して最適化問題を解く
xb, fb, cb, xbest, fbest, cbest, itt, inform = glopt.nlp_pso(
    bl=bl,
    bu=bu,
    xbest=xbest,
    fbest=fbest,
    cbest=cbest,
    objfun=objfun,
    monmod=monmod,
    comm=comm,
    confun=confun,
    data={}
)

# 最適化結果を表示
if inform == 0:
    print("最適化中にエラーが発生したため、最適解は得られませんでした。")
elif inform == 1:
    print("設定された目的関数値に到達したため、最適化が終了しました。得られた解は以下の通りです。")
    print(f"反復回数: {itt[0]}")
    r = xb[-1]
    print(f"総面積: {fb:.5f} 平方メートル")
    print(f"養殖エリア半径: {r:.5f} メートル")
    for i in range(10):
        xi, yi = xb[2*i], xb[2*i+1]
        print(f"養殖エリア {i+1:2}: (x={xi:.5f}, y={yi:.5f})")
elif inform in [2, 3, 4, 5, 6]:
    print("設定された終了条件に達したため、最適化が終了しました。得られた解は現時点での最良解です。")
    print(f"反復回数: {itt[0]}")
    r = xb[-1]
    print(f"総面積: {fb:.5f} 平方メートル")
    print(f"養殖エリア半径: {r:.5f} メートル")
    for i in range(10):
        xi, yi = xb[2*i], xb[2*i+1]
        print(f"養殖エリア {i+1:2}: (x={xi:.5f}, y={yi:.5f})")
elif inform == 7:
    print("実行可能解が得られましたが、目的関数は最小化されていません。得られた解は以下の通りです。")
    print(f"反復回数: {itt[0]}")
    r = xb[-1]
    print(f"総面積: {fb:.5f} 平方メートル")
    print(f"養殖エリア半径: {r:.5f} メートル")
    for i in range(10):
        xi, yi = xb[2*i], xb[2*i+1]
        print(f"養殖エリア {i+1:2}: (x={xi:.5f}, y={yi:.5f})")

結果例

上記のコードを実行すると、以下のような出力が得られます。

反復:1 現時点でのベスト 総面積:317.54303平方メートル 半径:3.1793メートル
[35.82072586 21.25289008 10.96945324 24.73892217 26.22055069 28.25181082
  9.50623719 31.19426512 16.68278192 39.78602548 21.99215661 18.73787713
 24.49508862  6.07966096 15.29256908  9.6750819  13.23102104 40.26371998
 17.89846665 19.06061597  3.17926226]
反復:2 現時点でのベスト 総面積:293.47708平方メートル 半径:3.0564メートル
[12.48454509 44.41348853 18.11999719  8.17296683 31.50503533 19.17347256
 15.93922587 21.20620724 24.49384787 27.34377488 10.21140125 40.36815009
 26.030046   14.1478701  11.67745335 30.71448767 18.60730751 41.03511396
 34.53771533 26.99214522  3.05641384]
...
...  反復を追うごとに徐々に最適値がアップデートされていきます。
...
反復:21000 現時点でのベスト 総面積:1550.36559平方メートル 半径:7.0249メートル
[ 7.06035755 37.38766053  7.03764444 11.78897859 12.8956621  24.57417273
 19.7549301  43.47263084  8.96240974 52.85016103 26.97320736 25.8433899
 32.95400764 13.05517655 20.25837072  7.02632885 32.97074161 38.58286255
 31.49711561 52.69893777  7.024932  ]
aquaculture-optimal-allocation.py:115: NagAlgorithmicWarning: (nAG Python function naginterfaces.base.glopt.nlp_pso, code 1:1,10101)
** A finalization criterion was reached that cannot guarantee success.
** On exit, inform = 5.
  xb, fb, cb, xbest, fbest, cbest, itt, inform = glopt.nlp_pso(
設定された終了条件に達したため、最適化が終了しました。得られた解は現時点での最良解です。
反復回数: 21001
総面積: 1550.36559 平方メートル
養殖エリア半径: 7.02493 メートル
養殖エリア  1: (x=7.06036, y=37.38766)
養殖エリア  2: (x=7.03764, y=11.78898)
養殖エリア  3: (x=12.89566, y=24.57417)
養殖エリア  4: (x=19.75493, y=43.47263)
養殖エリア  5: (x=8.96241, y=52.85016)
養殖エリア  6: (x=26.97321, y=25.84339)
養殖エリア  7: (x=32.95401, y=13.05518)
養殖エリア  8: (x=20.25837, y=7.02633)
養殖エリア  9: (x=32.97074, y=38.58286)
養殖エリア 10: (x=31.49712, y=52.69894)
最適化結果のプロット図

得られた結果から、各養殖エリアの中心座標と半径が決定されました。この配置により、湖沼内で養殖エリアが重複することなく、総面積が効率的に使用されています。尚、今回の最適化プロセスでは、粒子群最適化(PSO)の手法を用いており、得られた解が数学的な最適解であるという保証はありません。

まとめ

ここでは、粒子群最適化(PSO)を用いて養殖エリアの最適配置を求めることで、限られた水域の有効活用と生産性の向上を目指しました。このアプローチは、 モデルに新たな制約を追加することで、例えば、水質や水温などの環境条件を考慮した最適化や、複数の魚種を同時に養殖する場合の最適配置問題への応用も可能です。


関連情報
© 譌・譛ャ繝九Η繝シ繝。繝ェ繧ォ繝ォ繧「繝ォ繧エ繝ェ繧コ繝�繧コ繧ー繝ォ繝シ繝玲�ェ蠑丈シ夂、セ 2024
Privacy Policy  /  Trademarks