CasADi - データ型の詳しい説明と基本操作の紹介


1. よく使われる記号

1.1 SX記号

  SX データ型は、要素が一連の単項演算と二項演算で構成されるシンボリック式で構成される行列を表すために使用されます。これが実際にどのように機能するかを確認するには、対話型 Python シェルを開始するか (たとえば、Linux ターミナルまたは Spyder などの統合開発環境で ipython と入力して)、MATLAB または Octave のグラフィカル ユーザー インターフェイスを開始します。CasADi が正しくインストールされていると仮定すると、次のようにライブラリをワークスペースにインポートできます。

from casadi import *

次の構文を使用して変数 x を作成します。

x = MX.sym("x")
print(x)

実行後の出力は次のとおりです。

バツ

上記のコードは、x (スカラー) という名前のシンボリック プリミティブである 1×1 行列を作成します。これは単なる表示名であり、識別子ではありません。複数の変数が同じ名前であっても、異なる名前を持つことができます。識別子は戻り値です。さらに、関数 SX.sym を (パラメーターを使用して) 呼び出して、ベクトルまたは行列値のシンボリック変数を作成できます。

y = SX.sym('y',5)
Z = SX.sym('Z',4,2)
print(y)
print(z)

実行後の出力は次のとおりです。

[y_0, y_1, y_2, y_3, y_4]
[[Z_0, Z_4],
 [Z_1, Z_5],
 [Z_2, Z_6],
 [Z_3, Z_7]]

上記のコードは、それぞれシンボリック プリミティブを含む 5×1 行列 (つまり、ベクトル) と 4×2 行列を作成します。SX.sym は、SX インスタンスを返す (静的) 関数です。変数を宣言した後、式を直感的な方法で作成できるようになりました。

f = x**2 + 10
f = sqrt(f)
print(f)

実行後の出力は次のとおりです。

sqrt((10+sq(x)))

これに加えて、定数を使用せずに SX インスタンスを作成することもできます。

B1 = SX.zeros(4,5): # 一个全零的密集 4×5 空矩阵 
B2 = SX(4,5): # 全零的稀疏 4×5 空矩阵 
B4 = SX.eye(4): # 稀疏的4×4矩阵,对角线为1 
print(B1)
print(B2)
print(B4)

実行後の出力は次のとおりです。

B1: @1=0、
[[@1、@1、@1、@1、@1]、
 [@1、@1、@1、@1、@1]、
 [@1、@1、@ 1, @1, @1],
 [@1, @1, @1, @1, @1]]
B2:
[[00, 00, 00, 00, 00],
 [00, 00, 00, 00, 00]、
 [00, 00, 00, 00, 00]、
 [00, 00, 00, 00, 00]]
B4: @1=1,
[[@1, 00, 00, 00],
 [00, @ 1, 00, 00]、
 [00, 00, @1, 00]、
 [00, 00, 00, @1]]

  : 上記の式の違いに注意してください。構造ゼロ(明示的に割り当てられていない疎行列の要素) を含む式を出力する場合、これらのゼロは実際のゼロ@1 (実際の明示的な For 要素)と比較するために 00 として表されます。式によって割り当てられる場合、構造上のゼロと実際のゼロの違いを区別するために、印刷後に 0 を表すために @1 を使用します)。

上記の使用方法に加えて、次の表に一般的に使用される SX 式を示します。

