2016年9月12日月曜日

非退化内積と計量テンソルの関係

概要

この記事では、「計量ベクトル空間に非退化内積が入っているとき、計量テンソルの行列は逆行列をもつ」という主張を証明する。 それを以下の3ステップで行う。
  1. 基底について証明すればよい
  2. 内積が非退化ならば計量テンソルの行列式はゼロでない
  3. 計量ベクトル空間に非退化内積が入っているとき、計量テンソルの行列は逆行列をもつ

1. 基底について証明すればよい

「任意のベクトルについて・・・」と「基底について・・・」が同値であるという、線形代数でよく使われる考え方を証明する。

まずは、非退化内積の定義を思い出す。
上のベクトル空間 V において、<・|・> : V×V → の関数が
xy∈ V で a ∈ R のとき

1. 双線形性
  • <ax + y | z> = a <x | z> + <y | z>
  • <x | ay + z> = a <x | y> + <x | z>
2. 対称性
  • <x | y> = <y | x>

3. 非退化性
  • x [ <x | z> = 0 ] ⇒ z = 0

をみたす場合、非退化内積という。


この非退化性について次の主張を証明したい。

「任意にVの基底{e1, … , en}をとった場合、

x [ <x | z> = 0 ]



∀i [ <ei | z> = 0 ]

同値である。」


(証明)
任意に基底{e1, … , en}をとる。 どんなベクトルx (∈ V) も、
x = x1e1 + ... + xnen
と表せる。

x [ <x | z> = 0 ] を仮定すると、



となり、xiは任意の実数なので、

<ei|z> = 0

が成り立つ。

逆も容易にいえる(基底の定義を使えばよい)。


2. 内積が非退化ならば計量テンソルの行列式はゼロでない



が成り立つことをいうのだが、上で考えたことから

を証明すればよい(記号→と⇒は「ならば」を表す)。

そして直接これを示すのではなく、対偶の

を示す(記号∧は「かつ」を表す)。

(証明)


を仮定する。これは、



において、x1 = x2 = … = xn = 0 でない x1 , x2 , … , xn が存在することと同値(この記事では、丸括弧()を行列を表す記号として、角括弧[]を全称記号∀や存在記号∃の適用範囲を表すものとして用いている)。
さらに言い換えると、




ここで

とおくと



となるzが存在することと同じ。
これは証明したかった結論
を意味する。(証明終わり)


(逆の証明の概略)
上記の証明は同値変形をしているので、逆も成り立つ。
直接証明したい場合は




を仮定して、z = 0 を導けばよい。このとき

とおくのがポイント。(逆の証明の概略終わり)


よく gij = < ei | ej > と表記され、計量テンソルの行列式を



のように表す。そのような文脈では

が使われるが、この式の分母が零にならないことが内積の非退化性から保証されることになる。


3.計量ベクトル空間に非退化内積が入っているとき、計量テンソルの行列は逆行列をもつ

計量テンソルの行列式が、


をみたすことと、計量テンソルの行列が逆行列をもつことは同値である。
よって、「計量ベクトル空間に非退化内積が入っているとき、計量テンソルの行列は逆行列をもつ」 ことがいえた。

よく計量テンソル gij の逆行列 gij が定義されるが、ベクトル空間に非退化内積を入れれば gij の存在が保証されることが分かる。

2016年5月30日月曜日

CUPS プリンタサーバを使いPDFに変換する


今回のプリンタサーバの設定はUbuntu16.04で行った。

CUPS-PDFのインストールと設定

他のPCなどからPDFに変換できるよう、CUPS-PDFをプリンタサーバにインストールする。
次のコマンドをプリンタサーバで実行する。

$ sudo apt-get install cups-pdf

その後「プリンター」を開くと、「PDF」というプリンターが追加されている。右クリックで「共有」にチェックを入れると、ネットワーク上の他のデバイスからでもPDF出力できるようになる。



