C++で初等関数を扱う(クラスの使用法)
はじめに
C++ですべての数学初等関数を扱うためのクラスSpFuncを実装して公開したので、この記事ではその使い方を紹介していきます。実装経緯や実装手法は別記事の方で書いています。
別記事: SpFuncクラスの実装経緯と実装手法
コード:ヘッダファイルとソースファイル
クラスの概要
前項で書いたように今回作成したクラスSpFuncは初等関数を統一的に扱うことを目的として作成しており、多変数関数の生成・代入操作・解析的偏微分を、C++上で数学表記に近い形で書いて行うために実装しました。
使用も改変も自由にして頂いて大丈夫ですが、もしバグっぽい挙動が起きたら教えて頂けると幸いです。
使用のための準備
本コードはstd::variantを使っているので、C++17以降でないと動作しません。もしCUDA C++で使いたければコンパイルオプションに「-std c++17」が必要だったと思います。
SpFuncクラスを使う場合はまずヘッダファイル SuperFunc.hpp をインクルードしてください。
#include "SuperFunc.hpp"
またこれは必須ではないですが、SpFuncは名前空間spfunc内で定義されているので
using namespace spfunc;
と書いておくと楽です
関数の生成
関数を生成するときには基本的に数値と変数を組み合わせて表現します。数値はdouble型で、変数はVariableというenum class型で定義されています。そのため変数を宣言して使いたい場合は
auto x = spfunc::Variable::_X;
auto t = spfunc::Variable::_A;
のように書きます。あらかじめ用意しているのは _A,_B,_C,_D,_E,_X,_Y,_Z の8種類なので、9変数以上使いたい場合はヘッダファイルに適宜追加して使ってください。
また初等関数表現のため指数関数・対数関数・三角関数・逆三角関数・双曲線関数・逆双曲線関数計14種類が用意されています。
SpFunc exp(const SpFunc&); //自然指数関数
SpFunc log(const SpFunc&); //自然対数関数
SpFunc sin(const SpFunc&); //正弦関数
SpFunc cos(const SpFunc&); //余弦関数
SpFunc tan(const SpFunc&); //正接関数
SpFunc asin(const SpFunc&); //逆正弦関数
SpFunc acos(const SpFunc&); //逆余弦関数
SpFunc atan(const SpFunc&); //逆正接関数
SpFunc sinh(const SpFunc&); //双曲線正弦関数
SpFunc cosh(const SpFunc&); //双曲線余弦関数
SpFunc tanh(const SpFunc&); //双曲線正接関数
SpFunc asinh(const SpFunc&); //逆双曲線正弦関数
SpFunc acosh(const SpFunc&); //逆双曲線余弦関数
SpFunc atanh(const SpFunc&); //逆双曲線正接関数
では実際に関数を生成するときですが、算術演算子を用いて定数・変数・関数を結合することで表現します。具体例を見た方が早いと思うので
spfunc::SpFunc f = (spfunc::Variable::_X)^(1. - 0.5 * spfunc::sin(spfunc::Variable::_Y));
これは一行で書きましたが、数行に分けて書くと以下のように書けます。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
SpFunc f = x ^ (1.0 - 0.5 * sin(y));
ここで本来^は排他的論理和を表すbit演算子ですが、冪乗を表す演算子として定義しています。ただしそのせいで優先順位が加減算より下です。また+=,-=,*=,/=,^=のような複合演算子も定義されています。
1つ注意点として
using namespace spfunc;
auto x = Variable::_X;
SpFunc g = (x ^ 2.0)^(0.5);
のように関数
数値の代入
代入の仕方の前に変数群VarTableというものの表記法について書きます。
SpFuncでは多変数関数を扱えるので、各変数の値をまとめておくためにVarTableを使います。VarTableはstd::map<Variable,double>のエイリアスなので、std::mapの表記法がそのまま使えます。例えば
spfunc::VarTable data1{
{spfunc::Variable::_X,0.5},
{spfunc::Variable::_Y,-1.3},
};
またVarTable同士の加減算、定数倍にも対応しているので、変数ベクトルのように扱えます。
代入操作を行うには、operator()に引数としてVarTableを渡すことで、その値を各変数に代入した計算結果がdouble型で返ってきます。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
spfunc::VarTable data1{
{x,0.5},
{y,-1.3},
};
SpFunc f = x ^ (1.0 - 0.5 * sin(y));
f(data1) // (=0.358047…)
SpFuncクラスが引数に渡していない変数を持っていた場合、未定義動作となります。
ただし1変数関数で毎回VarTableを用意するのは面倒なので、operator()に引数としてdouble型を渡すと変数にその値を代入して計算します。多変数関数の場合にはすべての変数に引数を代入した場合の計算結果が返ってきます。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
SpFunc f = x ^ (1.0 - 0.5 * sin(y));
f(2) // (=1.45938…)
代入操作の際の計算は関数が複雑であるほど計算回数が増えるために誤差が増えます。指数関数・三角関数といった各種数学関数は標準ライブラリの<cmath>を用いて計算しているので、計算精度はそちらと同じです。
その他の処理
1. 導関数
SpFuncクラスはすべて解析的偏微分が行えます。メンバ関数Diff()に引数として変数Variableを渡すと、その変数で偏微分した導関数が戻り値として返ってきます。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
SpFunc h1 = (2.0*x + y)^2;
SpFunc h2 = h1.Diff(x); // ( =4.0*(2.0*x+y) )
2. 誤差計算
で与えられます。
AbsError(),RelError()を用いるとこの式を用いてそれぞれ絶対誤差,相対誤差を求めることができます。第1引数は多変数関数
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
auto z = Variable::_Z;
SpFunc F = (x + z)/(x + y);
VarTable maindata{
{x,5.0},
{y,1.0},
{z,2.0},
};
VarTable errdata{
{x,0.2},
{y,0.1},
{z,0.15},
};
std::cout << "AbsError : " << AbsError(F,maindata,errdata) << std::endl;
std::cout << "RelError : " << RelError(F,maindata,errdata) << std::endl;
3. 関数表示
SpFuncクラスでは内部的に関数を逆ポーランド記法で管理しており、RPN_Output()に引数としてSpFuncを渡すと、std::string型で関数の逆ポーランド記法表示が返ってきます。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
SpFunc f = x ^ (1.0 - 0.5 * sin(y));
std::cout << RPN_Output(f) << std::endl; // ( X 1 Y sin 0.5 * - ^ )
4. 関数群
多変数から多変数への写像を考えるとき、その関係は
と表せるので、
例として関数群FuncGroupを表現行列のように利用すると次のようになります。
using namespace spfunc;
auto x = Variable::_X;
auto y = Variable::_Y;
auto z = Variable::_Z;
SpFunc f1 = x + y + z;
SpFunc f2 = x + y - 2.0*z;
SpFunc f3 = x - y;
VarTable data3{
{x,1.0},
{y,2.0},
{z,3.0},
};
FuncGroup G;
G[x] = f1; G[y] = f2; G[z] = f3;
VarTable data4 = G(data3);
おわりに
今回作ったクラスSpFuncはプログラミング上で多くの関数を簡単に取り扱うために作ったので、汎用性は高いと思います。関数を扱うときに一人でも多くの人に使って頂ければ幸いです。
Discussion