関数式 使用
SX.sym(名前,n,m) n×m のシンボリック プリミティブを作成する
SX.ゼロ(n,m) すべての要素が 0 である n 行 m 列の密行列を作成する
SX(n,m) すべての要素がゼロの n×m 疎行列を作成する
SX.ones(n,m) すべて 1 を含む n 行 m 列の密行列を作成します。
SX.eye(n) 対角要素が 1 つ、その他の部分が構造ゼロである n×n 対角行列を作成します。
SX(スカラータイプ) 引数で指定された値を使用してスカラー (1×1 行列) を作成します。このメソッドは、SX(9) のように明示的に使用することも、9 * SX.ones(2,2) のように暗黙的に使用することもできます。
SX(行列の種類) 指定された数値行列の行列を、NumPy 行列または SciPy 行列 (Python の場合)、または密行列または疎行列 (MATLAB/Octave の場合) として作成します。たとえば、MATLAB/Octave では、SX([1,2,3,4]) は行ベクトルを表し、SX([1;2;3;4]) は列ベクトルを表し、SX([1,2;3]) ,4] ) は 2×2 行列を表し、このメソッドは明示的または暗黙的に使用できます。
repmat(v,n,m) 式 v を垂直方向に n 回、水平方向に m 回繰り返します。例: repmat(SX(3),2,1) は、すべての要素が 3 である 2×1 行列を作成します。
SX(リスト) (Python のみ) SX([1,2,3,4]) などのリスト内の要素を使用して、列ベクトル (n 行 1 列の行列) を作成します。(Python リストと MATLAB/Octave 水平連結の違いに注意してください。どちらも角括弧構文を使用します)
SX (リストのリスト) (Python のみ) リスト内の要素を使用して密行列を作成するか、たとえば SX([[1,2],[3,4]]) を使用して (1×n 行列を作成します) SX([[1,2,3,4] ]) ) 行ベクトル。

1.2 DM シンボル

  DM は SX と非常によく似ていますが、ゼロ以外の要素がシンボリック式ではなく数値である点が異なります。DM の構文も SX.sym などの一部の機能を除き、SX と同じです。

  DM は主に、CasADi に行列を保存するため、および関数の入出力として使用されます。計算負荷の高い計算での使用を目的としていません。これを行うには、MATLAB の組み込みの密またはスパース データ型、Python の NumPy または SciPy 行列、または C++ の eigen、ublas、MTL などの式テンプレート ベースのライブラリを使用します。通常、型間の変換は簡単です。

C = DM(2,3)

C_dense = C.full()
from numpy import array
C_dense = array(C) # equivalent

C_sparse = C.sparse()
from scipy.sparse import csc_matrix
C_sparse = csc_matrix(C) # equivalent

  SX のその他の使用例は、http://install.casadi.org/ のサンプル パッケージにあります。C++ API など、このクラス (および他のクラス) の特定の機能に関するドキュメントは、http://docs.casadi.org/ にあります。


1.3 MX 表記

  次のコードの出力は 2×2 行列です。ここで最初の文 @1=3 に注目してください。これは、@1 が 2 行目の出力で 3 を表すことを意味します。出力結果は、CasADi がそれぞれの行列 f このエントリは新しい式 (タイプ SX) を作成します。

x = SX.sym('x',2,2)
y = SX.sym('y')
f = 3*x + y
print(f)
print(f.shape)

実行後の出力は次のとおりです。

@1=3,
[[((@1 x_0)+y), ((@1 x_2)+y)],
 [((@1 x_1)+y), ((@1 x_3)+y)]]
(2, 2)

  上記の方法に加えて、2 番目のより一般的な行列式タイプ MX も使用できます。MX タイプでは、SX と同様に、一連の基本演算からなる式を構築できます。ただし、SX とは異なり、これらの基本演算はスカラー単項演算や二項演算に限定されません ( R → R \mathbb{R}→\mathbb{R}RRまたはR × R → R \mathbb{R}×\mathbb{R}→\mathbb{R}R×RR)。代わりに、MX 式を形成するために使用される基本演算は、一般的な複数の疎行列値の入力、複数の疎行列値の出力関数になります。R n 1 × m 1 × … × R n N × m N → R p 1 × q 1 × … × R p M × q M \mathbb{R}^{ { n_1}×{m_1}}×…×\mathbb{R}^{ { n_N}×{m_N}}→\mathbb{ R}^{ {p_1 }×{q_1}}×…×\mathbb{R}^{ {p_M}×{q_M}}Rn1× m1××RnN× mNRp1× q1××RpM× qM、例えば:

x = MX.sym('x',2,2)
y = MX.sym('y')
f = 3*x + y
print(f)
print(f.shape)

実行後の出力は次のとおりです。

((3*x)+y)
(2, 2)

  上記の 2 つのコードは同じ結果になります。MX 表記は 2 つの演算 (乗算と加算) のみで構成されますが、SX 表記には 8 つの演算があります (各要素は 2 つで構成され、f(1,1) は (@ 1x_0) と y 合成)。したがって、多くの要素を含む自然なベクトルまたは行列値を使用した演算を使用する場合、MX はより経済的になります。

  MX は、SX と同じ構文を使用して要素の取得と設定をサポートしますが、実装は大きく異なります。たとえば、2×2 シンボリック変数の左上の要素をテスト出力します。