PDFに変換する方法

プリンタサーバにしたPCから、 「PDF」というプリンターへ印刷すれば、

~/PDF

にPDFとして出力される。

iOSなどで印刷の時に「PDF」というプリンターへ印刷すれば、プリンタサーバの

 /var/spool/cups-pdf

にPDFが出力される。

2016年5月16日月曜日

C++14による正方行列クラスの作成(その6)

前回は行列式と余因子行列を定義した。
最後の今回は、転置行列、逆行列、要素型との除算を実装する。また、いくつかのis関数を実装する。
今回のプログラムの実行にはBoostライブラリを必要とする。

// matrix.cpp
#include <iostream>
#include <initializer_list>
#include <stdexcept>
#include <boost/rational.hpp>



template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  算術代入演算子
  //
  //------------------------------------------
  Matrix& operator +=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] += other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator -=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] -= other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator *=(const T& a) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] *= a;
      }
    }
    return *this;
  }

  Matrix& operator *=(const Matrix& other) & {
    Matrix tmp = *this * other;
    using std::swap;
    swap(*this, tmp);
    return *this;
  }

  Matrix& operator /=(const T& a) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] /= a;
      }
    }
    return *this;
  }

  Matrix&& operator +=(const Matrix& other) && = delete;
  Matrix&& operator -=(const Matrix& other) && = delete;
  Matrix&& operator *=(const T& a) && = delete;
  Matrix&& operator *=(const Matrix& other) && = delete;
  Matrix&& operator /=(const T& a) && = delete;


public:
  //------------------------------------------
  //
  //  算術二項演算子
  //
  //------------------------------------------
  friend Matrix operator +(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret += rhs;
  }

  friend Matrix operator -(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret -= rhs;
  }

  friend Matrix operator *(const Matrix& lhs, const T& rhs) {
    Matrix ret(lhs);
    return ret *= rhs;
  }

  friend Matrix operator *(const T& lhs, const Matrix& rhs) {
    return rhs *= lhs;
  }

  friend Matrix operator *(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        for (int i = 0; i < N; i++) {
          ret.elements_[iRow][iColumn] += lhs.elements_[iRow][i] * rhs.elements_[i][iColumn];
        }
      }
    }
    return ret;
  }

  friend Matrix operator /(const Matrix& lhs, const T& rhs) {
    Matrix ret(lhs);
    return ret /= rhs;
  }


public:
  //------------------------------------------
  //
  // その他のメンバ関数
  //
  //------------------------------------------

  // iRow行とiCokumn列を除いた成分を行列にして返す。
  Matrix<T, N - 1> minor_matrix(int iRow, int iColumn) const {
    static_assert(N > 1, "");

    Matrix<T, N - 1> ret;
    for (int iRR = 0, i = -1; iRR < N; iRR++) {
      if (iRR == iRow) continue;
      i++;
      for (int iCC = 0, j = -1; iCC < N; iCC++) {
        if (iCC == iColumn) continue;
        j++;
        ret(i, j) = elements_[iRR][iCC];
      }
    }
    return ret;
  }

  // 零行列ならtrue
  bool is_zero() const {
    for (int iRow = 0; iRow < N; iRow++)
      for(int iColumn = 0; iColumn < N; iColumn++) {
        if (elements_[iRow][iColumn] != 0)
          return false;
      }
    return true;
  }

  // 対角行列ならtrue
  bool is_diag() const {
    for (int iRow = 0; iRow < N; iRow++)
      for(int iColumn = 0; iColumn < N; iColumn++) {
        if (iRow == iColumn)
          continue;
        if (elements_[iRow][iColumn] != 0)
          return false;
      }
    return true;
  }

  // 零因子ならtrue
  bool is_zerodevisor() const {
    return det(*this) == static_cast(0);
  }


