diff --git a/doc/common/img/limitation_beta_max.pdf b/doc/common/img/limitation_beta_max.pdf
new file mode 100644
index 00000000..49e43d3e
Binary files /dev/null and b/doc/common/img/limitation_beta_max.pdf differ
diff --git a/doc/common/img/limitation_beta_max.png b/doc/common/img/limitation_beta_max.png
new file mode 100644
index 00000000..4446b42d
Binary files /dev/null and b/doc/common/img/limitation_beta_max.png differ
diff --git a/doc/common/img/limitation_beta_min.pdf b/doc/common/img/limitation_beta_min.pdf
new file mode 100644
index 00000000..f130258d
Binary files /dev/null and b/doc/common/img/limitation_beta_min.pdf differ
diff --git a/doc/common/img/limitation_beta_min.png b/doc/common/img/limitation_beta_min.png
new file mode 100644
index 00000000..7360c798
Binary files /dev/null and b/doc/common/img/limitation_beta_min.png differ
diff --git a/doc/ja/source/input.rst b/doc/ja/source/input.rst
index d8606232..ea2bf59d 100644
--- a/doc/ja/source/input.rst
+++ b/doc/ja/source/input.rst
@@ -118,10 +118,11 @@ py2dmat は入力ファイルの形式に `TOML `_ を採
[``runner``] セクション
************************
``Algorithm`` と ``Solver`` を橋渡しする要素である ``Runner`` の設定を記述します。
-サブセクションとして ``mapping`` と ``log`` を持ちます。
+サブセクションとして ``mapping`` 、 ``limitation`` 、 ``log`` を持ちます。
+
[``mapping``] セクション
-************************
+************************************************
``Algorithm`` で探索している :math:`N` 次元のパラメータ :math:`x` から ``Solver`` で使う :math:`M` 次元のパラメータ :math:`y` への写像を定義します。
:math:`N \ne M` となる場合には、 ``solver`` セクションにも ``dimension`` パラメータを指定してください。
@@ -166,8 +167,96 @@ py2dmat は入力ファイルの形式に `TOML `_ を採
を表します。
+[``limitation``] セクション
+************************************************
+
+``Algorithm`` で探索している :math:`N` 次元のパラメータ :math:`x` に、制約条件を課すことが出来ます。
+``Algorithm`` ごとに定義する探索範囲(例:``exchange`` の ``min_list`` や ``max_list`` ) に加えて課すことが出来ます。
+現在は :math:`M` 行 :math:`N` 列の行列Aと :math:`M` 次元の縦ベクトルbから定義される :math:`Ax+b>0` の制約式が利用可能です。具体的に
+
+.. math::
+
+ A_{1,1} x_{1} + A_{1,2} x_{2} + &... + A_{1,N} x_{N} + b_{1} > 0\\
+ A_{2,1} x_{1} + A_{2,2} x_{2} + &... + A_{2,N} x_{N} + b_{2} > 0\\
+ &...\\
+ A_{M,1} x_{1} + A_{M,2} x_{2} + &... + A_{M,N} x_{N} + b_{M} > 0
+
+という制約をかけることが出来ます。
+ここで :math:`M` は制約式の個数(任意)となります。
+
+- ``co_a``
+
+ 形式: リストのリスト、あるいは文字列 (default: [])
+
+ 説明: 制約式の行列 :math:`A` を設定します。
+
+ 行数は制約式数 :math:`M` 列数は探索変数の数 :math:`N` である必要があります。
+
+ ``co_b`` を同時に定義する必要があります。
+
+- ``co_b``
+
+ 形式: リストのリスト、あるいは文字列 (default: [])
+
+ 説明: 制約式の縦ベクトル :math:`b` を設定します。
+
+ 次元数が制約式数 :math:`M` の縦ベクトルを設定する必要があります。
+
+ ``co_a`` を同時に定義する必要があります。
+
+行列の指定方法について、[``mapping``] セクションと同様で、例えば、 ::
+
+ A = [[1,1], [0,1]]
+
+と ::
+
+ A = """
+ 1 1
+ 0 1
+ """
+
+はともに
+
+.. math::
+
+ A = \left(
+ \begin{matrix}
+ 1 & 1 \\
+ 0 & 1
+ \end{matrix}
+ \right)
+
+を表します。また、 ::
+
+ co_b = [[0], [-1]]
+
+と ::
+
+ co_b = """0 -1"""
+
+と ::
+
+ co_b = """
+ 0
+ -1
+ """
+
+はともに
+
+.. math::
+
+ b = \left(
+ \begin{matrix}
+ 0 \\
+ -1
+ \end{matrix}
+ \right)
+
+を表します。
+``co_a`` と ``co_b`` のどちらも定義しない場合、制約式を課さずに探索します。
+
[``log``] セクション
-************************
+************************************************
solver 呼び出しのlogging に関する設定です。
diff --git a/doc/ja/source/tutorial/index.rst b/doc/ja/source/tutorial/index.rst
index 133470a5..8b3ebded 100644
--- a/doc/ja/source/tutorial/index.rst
+++ b/doc/ja/source/tutorial/index.rst
@@ -38,6 +38,7 @@ TRHEPDでは原子座標を与えた場合に、回折データがシミュレ
の5つのアルゴリズムが用意されています。
本チュートリアルでは、最初に順問題プログラム ``sim_trhepd_rheed`` の実行方法、
その後に ``minsearch`` , ``mapper_mpi``, ``bayes``, ``exchange``, ``pamc`` の実行方法について順に説明します。
+また、制約式を用いて探索範囲を制限出来る ``[runner.limitation]`` セクションを使用した実行方法も説明しています。
最後に、自分で順問題ソルバーを定義する簡単な例について説明します。
.. toctree::
@@ -48,6 +49,7 @@ TRHEPDでは原子座標を与えた場合に、回折データがシミュレ
mpi
bayes
exchange
+ limitation
pamc
solver_simple
diff --git a/doc/ja/source/tutorial/limitation.rst b/doc/ja/source/tutorial/limitation.rst
new file mode 100644
index 00000000..dd53f3be
--- /dev/null
+++ b/doc/ja/source/tutorial/limitation.rst
@@ -0,0 +1,192 @@
+制約式を適用したレプリカ交換モンテカルロ法による探索
+==========================================================================
+
+ここでは、 ``[runner.limitation]`` セクションに設定できる制約式機能のチュートリアルを示します。
+例として、レプリカ交換モンテカルロ法を、Himmelblauを対象に探索する計算に制約式を適用します。
+
+サンプルファイルの場所
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+サンプルファイルは ``sample/analytical/limitation`` にあります。
+フォルダには以下のファイルが格納されています。
+
+- ``ref.txt``
+
+ 計算が正しく実行されたか確認するためのファイル(本チュートリアルを行うことで得られる ``best_result.txt`` の回答)。
+
+- ``input.toml``
+
+ メインプログラムの入力ファイル。
+
+- ``do.sh``
+
+ 本チュートリアルを一括計算するために準備されたスクリプト
+
+以下、これらのファイルについて説明したあと、実際の計算結果を紹介します。
+
+入力ファイルの説明
+~~~~~~~~~~~~~~~~~~~
+
+メインプログラム用の入力ファイル ``input.toml`` について説明します。
+``input.toml`` の詳細については入力ファイルに記載されています。
+以下は、サンプルファイルにある ``input.toml`` の中身になります。
+
+.. code-block::
+
+ [base]
+ dimension = 2
+ output_dir = "output"
+
+ [algorithm]
+ name = "exchange"
+ seed = 12345
+
+ [algorithm.param]
+ max_list = [6.0, 6.0]
+ min_list = [-6.0, -6.0]
+ unit_list = [0.3, 0.3]
+
+ [algorithm.exchange]
+ Tmin = 1.0
+ Tmax = 100000.0
+ numsteps = 10000
+ numsteps_exchange = 100
+
+ [solver]
+ name = "analytical"
+ function_name = "himmelblau"
+
+ [runner]
+ [runner.limitation]
+ co_a = [[1, -1],[1, 1]]
+ co_b = [[0], [-1]]
+
+ここではこの入力ファイルを簡単に説明します。
+詳細は入力ファイルのレファレンスを参照してください。
+
+``[base]`` セクションはメインプログラム全体のパラメータです。
+``dimension`` は最適化したい変数の個数で、今の場合は2つの変数の最適化を行うので、``2`` を指定します。
+
+``[algorithm]`` セクションは用いる探索アルゴリズムを設定します。
+交換モンテカルロ法を用いる場合には、 ``name`` に ``"exchange"`` を指定します。
+``label_list`` は、``value_0x`` (x=1,2) を出力する際につけるラベル名のリストです。
+``seed`` は擬似乱数生成器に与える種です。
+
+``[algorithm.param]`` サブセクションは、最適化したいパラメータの範囲などを指定します。
+``min_list`` は最小値、 ``max_list`` は最大値を示します。
+
+``[algorithm.exchange]`` サブセクションは、交換モンテカルロ法のハイパーパラメータを指定します。
+
+- ``numstep`` はモンテカルロ更新の回数です。
+- ``numsteps_exchange`` で指定した回数のモンテカルロ更新の後に、温度交換を試みます。
+- ``Tmin``, ``Tmax`` はそれぞれ温度の下限・上限です。
+- ``Tlogspace`` が ``true`` の場合、温度を対数空間で等分割します。このオプションはデフォルト値が ``true`` であるため、今回の ``input.toml`` に指定は無いですが、 ``true`` になっています。
+
+``[solver]`` セクションではメインプログラムの内部で使用するソルバーを指定します。
+今回は ``analytical`` ソルバーを指定しています。 ``analytical`` ソルバーは ``function_name`` パラメータを用いて関数を設定します。
+今回はHimmelblau 関数を指定しています。
+``analytical`` ソルバーに関してはチュートリアル「順問題ソルバーの追加」を参照してください。
+
+``[runner]`` セクションは ``[runner.limitation]`` サブセクションを持ち、この中に制約式を設定します。
+現在、制約式は :math:`N` 次元のパラメータ :math:`x` 、 :math:`M` 行 :math:`N` 列の行列 :math:`A` 、
+:math:`M` 次元の縦ベクトル :math:`b` から定義される :math:`Ax+b>0` の制約式が利用可能です。
+パラメータとしては、以下の項目が設定可能です。
+
+- ``co_a`` は行列 :math:`A` を設定します。
+- ``co_b`` は縦ベクトル :math:`b` を設定します。
+
+パラメータの詳しい設定方法はマニュアル内「入力ファイル」項の「 [``limitation``] セクション」を参照してください。
+今回は
+
+.. math::
+
+ x_{1} − x_{2} > 0\\
+ x_{1} + x_{2} − 1 > 0
+
+の制約式を課して実行しています。
+
+計算実行
+~~~~~~~~~~~~
+
+最初にサンプルファイルが置いてあるフォルダへ移動します(以下、本ソフトウェアをダウンロードしたディレクトリ直下にいることを仮定します).
+
+.. code-block::
+
+ cd sample/analytical/limitation
+
+そのあとに、メインプログラムを実行します(計算時間は通常のPCで20秒程度で終わります)。
+
+.. code-block::
+
+ mpiexec -np 10 python3 ../../../src/py2dmat_main.py input.toml | tee log.txt
+
+ここではプロセス数10のMPI並列を用いた計算を行っています。
+(Open MPI を用いる場合で、使えるコア数よりも要求プロセス数の方が多い時には、
+``mpiexec`` コマンドに ``--oversubscribed`` オプションを追加してください。)
+実行すると、 ``output`` フォルダが生成され、その中に各ランクのフォルダが作成されます。
+更にその中には、各モンテカルロステップで評価したパラメータおよび目的関数の値を記した ``trial.txt`` ファイルと、
+実際に採択されたパラメータを記した ``result.txt`` ファイルが作成されます。
+ともに書式は同じで、最初の2列がステップ数とプロセス内のwalker 番号、次が温度、3列目が目的関数の値、4列目以降がパラメータです。
+以下は、 ``output/0/result.txt`` ファイルの冒頭部分です。
+
+.. code-block::
+
+ # step walker T fx x1 x2
+ 0 0 1.0 187.94429125133564 5.155393113805774 -2.203493345018569
+ 1 0 1.0 148.23606736778044 4.9995614992887525 -2.370212436322816
+ 2 0 1.0 148.23606736778044 4.9995614992887525 -2.370212436322816
+ 3 0 1.0 148.23606736778044 4.9995614992887525 -2.370212436322816
+
+最後に、 ``output/best_result.txt`` に、目的関数が最小となったパラメータとそれを得たランク、モンテカルロステップの情報が書き込まれます。
+
+.. code-block::
+
+ nprocs = 10
+ rank = 2
+ step = 4523
+ walker = 0
+ fx = 0.00010188398524402734
+ x1 = 3.584944906595298
+ x2 = -1.8506985826548874
+
+なお、一括計算するスクリプトとして ``do.sh`` を用意しています。
+``do.sh`` では ``best_result.txt`` と ``ref.txt`` の差分も比較しています。
+以下、説明は割愛しますが、その中身を掲載します。
+
+.. code-block::
+
+ #!/bin/bash
+ mpiexec -np 10 --oversubscribe python3 ../../../src/py2dmat_main.py input.toml
+
+ echo diff output/best_result.txt ref.txt
+ res=0
+ diff output/best_result.txt ref.txt || res=$?
+ if [ $res -eq 0 ]; then
+ echo TEST PASS
+ true
+ else
+ echo TEST FAILED: best_result.txt and ref.txt differ
+ false
+ fi
+
+計算結果の可視化
+~~~~~~~~~~~~~~~~~~~
+
+``result.txt`` を図示して、制約式を満たした座標のみを探索しているかを確認します。
+今回の場合は、以下のコマンドを打つことで2次元パラメータ空間の図が ``<実行日>_histogram`` フォルダ内に作成されます。
+生成されるヒストグラムは、burn-in期間として最初の1000ステップ分の探索を捨てたデータを使用しています。
+
+.. code-block::
+
+ python3 hist2d_limitation_sample.py -p 10 -i input.toml -b 0.1
+
+作成された図には2本の直線 :math:`x_{1} − x_{2} = 0, x_{1} + x_{2} − 1 = 0` と
+探索結果(事後確率分布のヒストグラム)を図示しています。
+図を見ると :math:`x_{1} − x_{2} > 0, x_{1} + x_{2} − 1 > 0` の範囲のみ探索をしていることが確認できます。
+以下に図の一部を掲載します。
+
+.. figure:: ../../../common/img/limitation_beta_min.*
+
+.. figure:: ../../../common/img/limitation_beta_max.*
+
+ サンプルされたパラメータと確率分布。横軸は ``value_01 (x1)`` , 縦軸は ``value_02 (x2)`` を表す。
diff --git a/sample/analytical/limitation/do.sh b/sample/analytical/limitation/do.sh
new file mode 100644
index 00000000..8de55d57
--- /dev/null
+++ b/sample/analytical/limitation/do.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+mpiexec -np 10 --oversubscribe python3 ../../../src/py2dmat_main.py input.toml
+
+echo diff output/best_result.txt ref.txt
+res=0
+diff output/best_result.txt ref.txt || res=$?
+if [ $res -eq 0 ]; then
+ echo TEST PASS
+ true
+else
+ echo TEST FAILED: best_result.txt and ref.txt differ
+ false
+fi
diff --git a/sample/analytical/limitation/hist2d_limitation_sample.py b/sample/analytical/limitation/hist2d_limitation_sample.py
new file mode 100644
index 00000000..0183c8a1
--- /dev/null
+++ b/sample/analytical/limitation/hist2d_limitation_sample.py
@@ -0,0 +1,210 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.colors as clr
+import toml as tm
+import sys
+import datetime
+import os
+import math as m
+import collections as cl
+import argparse
+import decimal
+
+def read_toml():
+ #input.tomlを取得
+ dict_toml= tm.load(inputfile)
+
+ #変数の範囲の最大値・最小値とTがlogスケールかを取得
+ x1_min,x2_min= dict_toml['algorithm']['param']['min_list']
+ x1_max,x2_max= dict_toml['algorithm']['param']['max_list']
+ T_min= dict_toml['algorithm']['exchange']['Tmin']
+ T_max= dict_toml['algorithm']['exchange']['Tmax']
+ Tlog= True #dict_toml['algorithm']['exchange']['Tlogspace']
+ #Tlogはブーリアン型
+
+ #結果を出力したディレクトリ名を取得
+ output_dicname= dict_toml['base']['output_dir']
+
+ #探索回数を取得
+ numsteps= int(dict_toml['algorithm']['exchange']['numsteps'])
+ numsteps_exchange= int(dict_toml['algorithm']['exchange']['numsteps_exchange'])
+
+ name_for_simulate = ''
+ return x1_min, x2_min, x1_max, x2_max, T_max, T_min, Tlog, output_dicname, numsteps, numsteps_exchange, name_for_simulate
+
+
+def read_result():
+
+ #各ディレクトリから得た値を格納する変数
+ x1= []
+ x2= []
+ fx= []
+ T= []
+ step= range(1,numsteps+1,1)
+
+ #各探索プロセッサにおけるfxの最大値・最小値を格納する変数
+ fx_max_list= []
+ fx_min_list= []
+
+ #以下よりファイルの読み込み
+ i= 0
+ while i <= number_of_replica-1 :
+ print('{0}/{1} ファイル読み込み開始'.format(i+1,number_of_replica))
+ #リストの中にi番目のディレクトリの値を格納するリストを作成
+ x1.append([])
+ x2.append([])
+ fx.append([])
+ T.append([])
+
+ #ファイルを開き、改行区切りのリストにする。
+ f=open('{0}/{1}/result.txt'.format(output_dicname,i))
+ frs= f.read().splitlines()
+ f.close()
+
+ #繰り返し処理で値を抽出、float型へ変換
+ #jが1からスタートなのはresult.txtの1行目には結果の値が入っていないため
+ j= 1
+ while j< len(frs):
+ sp= frs[j].split()
+ x1[i].append(float(sp[-2]))
+ x2[i].append(float(sp[-1]))
+ fx[i].append(float(sp[-3]))
+ T[i].append(float(sp[-4]))
+ j+= 1
+
+ #i番目のresult.txtにおけるfxの最大値・最小値を格納
+ fx_max_list.append(max(fx[i]))
+ fx_min_list.append(min(fx[i]))
+ i+= 1
+
+ print('読み込み完了')
+
+ #読み込みができてるかの確認
+ #print(x1[0][0:5],'\n\n',x1[1][0:5],'\n\n',x1[2][0:5],'\n\n',x1[3][0:5])
+ return x1, x2, fx, T, step, max(fx_max_list), min(fx_min_list)
+
+
+def sort_T():
+ sorted_x1= []
+ sorted_x2= []
+ sorted_fx= []
+ replica_num= []
+ #original_x1= cp.deepcopy(x1)
+ #original_x2= cp.deepcopy(x2)
+ #original_fx= cp.deepcopy(fx)
+
+ Tvalue= make_Tvalue_list()
+
+ i= 0
+ while i < len(Tvalue):
+ sorted_x1.append([])
+ sorted_x2.append([])
+ sorted_fx.append([])
+ replica_num.append([])
+ i+= 1
+
+ i = int(numsteps*burn_in)
+ while i < numsteps:
+ j= 0
+ while j < number_of_replica:
+ k= 0
+ while k < len(Tvalue):
+ if Tvalue[k] == T[j][i]:
+ sorted_x1[k].append(x1[j][i])
+ sorted_x2[k].append(x2[j][i])
+ sorted_fx[k].append(fx[j][i])
+ replica_num[k].append(j)
+ k+= 1
+ j+= 1
+ i+= 1
+
+ sorted_T= True
+ return sorted_x1, sorted_x2, sorted_fx, replica_num, Tvalue, sorted_T
+
+
+def make_Tvalue_list():
+ Tvalue= []
+ i= 0
+ while i < number_of_replica :
+ Tvalue.extend(list(cl.Counter(T[i]).keys()))
+ i+= 1
+ Tvalue= list(cl.Counter(Tvalue).keys())
+ return Tvalue
+
+def soloplot():
+ #繰り返し処理でプロット・図の保存
+ print('グラフ描画開始')
+ min_l = []
+ max_l = []
+
+ l= 0
+ while l< number_of_replica:
+ fig = plt.figure(figsize= (8,8))
+ ax = fig.add_subplot()
+
+ num_of_sample = len(sorted_x1[l][:])
+ weight_l = np.ones(num_of_sample)/num_of_sample
+
+ hst2d = ax.hist2d(
+ sorted_x1[l][:], sorted_x2[l][:],
+ norm= clr.LogNorm(vmin=10**-4, vmax=10**-1),
+ range=[ [x1_min,x1_max] , [x2_min,x2_max] ],
+ cmap= 'Reds', weights=weight_l , bins=100)
+
+ sum_allspace = np.sum(hst2d[0])
+ max_in_one_space = hst2d[0].max()
+ min_in_one_space = hst2d[0][np.nonzero(hst2d[0])].min()
+ min_l.append(min_in_one_space)
+ max_l.append(max_in_one_space)
+
+ cb = fig.colorbar(hst2d[3])
+
+ temp_for_title = decimal.Decimal(1/Tvalue[l]).quantize(
+ decimal.Decimal('0.00001'), rounding=decimal.ROUND_HALF_UP
+ )
+ figtitlestr = f'beta = {temp_for_title}'
+
+ ax.set_title(figtitlestr)
+
+ line_x1 = np.arange(x1_min,x1_max+1,1)
+ line_x2_1 = line_x1
+ line_x2_2 = -line_x1 + 1
+ ax.plot(line_x1,line_x2_1,c="black",alpha=0.3,lw=0.5)
+ ax.plot(line_x1,line_x2_2,c="black",alpha=0.3,lw=0.5)
+ #'''
+ ax.set_xlabel("x1")
+ ax.set_ylabel("x2")
+ ax.set_xlim(x1_min,x1_max)
+ ax.set_ylim(x2_min,x2_max)
+ ax.set_aspect('equal')
+ #軸サイズ設定と図の保存
+ fig.savefig(f'{dirname}/{l}_beta_{temp_for_title}_burn_in_{burn_in}.png',dpi=300)
+ l+= 1
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-p','--mpiprocess',help='MPI process')
+ parser.add_argument('-i','--inputfile',help='input toml file')
+ parser.add_argument('-b','--burn_in',help='burn-in ratio')
+ args = parser.parse_args()
+
+ number_of_replica = int(args.mpiprocess)
+ inputfile = args.inputfile
+ burn_in = float(args.burn_in)
+
+ x1_min, x2_min, x1_max, x2_max, T_max, T_min, Tlog, output_dicname, numsteps, numsteps_exchange, name_for_simulate= read_toml()
+
+ #result.txtから読み取り
+ x1, x2, fx, T, step, fx_max, fx_min = read_result()
+
+ #グラフタイトル・ファイル名用に実行した時間の年日時間分を取得
+ time= datetime.date.today().strftime("%Y%m%d")
+
+ sorted_x1, sorted_x2, sorted_fx, replica_num, Tvalue, sorted_T= sort_T()
+
+ dirname= '{0}_histogram'.format(time)
+ if os.path.exists(dirname) == False:
+ os.makedirs(dirname)
+
+ soloplot()
diff --git a/sample/analytical/limitation/input.toml b/sample/analytical/limitation/input.toml
new file mode 100644
index 00000000..05e4163a
--- /dev/null
+++ b/sample/analytical/limitation/input.toml
@@ -0,0 +1,27 @@
+[base]
+dimension = 2
+output_dir = "output"
+
+[algorithm]
+name = "exchange"
+seed = 12345
+
+[algorithm.param]
+max_list = [6.0, 6.0]
+min_list = [-6.0, -6.0]
+unit_list = [0.3, 0.3]
+
+[algorithm.exchange]
+Tmin = 1.0
+Tmax = 100000.0
+numsteps = 10000
+numsteps_exchange = 100
+
+[solver]
+name = "analytical"
+function_name = "himmelblau"
+
+[runner]
+[runner.limitation]
+co_a = [[1, -1],[1, 1]]
+co_b = [[0], [-1]]
diff --git a/sample/analytical/limitation/ref.txt b/sample/analytical/limitation/ref.txt
new file mode 100644
index 00000000..63b22eae
--- /dev/null
+++ b/sample/analytical/limitation/ref.txt
@@ -0,0 +1,7 @@
+nprocs = 10
+rank = 2
+step = 4523
+walker = 0
+fx = 0.00010188398524402734
+x1 = 3.584944906595298
+x2 = -1.8506985826548874
diff --git a/src/py2dmat/_runner.py b/src/py2dmat/_runner.py
index 7e21e21f..f60818fb 100644
--- a/src/py2dmat/_runner.py
+++ b/src/py2dmat/_runner.py
@@ -184,47 +184,55 @@ def __init__(
self.ndim = info.base["dimension"]
if limitation is None:
info_limitation = info.runner.get("limitation",{})
- co_a: Optional[np.ndarray] = py2dmat.util.read_matrix.read_matrix(
+ co_a: np.ndarray = py2dmat.util.read_matrix.read_matrix(
info_limitation.get("co_a", [])
)
- co_b: Optional[np.ndarray] = py2dmat.util.read_matrix.read_matrix(
+ co_b: np.ndarray = py2dmat.util.read_matrix.read_matrix(
info_limitation.get("co_b", [])
)
- if co_a is not None:
- if co_a.size == 0:
- co_a = None
- else:
- if co_a.ndim != 2:
- raise InputError("co_a should be a matrix")
- if co_a.shape[1] != self.ndim:
- msg ='The number of columns in co_a should be equal to'
- msg+='the value of "dimension" in the [base] section'
- raise InputError(msg)
- n_row_co_a = co_a.shape[0]
- if co_b is not None:
- if co_b.size == 0:
- if co_a is None:
- co_b = None
- else: # co_a is not None:
- msg = "ERROR: co_a is defined but co_b is not."
- raise InputError(msg)
- elif co_b.ndim == 2:
- if co_a is not None:
- if co_b.shape[0] == 1 or co_b.shape[1] == 1:
- co_b = co_b.reshape(-1)
- else:
- raise InputError("co_b should be a vector")
- if co_b.size != n_row_co_a:
- msg ='The number of row in co_a should be equal to'
- msg+='the number of size in co_b'
- raise InputError(msg)
- else: # co_a is None:
- msg = "ERROR: co_b is defined but co_a is not."
+ if co_a.size == 0:
+ is_set_co_a = False
+ else:
+ is_set_co_a = True
+ if co_a.ndim != 2:
+ raise InputError("co_a should be a matrix")
+ if co_a.shape[1] != self.ndim:
+ msg ='The number of columns in co_a should be equal to'
+ msg+='the value of "dimension" in the [base] section'
+ raise InputError(msg)
+ n_row_co_a = co_a.shape[0]
+ if co_b.size == 0:
+ if not is_set_co_a :
+ is_set_co_b = False
+ else: # is_set_co_a is True
+ msg = "ERROR: co_a is defined but co_b is not."
+ raise InputError(msg)
+ elif co_b.ndim == 2:
+ if is_set_co_a:
+ if co_b.shape[0] == 1 or co_b.shape[1] == 1:
+ is_set_co_b = True
+ co_b = co_b.reshape(-1)
+ else:
+ raise InputError("co_b should be a vector")
+ if co_b.size != n_row_co_a:
+ msg ='The number of row in co_a should be equal to'
+ msg+='the number of size in co_b'
raise InputError(msg)
+ else: # not is_set_co_a:
+ msg = "ERROR: co_b is defined but co_a is not."
+ raise InputError(msg)
+ elif co_b.ndim > 2:
+ raise InputError("co_b should be a vector")
+
+ if is_set_co_a and is_set_co_b:
+ is_limitation = True
+ elif (not is_set_co_a) and (not is_set_co_b):
+ is_limitation = False
+ else:
+ msg = "ERROR: Both co_a and co_b must be defined."
+ raise InputError(msg)
- elif co_b.ndim > 2:
- raise InputError("co_b should be a vector")
- self.limitation = py2dmat.util.limitation.Inequality(co_a, co_b)
+ self.limitation = py2dmat.util.limitation.Inequality(co_a, co_b, is_limitation)
def prepare(self, proc_dir: Path):
self.logger.prepare(proc_dir)
diff --git a/src/py2dmat/util/limitation.py b/src/py2dmat/util/limitation.py
index 0c973f7f..5a1f03e4 100644
--- a/src/py2dmat/util/limitation.py
+++ b/src/py2dmat/util/limitation.py
@@ -6,23 +6,14 @@
# for type hints
from pathlib import Path
-from typing import List, Optional, TYPE_CHECKING, Dict, Tuple
+from typing import List, Optional, Dict
class LimitationBase(metaclass=ABCMeta):
@abstractmethod
- def __init__ (self, a: Optional[np.ndarray] = None,
- b: Optional[np.ndarray] = None):
- if (a is not None) or (b is not None):
- if a is None:
- msg = "ERROR: b is defined but a is not."
- raise RuntimeError(msg)
- if b is None:
- msg = "ERROR: a is defined but b is not."
- raise RuntimeError(msg)
- if (a is None) and (b is None):
- self.islimitary = False
- else:
- self.islimitary = True
+ def __init__ (self, a: np.ndarray, b: np.ndarray, is_limitary: bool):
+
+ self.islimitary = is_limitary
+ if self.islimitary:
self.a = a
self.b = b
self.n_formura = a.shape[0]
@@ -33,9 +24,8 @@ def judge(self, x: np.ndarray) -> bool:
pass
class Inequality(LimitationBase):
- def __init__ (self, a: Optional[np.ndarray] = None,
- b: Optional[np.ndarray] = None):
- super().__init__(a, b)
+ def __init__ (self, a: np.ndarray, b: np.ndarray, is_limitary: bool):
+ super().__init__(a, b, is_limitary)
def judge(self, x: np.ndarray) -> bool:
if self.islimitary :