x = MX.sym('x',2,2)
print(x[0,0])

出力は、上記の SX の場合の x_0 とは異なり、行列の最初 (インデックス 0) の位置である x の最初 (つまり、C++ のインデックス 0) の構造的にゼロでない要素に等しい式として理解される必要があります。

  要素を設定するときの使用法は同様です。

x = MX.sym('x',2)
A = MX(2,2)
A[0,0] = x[0]
A[1,1] = x[0]+x[1]
print('A:', A)

実行後の出力は次のとおりです。

A: (プロジェクト((zeros(2x2,1nz)[0] = x[0]))[1] = (x[0]+x[1]))

この出力は次のように理解できます。すべてゼロのスパース行列から始まり、1 つの要素が x_0 に割り当てられます。次に、それを異なるスパース度の行列に投影し、別の要素を x_0+x_1 に割り当てます。


1.4 SX と MX の混合

  SX オブジェクトと MX オブジェクトを乗算したり、同じ式グラフ内で 2 つを混合する他の操作を実行したりすることはできません。ただし、SX 式で定義された関数の呼び出しを MX グラフに含めることは可能です。プログラムの効率を向上させる必要がある場合は、SX と MX を混合できます。これは、SX 式で定義された関数の各演算のオーバーヘッドが大幅に低くなり、システムが一連のスカラー演算をより速く完了できるためです。したがって、SX 式は低レベルの操作を目的としており、MX 式は NLP の制約関数の実装など、責任のある操作を実装するために使用されます。


1.5 スパースクラス

  CasADi の行列は、圧縮列ストレージ (CCS) 形式を使用して保存されます。これは、要素ごとの演算、行列の乗算、転置などの線形代数演算を効率的に実行できる疎行列の標準形式です。CCS 形式では、スパース モードはデコードに次元 (行と列の数) と 2 つのベクトルを使用します。最初のベクトルには各列の最初の構造的にゼロでない要素のインデックスが含まれ、2 番目のベクトルには各非ゼロ要素の行インデックスが含まれます。CCS 形式の詳細については、Netlib の線形システム ソリューションのテンプレートなどを参照してください。CasADi は、疎行列と密行列に CCS 形式を使用することに注意してください。

  CasADi のスパーシティ パターンは、Sparsity クラスのインスタンスとして保存され、参照カウントされます。これは、MX 式グラフや SX および DM のインスタンスなど、複数の行列が同じスパーシティ パターンを共有できることを意味します。スパース クラスもキャッシュされるため、同じスパース パターンの複数のインスタンスの作成が常に回避されます。

  次のリストは、新しいスパース パターンを構築する最も一般的な方法をまとめたものです。

関数名 関数
スパーシティ.デンス(n,m) 密なn×mの疎なパターンを作成する
スパースシティ(n,m) スパースn×mスパースパターンの作成
スパーシティ.diag(n) 斜めのn×nスパースパターンの作成
スパーシティ.upper(n) 上三角 n×n スパース パターンの作成
Sparsity. lower(n) 下三角 n×n スパース パターンを作成する

  スパース クラスは、次のような非標準行列の作成に使用できます。

print(SX.sym('x',Sparsity.lower(3)))

実行後の出力は次のとおりです。

[[x_0, 00, 00]、
 [x_1, x_3, 00]、
 [x_2, x_4, x_5]]

1.5.1 行列内の要素の取得と設定

  CasADi 行列タイプ (SX、MX、DM) の要素または要素のセットを取得または設定するには、Python では角かっこを使用し、C++ および MATLAB では丸かっこを使用します。これらの言語の規則に従って、インデックス付けは C++ と Python では 0 から始まり、MATLAB では 1 から始まります。Python と C++ では、負のインデックスを使用して、最後から数えてインデックスを指定できます。MATLAB では、end キーワードを使用して、末尾からインデックス付けを開始します。

  インデックス作成は 1 つのインデックスまたは 2 つのインデックスで実行できます。2 つのインデックスを使用すると、特定の行 (または行のグループ) と特定の列 (または列のグループ) にアクセスできます。インデックスを使用すると、左上隅から列方向に右下隅まで要素 (または要素のグループ) にアクセスできます。構造的にゼロであるかどうかに関係なく、すべての要素がカウントされます。例えば:

M = SX([[3,7],[4,5]])
print(M[0,:])
M[0,:] = 1
print(M)

実行後の出力は次のとおりです。

[[3, 7]]
@1=1,
[[@1, @1],
 [4, 5]]

  Python の NumPy とは異なり、CasADi スライスは左側のデータのビューではなく、スライス アクセスによってデータがコピーされます。したがって、次の例では、行列 m はまったく変更されていません。

M = SX([[3,7],[4,5]])
M[0,:][0,0] = 1
print(M)

実行後の出力は次のとおりです。

[[3, 7]、
 [4, 5]]

  行列要素の取得と設定については、以下で詳しく説明します。この説明は、CasADi のすべてのマトリックス タイプに当てはまります。

  行と列のペアまたはそのフラット化されたインデックス (行列の左上隅から列方向に開始) を指定して、単一要素へのアクセスを取得または設定します。

M = diag(SX([3,4,5,6]))
print(M)

実行後の出力は次のとおりです。

[[3, 00, 00, 00],
 [00, 4, 00, 00],
 [00, 00, 5, 00],

 [00, 00, 00, 6]]

print(M[0,0])
print(M[1,0])
print(M[-1,-1])

実行後の出力は次のとおりです。

3
00
6

  スライスアクセスとは、複数の要素を一度に設定することを意味します。これは、一度に 1 つの要素を設定するよりもはるかに効率的です。フラグメントは、(開始、停止、ステップ) タプルを提供することによって取得または設定できます。Python と MATLAB では、CasADi は標準構文を使用します。

print(M[:,1])
print(M[1:,1:4:2])  # 对应(start , stop , step) 

実行後の出力は次のとおりです。

[00、4、00、00]

[[4, 00]、
 [00, 00]、
 [00, 6]]

  リスト アクセスはフラグメント アクセスに似ています (しかしおそらく効率は劣ります)。

M = SX([[3,7,8,9],[4,5,6,1]])
print(M)
print(M[0,[0,3]], M[[5,-6]])

実行後の出力は次のとおりです。

[[3, 7, 8, 9],
 [4, 5, 6, 1]]
[[3, 9]] [6, 7]


1.6 算術演算

1.6.1 乗算

  CasADi は、加算、乗算、累乗、三角関数などのほとんどの標準的な算術演算をサポートしています。

x = SX.sym('x')
y = SX.sym('y',2,2)
print(sin(y)-x)

実行後の出力は次のとおりです。

[[(sin(y_0)-x), (sin(y_2)-x)],
 [(sin(y_1)-x), (sin(y_3)-x)]]

  C++ と Python (MATLAB では除く) では、要素ごとの乗算 (MATLAB .* で) に標準の乗算演算 (* を使用) が引き続き使用されます。行列の乗算には、A @ B または (Python 3.4 以降では mtimes(A,B)) を使用します。

print(y*y, y@y)

実行後の出力は次のとおりです。

[[sq(y_0), sq(y_2)],
 [sq(y_1), sq(y_3)]]
[[(sq(y_0)+(y_2*y_1)), ((y_0*y_2)+(y_2*) y_3))]、
 [((y_1*y_0)+(y_3*y_1))、((y_1*y_2)+sq(y_3))]]

MATLAB の慣例により、いずれかの引数がスカラーの場合、* と .* を使用した乗算は等価です。

1.6.2 トランスポーズ

  転置は、Python では AT、C++ では AT()、および A または A という構文を使用して形成されます。MATLAB の場合:

print(y)
print(y.T)

実行後の出力は次のとおりです。

[[y_0, y_2],
 [y_1, y_3]]

[[y_0, y_1],
 [y_2, y_3]]

1.6.3 リシェイプ()

  再形成とは、行と列の数を変更しますが、要素の数とゼロ以外の要素の相対位置は保持することを意味します。これは非常に計算コストが低い操作であり、次の構文を使用して実行されます。

x = SX.eye(4)
print(reshape(x,2,8))

実行後の出力は次のとおりです。

@1=1,
[[@1, 00, 00, 00, 00, @1, 00, 00],
 [00, 00, @1, 00, 00, 00, 00, @1]]