public:
  //------------------------------------------
  //
  //  論理演算子
  //
  //------------------------------------------
  friend bool operator ==(const Matrix& lhs, const Matrix& rhs) {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        if (lhs.elements_[iRow][iColumn] != rhs.elements_[iRow][iColumn])
          return false;
      }
    }
    return true;
  }

  friend bool operator !=(const Matrix& lhs, const Matrix& rhs) {
    return !(lhs == rhs);
  }


  //------------------------------------------
  //
  // iRow行とiCokumn列を除いた成分を行列にして返す。
  //
  //------------------------------------------
  Matrix<T, N - 1> minor_matrix(int iRow, int iColumn) const {
    static_assert(N > 1, "");

    Matrix<T, N - 1> ret;
    for (int iRR = 0, i = -1; iRR < N; iRR++) {
      if (iRR == iRow) continue;
      i++;
      for (int iCC = 0, j = -1; iCC < N; iCC++) {
        if (iCC == iColumn) continue;
        j++;
        ret(i, j) = elements_[iRR][iCC];
      }
    }
    return ret;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


//------------------------------------------
//
// (i, j)成分の余因子を求める。
//
//------------------------------------------
template <typename T, int N> inline
T cofactor(const Matrix<T, N>& m, int iRow, int iColumn) {
  T sign = (iRow + iColumn) % 2 == 0 ? 1 : -1;
  return sign * det(m.minor_matrix(iRow, iColumn));
}


//------------------------------------------
//
// 行列式を求めるときのヘルパー。
// 余因子展開で再帰的(detとcofactorが相互に呼ぶ)に計算する。
// 部分的特殊化により、 N == 1 を特別な動作にしたい。これはそのために必要。
// (関数テンプレートは部分的特殊化ができないが、クラステンプレートならできる)
//
//------------------------------------------
template <typename T, int N>
struct Det {
  inline static T det(const Matrix<T, N>& matrix) {
    T ret = 0;
    for (int iColumn = 0; iColumn < N; iColumn++) {
      ret += matrix(0, iColumn) * cofactor(matrix, 0, iColumn);
    }
    return ret;
  }
};


template<typename T>
struct Det<T, 1> {
  inline static T det(const Matrix<T, 1>& matrix) {
    return matrix(0, 0);
  }
};


//------------------------------------------
//
// 行列式を求める
//
//------------------------------------------
template <typename T, int N> inline
T det(const Matrix<T, N>& matrix) {
  return Det<T, N>::det(matrix);
}


//------------------------------------------
//
// 余因子行列を求める
//
//------------------------------------------
template <typename T, int N> inline
Matrix<T, N> cofactor_matrix(const Matrix<T, N>& m) {
  Matrix<T, N> ret;
  for (int iRow = 0; iRow < N; iRow++) {
    for(int iColumn = 0; iColumn < N; iColumn++) {
      //ここで行と列を入れ替えているため、転置したことになっている。
      ret(iRow, iColumn) = cofactor(m, iColumn, iRow);
    }
  }
  return ret;
}


//------------------------------------------
//
// 転置行列を求める
//
//------------------------------------------
template <typename T, int N> inline
Matrix<T, N> t(const Matrix<T, N>& m) {
  Matrix<T, N> ret;
  for (int iRow = 0; iRow < N; iRow++) {
    for(int iColumn = 0; iColumn < N; iColumn++) {
      ret(iRow, iColumn) = m(iColumn, iRow);
    }
  }
  return ret;
}


//------------------------------------------
//
// 逆行列が存在しない場合の例外
//
//------------------------------------------
class ZeroDivisorException : public std::exception {
public:
  const char* what() const noexcept {
    return "Zero divisor cannot create inverse matrix.";
  }
};


//------------------------------------------
//
// 逆行列を求める
//
//------------------------------------------
template <typename T, int N> inline
Matrix<T, N> inverse(const Matrix<T, N>& m) {
  if (det(m) == 0) {
    throw ZeroDivisorException();
  }
  T d = det(m);
  Matrix<T, N> co_mat = cofactor_matrix(m);
  return co_mat / d;
}


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};

  try {
    Matrix<int, 3> z = inverse(x);
  } catch (ZeroDivisorException& e) {
    std::cout << e.what() << std::endl;
  }

  std::cout << x / 9 << std::endl << std::endl;


  using boost::rational;
  using M = Matrix<rational<int>, 3>;

  M a{{1,0,4},{3,-1,0},{-2,1,-1}};

  try {
    M b = inverse(a);
    std::cout << b << std::endl;
    std::cout << a * b << std::endl;
  } catch (ZeroDivisorException& e) {
    std::cout << e.what() << std::endl;
  }
  return 0;
}


