Keyword: 環境, 最適化, カーボンフットプリント, 温室効果ガス削減, NLP
環境・エコロジー分野の最適化問題:カーボンフットプリント削減
問題の概要
地球温暖化対策として、企業や自治体のカーボンフットプリント(CO2排出量)削減が急務となっています。しかし、経済活動を維持しつつ、効果的にCO2排出量を削減するには、最適な対策の組み合わせを見つける必要があります。
この問題では、限られた予算内で、複数の CO2 削減対策からその投資額を決定し、最大限のCO2削減効果を達成することを目指します。各対策には、投資額に応じた削減効果があり、それらの関係は非線形であるとします。
カーボンフットプリント削減の最適化
ある企業が、年間のCO2排出量を削減するために、以下の3つの対策を検討しています:
- 再生可能エネルギーの導入
- 省エネ設備への更新
- 森林保全プロジェクトへの投資
それぞれの対策には、投資額に応じた CO2 削減効果がありますが、その関係は非線形です。企業は、限られた予算内で、これらの対策への最適な投資配分を決定し、CO2削減量を最大化したいと考えています。
対策 | 投資額の範囲(百万円) | CO2削減効果(トン) |
---|---|---|
再生可能エネルギー | 0 - 50 | |
省エネ設備 | 0 - 30 | |
森林保全 | 0 - 20 |
この問題を解くことで、企業は限られた予算内で最大限のCO2削減を達成する投資配分を見つけることができます。
問題の定式化
パラメータ
パラメータ | 説明 | 値 |
---|---|---|
総予算(百万円) | 60 |
: 企業が CO2 削減対策に割り当てられる総予算を表す非負の連続値です。単位は百万円です。
決定変数
変数 | 説明 | 範囲 |
---|---|---|
再生可能エネルギーへの投資額(百万円) | ||
省エネ設備への投資額(百万円) | ||
森林保全への投資額(百万円) |
: それぞれの対策への投資額を表す非負の連続値です。単位は百万円です。
目的関数
目的 | 式 |
---|---|
CO2削減量の最大化 |
目的は、各対策による CO2
削減効果の合計を最大化することです。目的関数は、各対策への投資額
制約条件
制約 | 式 | 説明 |
---|---|---|
予算制約 | 総投資額が予算以内に収まる | |
投資額の非負制約 | 各対策への投資額は非負 | |
投資額の上限制約 | 各対策への投資額は上限以下 |
制約条件は、総投資額が予算以内に収まること、各対策への投資額が非負であること、そして各対策への投資額がそれぞれの上限以下であることを表しています。
コード例
以下に、この問題を nAG Library for Python の非線形最適化問題を解くための関数 nlp1_solve を用いて解くコード例を示します。
import numpy as np
from naginterfaces.library import opt
# パラメータ定義
= 60 # 総予算 (百万円), 予算制約のBです。
budget = {"renew": 50, "save": 30, "conserve": 20} # 各対策の投資上限 (百万円), 各対策への投資額の上限制約を表します。
limits = {"renew": 100, "save": 50, "conserve": 80} # 各対策のCO2削減係数, CO2削減効果の非線形関係を定義します。
coefs = 1e-8 # 微小値, 数値的安定性を保つために使用されます。
epsilon
def objective(mode, x, grad, _nstate):
""" 目的関数:CO2削減量最大化 """
if mode in [0, 2]:
# 目的関数の計算, 各投資額に対するCO2削減効果の合計値を計算します。
= -(
obj_val "renew"] * np.sqrt(x[0]) +
coefs["save"] * np.sqrt(x[1]) +
coefs["conserve"] * np.log(1 + x[2])
coefs[
)else:
= 0.0
obj_val if mode == 2:
# 勾配の計算, 最適化のための勾配ベクトルを計算します。
= [
grad[:] -coefs["renew"] / (2 * np.sqrt(x[0]) + epsilon),
-coefs["save"] / (2 * np.sqrt(x[1]) + epsilon),
-coefs["conserve"] / (1 + x[2] + epsilon)
]return obj_val, grad
# 投資範囲の設定
= [0, 0, 0] # 各対策の投資額の下限です。
lower_bounds_var = [limits["renew"], limits["save"], limits["conserve"]] # 上限制約です。
upper_bounds_var
# 線形制約
= np.full((1, 3), 1.0, dtype=np.float64)
a = [ -1.0e20 ]
lower_bounds_con = [ budget ]
upper_bounds_con
# nlp1_solveの仕様に合わせて変数の上下限と制約の上下限を結合
= np.concatenate((lower_bounds_var, lower_bounds_con))
lower_bounds = np.concatenate((upper_bounds_var, upper_bounds_con))
upper_bounds
# 各対策への投資額の初期値
= [15, 15, 15]
x_init
# オプション設定
= opt.nlp1_init('nlp1_solve')
comm 'Print Level = 0', comm)
opt.nlp1_option_string(
# 最適化実行
= opt.nlp1_solve(
result
a, lower_bounds, upper_bounds, objective, x_init, comm
)
# 結果表示
print('最適な投資額:')
print(f'再生可能エネルギーへの投資 (renew): {result.x[0]:.2f} 百万円')
print(f'省エネルギーへの投資 (save): {result.x[1]:.2f} 百万円')
print(f'森林保全への投資 (conserve): {result.x[2]:.2f} 百万円')
print(f'達成されたCO2削減量の最大値: {-result.objf:.2f} トン')
コードの補足説明
目的関数の設定
nlp1_solve
では、ユーザーが目的関数をコールバック関数(objective
)として定義します。この関数は、mode
に応じて目的関数の値
(obj_val
) とその勾配 (grad
)
を計算し、返すように記述します。mode=0
または2
の場合は目的関数値を、mode=2
の場合は勾配も計算します。
def objective(mode, x, grad, _nstate):
""" 目的関数:CO2削減量最大化 """
if mode in [0, 2]:
# 目的関数の計算, 各投資額に対するCO2削減効果の合計値を計算します。
obj_val = -(
coefs["renew"] * np.sqrt(x[0]) +
coefs["save"] * np.sqrt(x[1]) +
coefs["conserve"] * np.log(1 + x[2])
)
else:
obj_val = 0.0
if mode == 2:
# 勾配の計算, 最適化のための勾配ベクトルを計算します。
grad[:] = [
-coefs["renew"] / (2 * np.sqrt(x[0]) + epsilon),
-coefs["save"] / (2 * np.sqrt(x[1]) + epsilon),
-coefs["conserve"] / (1 + x[2] + epsilon)
]
return obj_val, grad
今回の目的は、各対策への投資額に応じたCO2削減効果の合計の最大化です。
つまり、目的関数は次のようになります:
最大化:100 * sqrt(x1) + 50 * sqrt(x2) + 80 * log(1 + x3)
nlp1_solve
関数は最小化問題を解くものであるため、最大化を行いたい場合は最小化問題に変換する必要があります。それを行うには目的関数の符号を反転させます。つまり、次のようにします:
最小化:-(100 * sqrt(x1) + 50 * sqrt(x2) + 80 * log(1 + x3))
符号を反転させて最小化することにより、結果的に元の目的関数を最大化することになります。
この関数では目的関数の勾配(偏微分)も計算させる必要があります。
今回の目的関数の勾配(偏微分)は以下のようになります。
d(100 * sqrt(x1)) / dx1
(100 * sqrt(x1)のx1に関する微分)=50 / sqrt(x1)
d(50 * sqrt(x2)) / dx2
(50 * sqrt(x2)のx2に関する微分)=25 / sqrt(x2)
d(80 * log(1 + x3)) / dx3
(80 * log(1 + x3)のx3に関する微分)=80 / (1 + x3)
勾配も符号の反転が必要なので、以下のように符合を反転させてgrad
に指定しています:
grad[0] = -100 / (2 * sqrt(x[0]) + epsilon)
grad[1] = -50 / (2 * sqrt(x[1]) + epsilon)
grad[2] = -80 / (1 + x[2] + epsilon)
(ここでepsilon を足しているのは、ゼロ除算や無限大になることを防ぐためです。これにより、数値計算が安定します。)
このように、目的関数とその勾配をコード内で直接定義することで、ソルバーに数式を渡すことができます。
制約条件の設定(線形制約)
# 投資範囲の設定
lower_bounds_var = [0, 0, 0] # 各対策の投資額の下限です。
upper_bounds_var = [limits["renew"], limits["save"], limits["conserve"]] # 上限制約です。
# 総予算の線形制約
a = np.full((1, 3), 1.0, dtype=np.float64)
lower_bounds_con = [-1.0e20]
upper_bounds_con = [budget]
# nlp1_solveの仕様に合わせて変数の上下限と制約の上下限を結合
lower_bounds = np.concatenate((lower_bounds_var, lower_bounds_con))
upper_bounds = np.concatenate((upper_bounds_var, upper_bounds_con))
ここでは、各対策への投資額に対する上下限と総予算の線形制約を設定しています。
lower_bounds_var
とupper_bounds_var
は、それぞれの変数(各対策の投資額)の範囲を設定しています。lower_bounds_var = [0, 0, 0]
は各投資額の下限を0に設定し、投資が0以上であることを示しています。upper_bounds_var = [50, 30, 20]
は各投資額の上限を設定し、再生可能エネルギーへの投資が50、省エネ設備への投資が30、森林保全への投資が20を超えないことを示しています。- 線形制約は、行列
a
で係数を指定し、lower_bounds_con
とupper_bounds_con
で上下限を設定します。
今回の総予算の制約式は次のようになっています:
x1 + x2 + x3 <= 60
この式の各係数(今回のケースでは全て1)を係数行列a
に以下のように指定しています。
a = np.full((1, 3), 1.0)
ここでlp_solve
は複数の制約式の係数を二次元配列a
で与えるようになっているため、その仕様に合わせ1x3の行列としています。(今回は1つの制約式があり、3つの変数があるため)
そして、制約式の上限はupper_bounds_con = [60]、下限はlower_bounds_con = [-1.0e20]と与え、各対策の投資額の合計が60を超えないことを指定しています。
変数と制約の上下限の指定方法
nlp1_solve
では、変数と制約の下限と上限を、lower_bounds
とupper_bounds
の引数にまとめて指定します。これらの配列の大きさは、変数の数
+ 線形制約の数 +
非線形制約の数であり、この順番で値を並べる必要があります。
今回の問題では、変数(投資額)が3つ、線形制約(予算制約)が1つあるので、lower_bounds
とupper_bounds
の配列の大きさは3 + 1 = 4
となります。具体的には、以下のように変数の上下限と線形制約の上下限を結合してそれを行っています。
lower_bounds = np.concatenate((lower_bounds_var, lower_bounds_con))
upper_bounds = np.concatenate((upper_bounds_var, upper_bounds_con))
ソルバーの呼び出しと結果の取得
以上で定義した目的関数(objective
)、制約式(a
)、変数と制約の上下限(lower_bounds
,
upper_bounds
)などを引数としてnlp1_solve
を呼び出すことで、最適化計算を実行しています。その他、変数の初期値(x_init
)や通信用のオブジェクト(comm
)なども引数として渡します。
result = opt.nlp1_solve(
a, lower_bounds, upper_bounds, objective, x_init, comm
)
nlp1_solve
関数は最適化の結果を含むオブジェクトを返します。このオブジェクトから最適解を取得しています。
結果例
上記のコードを実行すると、以下のような出力が得られます。
最適な投資額:
再生可能エネルギーへの投資 (renew): 40.64 百万円
省エネルギーへの投資 (save): 10.16 百万円
森林保全への投資 (conserve): 9.20 百万円
達成されたCO2削減量の最大値: 982.66 トン
上記の結果から、提示された予算60百万円の範囲内で、再生可能エネルギーへの投資に40.64百万円、省エネルギーへの投資に10.16百万円、森林保全への投資に9.20百万円を配分することが最適であることがわかります。この投資配分により、CO2削減量は982.66トンに達します。
これらの数値は、企業が限られた予算内で効果的にCO2排出量を削減するための意思決定に役立ちます。各対策への最適な投資額を把握することで、企業はリソースを戦略的に配分し、環境への影響を最小限に抑えながら、経済的にも持続可能な事業運営を行うことができます。
まとめ
ここでは、非線形計画法(NLP)を用いて、限られた予算内で複数のCO2削減対策に最適な投資配分を行い、CO2削減量を最大化する問題を取り上げました。 このアプローチは、他の資源配分問題にも適用可能で、幅広い分野での応用が可能です。