1.6.4 vertcat()/horzcat()

  連結とは、行列を水平または垂直に積み重ねることを意味します。CasADi では要素を列優先で格納するため、水平に積み上げられた行列が最も効率的です。実際には列ベクトルである行列 (つまり、単一の列で構成されている行列) も、効率的に垂直方向に積み重ねることができます。Python および C++ で関数 vertcat および horzcat (可変数の入力引数を受け取ります) を使用し、MATLAB で角かっこを使用して、垂直結合と水平結合を実行します。

x = SX.sym('x',5)
y = SX.sym('y',5)
print(vertcat(x,y))

実行後の出力は次のとおりです。

[x_0、x_1、x_2、x_3、x_4、y_0、y_1、y_2、y_3、y_4]

print(horzcat(x,y))

実行後の出力は次のとおりです。

[[x_0, y_0],
 [x_1, y_1],
 [x_2, y_2],
 [x_3, y_3],
 [x_4, y_4]]

  これらの関数には、リスト (Python の場合) またはセル配列 (Matlab の場合) を入力として受け取るバリエーションもあります。

L = [x,y]
print(hcat(L))

実行後の出力は次のとおりです。

[[x_0, y_0],
 [x_1, y_1],
 [x_2, y_2],
 [x_3, y_3],
 [x_4, y_4]]