実行結果は

Zero divisor cannot create inverse matrix. //零因子の逆行列は存在しない。
[[0,0,0],[0,0,0],[0,0,1]] //要素型がintの場合、intとintの除算が反映される。商が整数になり余りが切り捨てられる実装のため注意が必要。

[[1/5,4/5,4/5],[3/5,7/5,12/5],[1/5,-1/5,-1/5]] //要素型を分数にすることもできる。
[[1/1,0/1,0/1],[0/1,1/1,0/1],[0/1,0/1,1/1]]

のようになる。


気が向いたら、constexprや、pImplで実装をしてみたい。

2016年5月9日月曜日

C++14による正方行列クラスの作成(その5)

前回は除算以外の算術二項演算子と論理演算子を定義した。
今回で、いよいよ行列式と余因子行列を求める。
この実装は余因子展開を使うため、行列の一時オブジェクトをたくさん使う。しかし、要素型が整数の行列でもその行列式の値を浮動小数点型に拡張せずに、要素型で求められる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  算術代入演算子
  //
  //------------------------------------------
  Matrix& operator +=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] += other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator -=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] -= other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator *=(const T& a) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] *= a;
      }
    }
    return *this;
  }

  Matrix& operator *=(const Matrix& other) & {
    Matrix tmp = *this * other;
    using std::swap;
    swap(*this, tmp);
    return *this;
  }

  Matrix&& operator +=(const Matrix& other) && = delete;
  Matrix&& operator -=(const Matrix& other) && = delete;
  Matrix&& operator *=(const T& a) && = delete;
  Matrix&& operator *=(const Matrix& other) && = delete;


