Vector デモ — 数値ベクトルの活用

Vector<T> は動的サイズの数値ベクトル、 StaticVector<T, N> はコンパイル時にサイズが決まる固定長ベクトルである。 内積 (dot)、ノルム (norm)、正規化 (normalized) など ベクトル演算の基本操作を提供する。

このページでは 3 つのデモを通じて、ベクトル演算の実用的な応用を紹介する。 以下の出力はすべて calx で実際に計算したものである。 ソースコードはページ末尾に掲載しており、 calx をビルドすれば手元で同じ結果を確認できる。

Demo 1: PageRank — べき乗法で Web ページをランキング

Google の PageRank アルゴリズムをべき乗法で計算する。 4 ページの Web グラフに対してランキングベクトルを反復的に更新し、 各ページの重要度を求める。 ダンピングファクター 0.85、収束判定に norm(next - r) を使用している。

Iterating with damping factor = 0.85 ... Converged after 43 iterations. Page Rankings: Home: 0.3732 ##################################### About: 0.2068 #################### Blog: 0.3825 ###################################### External: 0.0375 ### Sum = 1.0000
Blog が最もランクが高い。これは他の全ページからリンクされているためである。

Demo 2: 3D 光線反射 — dot product で反射ベクトルを計算

光線の 3D 反射を dot product で計算する。 反射公式 $\mathbf{r}' = \mathbf{r} - 2(\mathbf{r} \cdot \mathbf{n})\mathbf{n}$ を StaticVector で実装している。 入射角は $\cos^{-1}(-\mathbf{r} \cdot \mathbf{n})$ で求められる。

Initial ray: ( 0.7071, -0.7071, 0.0000) Bounce 1: Surface normal: ( 0.0000, 1.0000, 0.0000) Angle of incidence: 45.0000 degrees Reflected ray: ( 0.7071, 0.7071, 0.0000) Bounce 2: Surface normal: (-0.7071, 0.7071, 0.0000) Angle of incidence: 90.0000 degrees Reflected ray: ( 0.7071, 0.7071, 0.0000) Bounce 3: Surface normal: ( 0.0000, 0.0000, 1.0000) Angle of incidence: 90.0000 degrees Reflected ray: ( 0.7071, 0.7071, 0.0000)
dot product と正規化だけで 3D の光線追跡が実装できる。

Demo 3: コサイン類似度 — 文書間の意味的距離

文書をベクトル空間モデルで表現し、 コサイン類似度 $\cos\theta = \frac{\mathbf{a} \cdot \mathbf{b}}{|\mathbf{a}||\mathbf{b}|}$ で類似度を計算する。情報検索や機械学習の基本手法である。

Documents (term frequencies: [math, science, art, music, sports]): Algebra Textbook: [9, 3, 0, 0, 0] Physics Paper: [5, 8, 0, 0, 1] Art History: [0, 0, 9, 2, 0] Music Theory: [2, 0, 3, 8, 0] Sports Analytics: [4, 2, 0, 0, 9] Cosine Similarity Matrix: Algebra Physics Art Music Sports Algebra Textbook 1.000 0.767 0.000 0.216 0.441 Physics Paper 0.767 1.000 0.000 0.120 0.472 Art History 0.000 0.000 1.000 0.532 0.000 Music Theory 0.216 0.120 0.532 1.000 0.091 Sports Analytics 0.441 0.472 0.000 0.091 1.000
Algebra Textbook と Physics Paper の類似度が 0.767 で最も高く、 Art History と Sports Analytics は 0.000 で無関係。 ベクトルの角度が文書間の意味的距離を反映する。

ソースコードと実行方法

このページの全出力は以下の C++ プログラムから生成された。 calx をビルドすれば、手元で同じ結果を確認できる。

example_vector_demo.cpp (クリックで展開)
// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// example_vector_demo.cpp
// Vector demo: showcasing Vector's capabilities in 3 scenarios
//
// Build:
//   cl /std:c++latest /EHsc /O2 /MD /utf-8 /I<calx>/include examples\example_vector_demo.cpp /MACHINE:X64