1.6.5分割

  水平分割と垂直分割は、上で紹介した水平連結と垂直連結の逆の操作です。式を水平方向に n 個の小さな式に分割するには、分割する式に加えて、長さ n+1 のベクトル オフセットを指定する必要があります。オフセット ベクトルの最初の要素は 0 でなければならず、最後の要素は列番号でなければなりません。残りの要素は降順ではない必要があります。次に、分割操作の出力 i には、offset[i]≤c<offset[i+1] (offset[i]≤c<offset[i+1])] が含まれます。オフセット[ i ] _ _ _ _c<オフセット[ i _ _ _ _+列c.構文を以下に示します。

x = SX.sym('x',5,2)
w = horzsplit(x,[0,1,2])
print(w[0], w[1])

実行後の出力は次のとおりです。

[x_0、x_1、x_2、x_3、x_4] [x_5、x_6、x_7、x_8、x_9]

  vertsplit 操作も同様に機能しますが、オフセット ベクトルは行を参照します。

w = vertsplit(x,[0,3,5])
print(w[0], w[1])

実行後の出力は次のとおりです。

[[x_0, x_5],
 [x_1, x_6],
 [x_2, x_7]]
[[x_3, x_8],
 [x_4, x_9]]

  上記の垂直分割の場合、水平分割と垂直分割の代わりにフラグメント アクセスを使用できます。

w = [x[0:3,:], x[3:5,:]]
print(w[0], w[1])

実行後の出力は次のとおりです。

[[x_0, x_5],
 [x_1, x_6],
 [x_2, x_7]]
[[x_3, x_8],
 [x_4, x_9]]

  SX によって作成された変数の場合、この代替方法は完全に同等ですが、MX プロットの場合、すべての分割式が必要な場合は horzsplit/vertsplit を使用する方がはるかに効率的です。

1.6.6 内積

  内积、定義は< A , B > : = tr ( AB ) = ∑ i , j A i , j B i , j <A,B>:=tr(AB)=∑_{i,j}A_{ i,j}B_{i,j}<B>:=t r ( A B )= j jB j、次のように作成されます。

x = SX.sym('x',2,2)
print(dot(x,x))

実行後の出力は次のとおりです。

(((sq(x_0)+sq(x_1))+sq(x_2)+sq(x_3))

  上記の演算の多くはスパース クラスに対しても定義されており、vertcat、horzsplit、transpose、加算 (2 つのスパース パターンの和集合を返す)、乗算 (2 つのスパース パターンの共通部分を返す) などの演算の後に返される行列も同様です。まばらな。

1.7 クエリ属性

  適切なメンバー関数を呼び出すことで、行列またはスパース パターンに特定のプロパティがあるかどうかを確認できます。たとえば、行列のサイズをクエリします。

y = SX.sym('y',10,1)
print(y.shape)

実行後の出力は次のとおりです。

(10,1)

  行列 A の属性をクエリするために一般的に使用される操作には、次のようなものがあります。

関数名 関数
A.size1() 行数
A.size2() 列の数
形状 (MATLAB の「サイズ」) 形状、つまりペア (nrow, ncol )
名前() 要素の数、つまり nrow*ncol
A.nnz() 構造内のゼロ以外の要素の数 (密な場合、A.numel() と等しくなります)。
A.sparsity() スパースモードへの参照を取得する
A.is_dense() 密な行列です。つまり、構造ゼロがありません。
A.is_scalar() 行列はスカラーですか、つまり次元は 1×1 ですか?
A.is_column() 行列はベクトル、つまり次元は n から 1 ですか?
A.is_square() 行列は正方行列ですか?
A.is_triu() 上三角行列でしょうか?
A.is_constant() すべての行列エントリは定数ですか?
A.is_integer() 矩阵项都是整数值吗?

1.8 线性代数

  CasADi 支持有限数量的线性代数运算,例如 对于线性方程组的解:

A = MX.sym('A',3,3)
b = MX.sym('b',3)
print(solve(A,b))

运行后输出结果为:

(A\b)

1.9 微积分 - 算法微分

  CasADi 最核心的功能是algorithmic(or automatic)微分 (AD)。 对于函数 f : R N → R M f:\mathbb{R}^N→\mathbb{R}^M f:RNRM:
y = f ( x ) , y=f(x), y=f(x),
前向模式方向导数(forward mode directional derivatives)可用于计算 Jacobian-times-vector 乘积:

y ^ = ∂ f ∂ x x ^ \hat{y} = \frac{\partial f}{\partial x} \hat{x} y^=xfx^
类似地,反向模式方向导数(reverse mode directional derivatives)可用于计算雅可比转置时间向量乘积:
x ^ = ( ∂ f ∂ x ) T y ^ \hat{x} = (\frac{\partial f}{\partial x})^T \hat{y} x^=(xf)Ty^

  正向和反向模式方向导数的计算成本与评估 f(x) 成正比,而与 x 的维数无关。

  CasADi 还能够有效地生成完整的、稀疏的雅可比行列式。 其算法非常复杂,但基本上包括以下步骤:

  • 自动检测 Jacobian 的稀疏模式
  • 使用图着色技术找到构建完整雅可比行列式所需的一些前向和/或方向导数
  • 以数字或符号方式计算方向导数
  • 计算雅可比行列式

Hessian 的计算方法是首先计算梯度,然后执行与上述相同的步骤,以与上述相同的方式计算梯度的雅可比矩阵,同时利用对称性。

1.9.1 句法

  计算雅可比行列式的表达式:

A = SX.sym('A',3,2)
x = SX.sym('x',2)
print(jacobian(A@x,x))

运行后输出结果为:

[[A_0, A_3],
 [A_1, A_4],
 [A_2, A_5]]

  当微分表达式为标量时,还可以计算矩阵意义上的梯度:

print(gradient(dot(A,A),A))

运行后输出结果为:

[[(A_0+A_0), (A_3+A_3)],
 [(A_1+A_1), (A_4+A_4)],
 [(A_2+A_2), (A_5+A_5)]]

请注意,与 jacobian 不同,梯度始终返回一个密集向量。

Hessians 和作为副产品的梯度计算方法如下:

[H,g] = hessian(dot(x,x),x)
print('H:', H)

运行后输出结果为:

H: @1=2,
[[@1, 00],
 [00, @1]]

  对于计算雅可比乘法向量乘积,jtimes 函数——执行前向模式 AD——通常比创建完整雅可比矩阵和执行矩阵向量乘法更有效:

A = DM([[1,3],[4,7],[2,8]])
x = SX.sym('x',2)
v = SX.sym('v',2)
f = mtimes(A,x)
print(jtimes(f,x,v))

运行后输出结果为:

[(v_0+(3v_1)), ((4v_0)+(7v_1)), ((2v_0)+(8*v_1))]

jtimes 函数可选地计算转置雅可比次向量乘积,即反向模式 AD:

w = SX.sym('w',3)
f = mtimes(A,x)
print(jtimes(f,x,w,True))

运行后输出结果为:

[(((2w_2)+(4w_1))+w_0), (((8w_2)+(7w_1))+(3*w_0))]

おすすめ

転載: blog.csdn.net/qq_16775293/article/details/117422579