public:
  //------------------------------------------
  //
  //  算術二項演算子
  //
  //------------------------------------------
  friend Matrix operator +(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret += rhs;
  }

  friend Matrix operator -(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret -= rhs;
  }

  friend Matrix operator *(const Matrix& lhs, const T& rhs) {
    Matrix ret(lhs);
    return ret *= rhs;
  }

  friend Matrix operator *(const T& lhs, const Matrix& rhs) {
    return rhs *= lhs;
  }

  friend Matrix operator *(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        for (int i = 0; i < N; i++) {
          ret.elements_[iRow][iColumn] += lhs.elements_[iRow][i] * rhs.elements_[i][iColumn];
        }
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  論理演算子
  //
  //------------------------------------------
  friend bool operator ==(const Matrix& lhs, const Matrix& rhs) {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        if (lhs.elements_[iRow][iColumn] != rhs.elements_[iRow][iColumn])
          return false;
      }
    }
    return true;
  }

  friend bool operator !=(const Matrix& lhs, const Matrix& rhs) {
    return !(lhs == rhs);
  }


  //------------------------------------------
  //
  // iRow行とiCokumn列を除いた成分を行列にして返す。
  //
  //------------------------------------------
  Matrix<T, N - 1> minor_matrix(int iRow, int iColumn) const {
    static_assert(N > 1, "");

    Matrix<T, N - 1> ret;
    for (int iRR = 0, i = -1; iRR < N; iRR++) {
      if (iRR == iRow) continue;
      i++;
      for (int iCC = 0, j = -1; iCC < N; iCC++) {
        if (iCC == iColumn) continue;
        j++;
        ret(i, j) = elements_[iRR][iCC];
      }
    }
    return ret;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


//------------------------------------------
//
// (i, j)成分の余因子を求める。
//
//------------------------------------------
template <typename T, int N> inline
T cofactor(const Matrix<T, N>& m, int iRow, int iColumn) {
  T sign = (iRow + iColumn) % 2 == 0 ? 1 : -1;
  return sign * det(m.minor_matrix(iRow, iColumn));
}


//------------------------------------------
//
// 行列式を求めるときのヘルパー。
// 余因子展開で再帰的(detとcofactorが相互に呼ぶ)に計算する。
// 部分的特殊化で N == 1 の動きを変えるために必要。
// (関数テンプレートは部分的特殊化ができないが、クラステンプレートならできる)
//
//------------------------------------------
template <typename T, int N>
struct Det {
  inline static T det(const Matrix<T, N>& matrix) {
    T ret = 0;
    for (int iColumn = 0; iColumn < N; iColumn++) {
      ret += matrix(0, iColumn) * cofactor(matrix, 0, iColumn);
    }
    return ret;
  }
};


template<typename T>
struct Det<T, 1> {
  inline static T det(const Matrix<T, 1>& matrix) {
    return matrix(0, 0);
  }
};


//------------------------------------------
//
// 行列式を求める
//
//------------------------------------------
template <typename T, int N> inline
T det(const Matrix<T, N>& matrix) {
  return Det<T, N>::det(matrix);
}


//------------------------------------------
//
// 余因子行列を求める
//
//------------------------------------------
template <typename T, int N> inline
Matrix<T, N> cofactor_matrix(const Matrix<T, N>& m) {
  Matrix<T, N> ret;
  for (int iRow = 0; iRow < N; iRow++) {
    for(int iColumn = 0; iColumn < N; iColumn++) {
      //ここで行と列を入れ替えているため、転置したことになっている。
      ret(iRow, iColumn) = cofactor(m, iColumn, iRow);
    }
  }
  return ret;
}


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};
  Matrix<int, 3> y = cofactor_matrix(x);

  std::cout << det(x) << std::endl;
  std::cout << y << std::endl;
  std::cout << x * y << std::endl;

  return 0;
}


実行結果は

0
[[-3,6,-3],[6,-12,6],[-3,6,-3]]
[[0,0,0],[0,0,0],[0,0,0]]

のようになる。

det(x)が0なので、余因子行列が零因子になっていることがわかる。

次回は、転置行列、要素型での除算、逆行列、いくつかのis関数を定義してみたい。
ずいぶんと、ソースが長くなってしまった。

2016年4月28日木曜日

C++14による正方行列クラスの作成(その4)

前回は要素にアクセスするための演算子と算術単項演算子を定義した。
今回は除算以外の算術二項演算子と論理演算子を実装してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  算術代入演算子
  //
  //------------------------------------------
  Matrix& operator +=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] += other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator -=(const Matrix& other) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] -= other.elements_[iRow][iColumn];
      }
    }
    return *this;
  }

  Matrix& operator *=(const T& a) & {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        elements_[iRow][iColumn] *= a;
      }
    }
    return *this;
  }

  Matrix& operator *=(const Matrix& other) & {
    Matrix tmp = *this * other;
    using std::swap;
    swap(*this, tmp);
    return *this;
  }

  Matrix&& operator +=(const Matrix& other) && = delete;
  Matrix&& operator -=(const Matrix& other) && = delete;
  Matrix&& operator *=(const T& a) && = delete;
  Matrix&& operator *=(const Matrix& other) && = delete;