#include <math/core/vector.hpp>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>

using namespace calx;

// ============================================================================
// Demo 1: PageRank -- Ranking Web Pages with the Power Method
//   -- Google's original algorithm, computed with vector iteration
// ============================================================================
static void demo_pagerank() {
    std::cout << "============================================================\n";
    std::cout << " Demo 1: PageRank -- Ranking Web Pages\n";
    std::cout << "============================================================\n\n";

    // A small web graph (4 pages):
    //   Page 0 -> Page 1, Page 2
    //   Page 1 -> Page 2
    //   Page 2 -> Page 0
    //   Page 3 -> Page 0, Page 1, Page 2

    const int N = 4;
    const double damping = 0.85;
    const std::string names[] = {"Home", "About", "Blog", "External"};

    // Build column-stochastic transition matrix (stored as rows for simplicity)
    // M[i][j] = probability of going from page j to page i
    double M[N][N] = {};
    // Page 0 links to 1, 2 (each gets 1/2)
    M[1][0] = 0.5; M[2][0] = 0.5;
    // Page 1 links to 2
    M[2][1] = 1.0;
    // Page 2 links to 0
    M[0][2] = 1.0;
    // Page 3 links to 0, 1, 2 (each gets 1/3)
    M[0][3] = 1.0/3; M[1][3] = 1.0/3; M[2][3] = 1.0/3;

    // Power iteration: r = d * M * r + (1-d)/N
    Vector<double> r(N, 1.0 / N);  // uniform initial
    const double teleport = (1.0 - damping) / N;

    std::cout << "  Iterating with damping factor = " << damping << " ...\n\n";

    for (int iter = 0; iter < 50; ++iter) {
        Vector<double> next(N, teleport);
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                next[i] += damping * M[i][j] * r[j];

        double diff = norm(next - r);
        r = std::move(next);

        if (diff < 1e-10) {
            std::cout << "  Converged after " << (iter + 1) << " iterations.\n\n";
            break;
        }
    }

    // Display rankings
    std::cout << std::fixed << std::setprecision(4);
    std::cout << "  Page Rankings:\n";
    for (int i = 0; i < N; ++i) {
        int bar_len = static_cast<int>(r[i] * 100);
        std::cout << "    " << std::setw(10) << names[i] << ": " << r[i]
                  << "  " << std::string(bar_len, '#') << "\n";
    }
    std::cout << "  Sum = " << (r[0] + r[1] + r[2] + r[3]) << "\n\n";
}

// ============================================================================
// Demo 2: Ray Reflection in 3D -- Light Bouncing Off Mirrors
//   -- Using dot product and cross product for geometric computation
// ============================================================================
static void demo_ray_reflection() {
    std::cout << "============================================================\n";
    std::cout << " Demo 2: Ray Reflection in 3D\n";
    std::cout << "============================================================\n\n";

    // Incident ray direction (pointing downward at 45 degrees)
    StaticVector<double, 3> ray{1.0, -1.0, 0.0};
    ray = ray.normalized();

    // Mirror surface normals for 3 bounces
    StaticVector<double, 3> normals[] = {
        StaticVector<double, 3>{0.0, 1.0, 0.0},                               // horizontal floor
        StaticVector<double, 3>{-1.0, 1.0, 0.0}.normalized(),                 // tilted wall
        StaticVector<double, 3>{0.0, 0.0, 1.0},                               // vertical wall (z)
    };

    auto print_v3 = [](const char* label, const StaticVector<double, 3>& v) {
        std::cout << "    " << std::setw(16) << label << ": ("
                  << std::setw(7) << v[0] << ", "
                  << std::setw(7) << v[1] << ", "
                  << std::setw(7) << v[2] << ")\n";
    };

    std::cout << std::fixed << std::setprecision(4);
    print_v3("Initial ray", ray);

    for (int bounce = 0; bounce < 3; ++bounce) {
        const auto& n = normals[bounce];

        // Reflection formula: r' = r - 2(r . n)n
        double dn = dot(ray, n);
        StaticVector<double, 3> reflected;
        for (int i = 0; i < 3; ++i)
            reflected[i] = ray[i] - 2.0 * dn * n[i];

        // Angle of incidence
        double angle = std::acos(-dn) * 180.0 / 3.14159265358979;

        std::cout << "\n  Bounce " << (bounce + 1) << ":\n";
        print_v3("Surface normal", n);
        std::cout << "    Angle of incidence: " << angle << " degrees\n";
        print_v3("Reflected ray", reflected);

        ray = reflected;
    }
    std::cout << std::endl;
}

