Keyword: 水産養殖業, グローバル最適化, 養殖エリア配置, 生産効率, PSO, NLP
水産養殖業分野の最適化問題:養殖エリアの最適配置
問題の概要
水産養殖業において、限られた海域や湖沼の中でいかに効率的に養殖エリアを配置するかは重要な課題です。 ここでは、与えられた条件下で養殖エリアの総面積を最大化する配置を求めます。
養殖エリアの最適配置
ある湖沼で、トラフグの養殖を行うための最適なエリア配置を決定したいと考えています。湖沼は長方形で、縦60m、横40mの大きさです。全ての養殖エリアは円形で、共通の半径rの大きさとします。養殖エリアの中心座標を(x, y)とし、これらを最適化の決定変数とします。
目的は、養殖エリアの総面積を最大化することです。ただし、以下の制約条件を満たす必要があります。
- 養殖エリアは湖沼の領域内に収まること
- 養殖エリア同士が重ならないこと
- 養殖エリアの半径rは3m以上25m以下であること
以下のパラメータを使用します。
- 湖沼のサイズ:縦60m、横40m
- 養殖エリアの数:10個
- 養殖エリアの半径r:3m以上15m以下の連続値
問題の定式化
パラメータ
パラメータ | 説明 | 値 |
---|---|---|
湖沼の横幅 [m] | 40 | |
湖沼の縦幅 [m] | 60 | |
養殖エリアの数 | 10 | |
養殖エリアの最小半径 [m] | 3 | |
養殖エリアの最大半径 [m] | 15 |
決定変数
変数 | 説明 | 範囲 |
---|---|---|
養殖エリアの半径 [m] |
目的関数
目的 | 式 |
---|---|
養殖エリアの総面積最大化 |
養殖エリアの総面積を最大化することで、限られた湖沼の面積を最大限に活用し、トラフグの生産量を向上させることを目指します。
制約条件
制約 | 式 | 説明 |
---|---|---|
湖沼内制約 | 養殖エリアが湖沼の横方向に収まる | |
養殖エリアが湖沼の縦方向に収まる | ||
重複制約 | 養殖エリア同士が重ならない | |
半径制約 | 養殖エリアの半径が適切な範囲内にある |
これらの制約条件により、養殖エリアが湖沼内に適切に配置され、エリア同士の干渉が避けられます。また、半径制約によって、養殖エリアの大きさが現実的な範囲内に保たれます。
コード例
以下にこの問題を 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):
# モニタリング関数:最適化の過程で各反復の情報を表示
= xb[-1]
r 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: 養殖エリアの半径
= x[-1]
r if mode in [0, 5, 2, 7]:
# 目的関数値の計算
= n * np.pi * r**2
objf if mode in [1, 2, 6, 7]:
# 目的関数の勾配(導関数)の計算
-1] = 2 * n * np.pi * r
vecout[-1] = 0
vecout[:return objf, vecout
def confun(mode, needc, x, cjac, nstate, data=None):
# 制約条件:養殖エリアが湖の境界内に収まること、および養殖エリア同士が重ならないこと
# x: [x1, y1, x2, y2, ..., xn, yn, r] の順序で変数が並んでいる
# xi, yi: 養殖エリアiの中心座標
# r: 養殖エリアの半径
= x[-1]
r = []
c if mode in [0, 2]:
# 制約条件の計算
for i in range(n):
= x[2*i], x[2*i+1]
xi, yi - r)
c.append(xi - (xi + r))
c.append(lake_width - r)
c.append(yi - (yi + r))
c.append(lake_height for i in range(n):
for j in range(i + 1, n):
= x[2*i], x[2*i+1], x[2*j], x[2*j+1]
xi, yi, xj, yj - xj)**2 + (yi - yj)**2 - 4*r**2)
c.append((xi if mode in [1, 2]:
# 制約条件のヤコビアン(導関数)の計算
0)
cjac.fill(= 0
idx for i in range(n):
= 2*i, 2*i + 1
xi_idx, yi_idx = 1
cjac[idx, xi_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)
cjac[idx+= 4
idx = 4 * n
idx for i in range(n):
for j in range(i + 1, n):
= x[2*i], x[2*i+1], x[2*j], x[2*j+1]
xi, yi, xj, yj 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
cjac[idx, += 1
idx return c, cjac
# 問題の定義
= 10 # 養殖エリアの数
n = 40 # 湖沼の幅
lake_width = 60 # 湖沼の高さ
lake_height = 3 # 養殖エリアの半径下限値
r_min = 15 # 養殖エリアの半径上限値
r_max
# 問題に依存する各種パラメータ
= 2 * n + 1 # 変数の数(各養殖エリアのx,y座標 + 半径)
nvar = 4 * n + n * (n - 1) // 2 # 制約式の数
ncon = nvar * 10 # パーティクルの数
npar
# 変数の下限と上限
# [x1, y1, x2, y2, ..., xn, yn, r] の順序で変数が並んでいる
= []
bl_var = []
bu_var for i in range(n):
0,0])
bl_var.extend([
bu_var.extend([lake_width, lake_height])
bl_var.append(r_min)
bu_var.append(r_max)
# 湖の境界に関する制約条件の下限と上限
= [0] * (4 * n)
bl_con_lake = [np.inf] * (4 * n)
bu_con_lake
# 養殖エリアの重なりに関する制約条件の下限と上限
= [0] * (n * (n - 1) // 2)
bl_con_overlap = [np.inf] * (n * (n - 1) // 2)
bu_con_overlap
= bl_var + bl_con_lake + bl_con_overlap
bl = bu_var + bu_con_lake + bu_con_overlap
bu
= np.random.rand(nvar, npar) * (np.array(bu_var) - np.array(bl_var))[:, np.newaxis] + np.array(bl_var)[:, np.newaxis]
xbest = np.zeros(npar)
fbest = np.zeros((ncon, npar))
cbest
= {}
comm 'Initialize = nlp_pso', comm)
glopt.optset('Optimize = Maximize', comm)
glopt.optset(#glopt.optset('Maximum Iterations Completed = 10000', comm)
# PSO アルゴリズムを使用して最適化問題を解く
= glopt.nlp_pso(
xb, fb, cb, xbest, fbest, cbest, itt, inform =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]}")
= xb[-1]
r print(f"総面積: {fb:.5f} 平方メートル")
print(f"養殖エリア半径: {r:.5f} メートル")
for i in range(10):
= xb[2*i], xb[2*i+1]
xi, yi print(f"養殖エリア {i+1:2}: (x={xi:.5f}, y={yi:.5f})")
elif inform in [2, 3, 4, 5, 6]:
print("設定された終了条件に達したため、最適化が終了しました。得られた解は現時点での最良解です。")
print(f"反復回数: {itt[0]}")
= xb[-1]
r print(f"総面積: {fb:.5f} 平方メートル")
print(f"養殖エリア半径: {r:.5f} メートル")
for i in range(10):
= xb[2*i], xb[2*i+1]
xi, yi print(f"養殖エリア {i+1:2}: (x={xi:.5f}, y={yi:.5f})")
elif inform == 7:
print("実行可能解が得られましたが、目的関数は最小化されていません。得られた解は以下の通りです。")
print(f"反復回数: {itt[0]}")
= xb[-1]
r print(f"総面積: {fb:.5f} 平方メートル")
print(f"養殖エリア半径: {r:.5f} メートル")
for i in range(10):
= xb[2*i], xb[2*i+1]
xi, yi 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)を用いて養殖エリアの最適配置を求めることで、限られた水域の有効活用と生産性の向上を目指しました。このアプローチは、 モデルに新たな制約を追加することで、例えば、水質や水温などの環境条件を考慮した最適化や、複数の魚種を同時に養殖する場合の最適配置問題への応用も可能です。