public:
  //------------------------------------------
  //
  //  算術二項演算子
  //
  //------------------------------------------
  friend Matrix operator +(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret += rhs;
  }

  friend Matrix operator -(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret(lhs);
    return ret -= rhs;
  }

  friend Matrix operator *(const Matrix& lhs, const T& rhs) {
    Matrix ret(lhs);
    return ret *= rhs;
  }

  friend Matrix operator *(const T& lhs, const Matrix& rhs) {
    return rhs *= lhs;
  }

  friend Matrix operator *(const Matrix& lhs, const Matrix& rhs) {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        for (int i = 0; i < N; i++) {
          ret.elements_[iRow][iColumn] += lhs.elements_[iRow][i] * rhs.elements_[i][iColumn];
        }
      }
    }
    return ret;
  }


public:
  //------------------------------------------
  //
  //  論理演算子
  //
  //------------------------------------------
  friend bool operator ==(const Matrix& lhs, const Matrix& rhs) {
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        if (lhs.elements_[iRow][iColumn] != rhs.elements_[iRow][iColumn])
          return false;
      }
    }
    return true;
  }

  friend bool operator !=(const Matrix& lhs, const Matrix& rhs) {
    return !(lhs == rhs);
  }


private:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};
  Matrix<int, 3> y = x;

  y += x;

  std::cout << y << std::endl;
  std::cout << std::boolalpha << (x == (y - x)) << std::endl;

  return 0;
}


実行結果は

[[2,4,6],[8,10,12],[14,16,18]]
true

のようになる。

operator+=()を使って、operator+()を定義するよくある実装を使った。
次回はいよいよ、余因子行列と行列式を定義してみたい。

2016年4月17日日曜日

C++14による正方行列クラスの作成(その3)

前回はコンストラクタとデストラクタ、コピーとムーブのコンストラクタと演算子を定義した。
今回は、要素にアクセスするための関数呼び出し演算子と算術単項演算子を実装してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");


public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix&) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


public:
  //------------------------------------------
  //
  //  要素の参照
  //
  //------------------------------------------
  T& operator ()(int row, int column) {
    return elements_[row][column];
  }

  const T& operator ()(int row, int column) const {
    return elements_[row][column];
  }


public:
  //------------------------------------------
  //
  //  算術単項演算子
  //
  //------------------------------------------
  Matrix operator +() const {
    return Matrix(*this);
  }

  Matrix operator -() const {
    Matrix ret;
    for (int iRow = 0; iRow < N; iRow++) {
      for (int iColumn = 0; iColumn < N; iColumn++) {
        ret.elements_[iRow][iColumn] = -elements_[iRow][iColumn];
      }
    }
    return ret;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  const Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};

  //x(1, 2) = 13;
  std::cout << x(1, 2) << std::endl;

  std::cout << -x << std::endl;

  return 0;
}


実行結果は

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

のようになる。


次回は、算術二項演算子、論理演算子を定義してみたい。

2016年4月11日月曜日

C++14による正方行列クラスの作成(その2)

前回は、Matrixクラスの挿入演算子を定義し、表示できるようにした。 今回はコンストラクタとデストラクタ、コピーとムーブのコンストラクタと演算子を定義してみる。

// matrix.cpp
#include <iostream>
#include <initializer_list>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");

public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


public:
  //------------------------------------------
  //
  //  コンストラクタ、デストラクタ、コピー、ムーブ
  //
  //------------------------------------------
  Matrix() noexcept(noexcept(T())) {}

  Matrix(const T& a) {
    for (int i = 0; i < N; i++)
      elements_[i][i] = a;
  }

  Matrix(std::initializer_list<std::initializer_list<T>> ll) {
    auto itRow = ll.begin();
    for (int iRow = 0; iRow < N && itRow != ll.end(); iRow++, ++itRow) {
      auto l = *itRow;
      auto itColumn = l.begin();
      for (int iColumn = 0; iColumn < N && itColumn != l.end(); iColumn++, ++itColumn) {
        elements_[iRow][iColumn] = *itColumn;
      }
    }
  }

  Matrix(const Matrix& other) noexcept = default;
  Matrix(Matrix&&) noexcept = default;
  ~Matrix() = default;

  Matrix& operator =(const Matrix&) & noexcept = default;
  Matrix& operator =(Matrix&&) & noexcept = default;

  Matrix&& operator =(const Matrix&) && noexcept = delete;
  Matrix&& operator =(Matrix&&) && noexcept = delete;


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x{{1,2,3},{4,5,6},{7,8,9}};
  Matrix<int, 3> y;

  y = x;

  std::cout << x << std::endl;
  std::cout << y << std::endl;

  //Matrix<int, 3>() = x;
  //Matrix<int, 3>() = Matrix<int, 3>();

  return 0;
}