// ============================================================================
// Demo 3: Cosine Similarity -- How Similar Are Two Vectors?
//   -- A fundamental operation in information retrieval and machine learning
// ============================================================================
static void demo_cosine_similarity() {
    std::cout << "============================================================\n";
    std::cout << " Demo 3: Cosine Similarity -- Document Comparison\n";
    std::cout << "============================================================\n\n";

    // Term frequency vectors for 5 documents
    // Dimensions: [math, science, art, music, sports]
    struct Doc {
        const char* name;
        Vector<double> tf;
    };

    Doc docs[] = {
        {"Algebra Textbook",    Vector<double>{9.0, 3.0, 0.0, 0.0, 0.0}},
        {"Physics Paper",       Vector<double>{5.0, 8.0, 0.0, 0.0, 1.0}},
        {"Art History",         Vector<double>{0.0, 0.0, 9.0, 2.0, 0.0}},
        {"Music Theory",        Vector<double>{2.0, 0.0, 3.0, 8.0, 0.0}},
        {"Sports Analytics",    Vector<double>{4.0, 2.0, 0.0, 0.0, 9.0}},
    };
    const int nd = 5;

    std::cout << "  Documents (term frequencies: [math, science, art, music, sports]):\n";
    for (auto& d : docs) {
        std::cout << "    " << std::setw(20) << d.name << ": [";
        for (size_t i = 0; i < d.tf.size(); ++i)
            std::cout << (i ? ", " : "") << static_cast<int>(d.tf[i]);
        std::cout << "]\n";
    }

    // Cosine similarity matrix
    const char* abbr[] = {"Algebra", "Physics", "Art", "Music", "Sports"};
    std::cout << "\n  Cosine Similarity Matrix:\n\n";
    std::cout << std::setw(22) << "";
    for (int i = 0; i < nd; ++i) std::cout << std::setw(10) << abbr[i];
    std::cout << "\n";

    std::cout << std::fixed << std::setprecision(3);
    for (int i = 0; i < nd; ++i) {
        std::cout << "    " << std::setw(18) << docs[i].name;
        for (int j = 0; j < nd; ++j) {
            double sim = dot(docs[i].tf, docs[j].tf) / (norm(docs[i].tf) * norm(docs[j].tf));
            std::cout << std::setw(10) << sim;
        }
        std::cout << "\n";
    }

    std::cout << "\n  Most similar pair: Algebra Textbook <-> Physics Paper\n";
    std::cout << "  Most different:    Art History <-> Sports Analytics\n";
    std::cout << std::endl;
}

// ============================================================================
// main
// ============================================================================
int main() {
    std::cout << "###########################################################\n";
    std::cout << "#  calx Vector Demo                                       #\n";
    std::cout << "###########################################################\n\n";

    demo_pagerank();
    demo_ray_reflection();
    demo_cosine_similarity();

    std::cout << "###########################################################\n";
    std::cout << "#  All demos completed                                    #\n";
    std::cout << "###########################################################\n";

    return 0;
}

API の詳細は Vector API リファレンス を参照のこと。

ビルドと実行

cd calx
mkdir build && cd build
cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build . --config Release --target example-vector-demo
examples\Release\example-vector-demo.exe