実行結果は

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

のようになる。


Matrix(const T& a)で、要素型から行列に容易に変換できるようにした。

initializer_listを使って初期化ができるようにした。

また、一時オブジェクトへの代入を禁止した(65-66行目)。


次回は、要素にアクセスするためのoperator()と算術単項演算子を定義してみたい。

2016年4月7日木曜日

C++14による正方行列クラスの作成(その1)

C++11,14の機能を使って、正方行列のクラスを定義してみようと思う。
最終的には行列式を求めることや、余因子行列や逆行列を求める関数も作成したい。
constexprは使わないことにし、pImpl実装もムーブの処理が複雑になるため、とりあえず扱わないことにする。 今回は出力のための挿入演算子を定義してみる。

// matrix.cpp
#include <iostream>


template <typename T, int N>
class Matrix {
  static_assert(N > 0, "");

public:
  //------------------------------------------
  //
  //  挿入演算子
  //
  //------------------------------------------
  friend std::ostream& operator <<(std::ostream& os, const Matrix& x) {
    os << "[";
    for (int iRow = 0; iRow < N; iRow++) {
      os << "[";
      for (int iColumn = 0; iColumn < N; iColumn++) {
        os << x.elements_[iRow][iColumn];
        if (iColumn != N - 1)
          os << ",";
      }
      os << "]";
      if (iRow != N - 1)
        os << ",";
    }
    os << "]";
    return os;
  }


protected:
  T elements_[N][N] = {};

};  // class Matrix


int main()
{
  Matrix<int, 3> x;

  std::cout << x << std::endl;

  return 0;
}


これを

g++ -std=c++14 matrix.cpp

のようにコンパイルすると、実行結果は

[[0,0,0],[0,0,0],[0,0,0]]

のようになる。


static_assertにより、Nが正かどうかをコンパイル時に確認する。

作成した行列クラスはクラステンプレートなので、挿入演算子(operator<<)は friend関数の生成イディオムを使うと定義しやすい。

また、C++11以降であれば、メンバ変数を elements_[N][N] = {} として初期化できる。


次回は、コンストラクタ、デストラクタ、そしてコピーとムーブのコンストラクタと代入演算子を定義してみたい。

2016年3月7日月曜日

正定値内積は非退化内積であること

この記事では主に、正定値内積は非退化内積の性質を満たすことを示す。また、逆は成り立たないことを例を挙げて示す。
そして、次の図の関係を示す。



以下では実数の集合をRとする。

内積の定義

上のベクトル空間 V において、<・|・> : V×V → の関数が
xy∈ V で a ∈ R のとき

1. 双線形性
  • <ax + y | z> = a <x | z> + <y | z>
  • <x | ay + z> = a <x | y> + <x | z>
2. 対称性
  • <x | y> = <y | x>

をみたすとき、(この記事では)内積という。

非退化内積の定義

上記に加えて、

3. 非退化性
  • z [ <x | z> = 0 ] ⇒ x = 0

をみたす内積を非退化内積という。
つまり

「どんなベクトルと内積をとっても零になるベクトルは、零ベクトルしかない」

という性質をみたす内積である。


3. 非退化性の対偶
  • x0 ⇒ ∃ z [ <x | z> ≠ 0 ]

ととらえてもよい。これはつまり、

「零ベクトルではない x との内積は、うまくベクトル z を選べば零にならないようにできる」

ということである。

定値内積の定義

1.双線形性と 2.対称性の他に、3.非退化性ではなく、

4. 定値性
  • <x | x> = 0 ⇒ x = 0
をみたす内積を、(この記事では)定値内積という。

正定値内積の定義

1.双線形性、 2.対称性、そして 4. 定値性の他に、

5. 正値性
  • <x | x> ≧ 0
をみたす内積を、正定値内積という。
この定義から直ちに、内積が正定値であれば、定値である。

定値内積は非退化内積であること

内積が「定値」ならば「非退化」であることを示す。

4. 「<x | x> = 0 ⇒ x = 0」と
3.の前件 「∀z [ <x | z> = 0 ]」を仮定する。

3.の前件で特に z = x のとき、<x | z> = <x | x> = 0 である。
よって4.により、「x = 0」が成り立つ。これは3.の結論である。(証明終わり)

以上より、内積が定値なら定値、よって非退化である。

非退化だが定値内積ではない例

4.定値性の否定は、∃x [ <x | x> = 0 かつ x0 ]なので

「∀z [ <x | z> = 0 ] ⇒ x = 0」 かつ ∃x [ <x | x> = 0 かつ x0 ]

をみたす内積<・|・>が存在することを示せばよい。

例1

簡単のため dimV = 2 とし、定値ではない内積を
<e1 | e1> = 1 , <e2 | e2> = 0 (ここが定値ではない) , <e1 | e2> = <e2 | e1> = 1
で定義する。
x = x1e1 + x2e1 , z = z1e1 + z2e2 とすると、(反変ベクトルの成分は添字を上に、基底の添字は下につける慣習に倣った)

<x | z> = x1z1 + x1z2 + x2z1 = (x1 + x2)z1 + x1z2 = 0

これが任意の z で成り立つには、( z1 と z2 についての恒等式なので)

x1 + x2 = 0 かつ x1 = 0

すなわち x = 0
よって非退化である。

例2

よくあるミンコフスキー平面の例。
dimV = 2 とし、定値ではない内積を
<e| e1> = 1 , <e| e2> = -1 , <e| e2> = <e| e1> = 0
で定義すると、

<x | z> = x1z1 - x2z2 = 0

これが任意の z で成り立つには、

x1 = 0 かつ x2 = 0

すなわち x0
よって非退化である。

そして、x = e1 + e2をとると、

<x | x> = 1 - 1 = 0 かつ x ≠ 0

のため、定値でない。

この例から、あるベクトルの大きさは正、あるベクトルの大きさは負となる内積は、<x | x> = 0 となる x を構成できるため、定値でないことがわかる。

定値内積だが正定値ではない例

例3

dimV = 2 とし、内積を
<e1 | e1> = -1 , <e2 | e2> = -1 , <e1 | e2> = <e2 | e1> = 0
で定義すると、

<x | x> = - (x1)2 - (x2)2 ≦ 0

であり、この式から

<x | x> = 0 ⇒ x = 0

も分かるので定値だが正定値ではない。

非退化でない内積の例

ついでに、非退化ですらない内積の例を挙げてみる。

例4

dimV = 2 とし、内積を
<e1 | e1> = 1 , <e2 | e2> = <e1 | e2> = <e2 | e1> = 0
で定義すると、

<x | z> = x1z1 = 0

が任意の z で成り立つとき、 x1 = 0 だが x2 は 0 でなくてもよい。
よって

z [ <x | z> = 0 ] かつ x0

をみたす x が存在する ( x = e2 あるいは e2 の実数倍) ことがいえたので、非退化でない。



(関連のある記事)
内積が非退化であると、計量テンソルに逆行列が存在することがいえる。それは次回