From 59eb1aeb1443c06ea2de6b9eb4c358e2a2526859 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:50:20 -0700 Subject: [PATCH 01/30] wip: add catch2 and 1 == 1 test case --- core/meson.build | 1 - meson.build | 4 ++++ subprojects/catch2.wrap | 11 +++++++++++ tests/core.cpp | 5 +++++ tests/math/matrix.cpp | 5 +++++ tests/meson.build | 4 ++++ 6 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 subprojects/catch2.wrap create mode 100644 tests/core.cpp create mode 100644 tests/math/matrix.cpp create mode 100644 tests/meson.build diff --git a/core/meson.build b/core/meson.build index 640d31c..b0cb99d 100644 --- a/core/meson.build +++ b/core/meson.build @@ -3,4 +3,3 @@ jmlcore = library('jmlcore', core_sources, include_directories: core_inc, install: true) - diff --git a/meson.build b/meson.build index f110dd3..32a4594 100644 --- a/meson.build +++ b/meson.build @@ -3,5 +3,9 @@ project('jml', 'cpp', core_inc = include_directories('core/include') +catch2_proj = subproject('catch2', default_options: {'tests': false}) +catch2_dep = catch2_proj.get_variable('catch2_with_main_dep') + subdir('core') +subdir('tests') diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap new file mode 100644 index 0000000..691d39c --- /dev/null +++ b/subprojects/catch2.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory = Catch2-3.4.0 +source_url = https://github.com/catchorg/Catch2/archive/v3.4.0.tar.gz +source_filename = Catch2-3.4.0.tar.gz +source_hash = 122928b814b75717316c71af69bd2b43387643ba076a6ec16e7882bfb2dfacbb +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz +wrapdb_version = 3.4.0-1 + +[provide] +catch2 = catch2_dep +catch2-with-main = catch2_with_main_dep diff --git a/tests/core.cpp b/tests/core.cpp new file mode 100644 index 0000000..b0a8951 --- /dev/null +++ b/tests/core.cpp @@ -0,0 +1,5 @@ +#include "catch2/catch_test_macros.hpp" + +TEST_CASE("Check sanity with 1 equals 1", "[useless]") { + REQUIRE(1 == 1); +} diff --git a/tests/math/matrix.cpp b/tests/math/matrix.cpp new file mode 100644 index 0000000..dbf9de9 --- /dev/null +++ b/tests/math/matrix.cpp @@ -0,0 +1,5 @@ +#include "catch2/catch_test_macros.hpp" + +TEST_CASE("nothing yet", "[math]") { + REQUIRE(2 == 2); +} \ No newline at end of file diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..79890db --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,4 @@ +test_sources = ['core.cpp', + 'math/matrix.cpp'] +tests = executable('tests', test_sources, dependencies: [catch2_dep]) +test('tests', tests, args: ['--reporter', 'TAP'], protocol: 'tap') From 955b55faa79020adde986b9473544760708c52fc Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 8 Oct 2023 09:53:38 -0700 Subject: [PATCH 02/30] chore: specify minimum meson version --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 32a4594..18f7a57 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,6 @@ project('jml', 'cpp', - default_options: ['cpp_std=c++14']) + default_options: ['cpp_std=c++14'], + meson_version: '>=1.2.0') core_inc = include_directories('core/include') From 00840aa82745fb1df5a0677b3467fa6186ddc07a Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 8 Oct 2023 09:57:21 -0700 Subject: [PATCH 03/30] chore: don't commit catch2 source --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ec85abb..76a469b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ test.cpp # --------------- # .vscode/ # ----------------# + +# Don't commit Catch2 source or wrapdb cache +subprojects/Catch2-3.4.0 +subprojects/packagecache From 3a582c2a6d1db2df67fa6fd0ae665c360fdc97d4 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 8 Oct 2023 11:27:07 -0700 Subject: [PATCH 04/30] wip: add matrix multiplication test --- core/meson.build | 2 ++ tests/core.cpp | 5 ---- tests/core/math/test_matrix.cpp | 41 +++++++++++++++++++++++++++++++++ tests/math/matrix.cpp | 5 ---- tests/meson.build | 12 ++++++---- 5 files changed, 51 insertions(+), 14 deletions(-) delete mode 100644 tests/core.cpp create mode 100644 tests/core/math/test_matrix.cpp delete mode 100644 tests/math/matrix.cpp diff --git a/core/meson.build b/core/meson.build index 6a6e1fa..ce32285 100644 --- a/core/meson.build +++ b/core/meson.build @@ -3,3 +3,5 @@ jmlcore = library('jmlcore', core_sources, include_directories: core_inc, install: true) + +jmlcore_dep = declare_dependency(link_with: jmlcore, include_directories: core_inc) diff --git a/tests/core.cpp b/tests/core.cpp deleted file mode 100644 index b0a8951..0000000 --- a/tests/core.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "catch2/catch_test_macros.hpp" - -TEST_CASE("Check sanity with 1 equals 1", "[useless]") { - REQUIRE(1 == 1); -} diff --git a/tests/core/math/test_matrix.cpp b/tests/core/math/test_matrix.cpp new file mode 100644 index 0000000..5bb9df0 --- /dev/null +++ b/tests/core/math/test_matrix.cpp @@ -0,0 +1,41 @@ +#include "catch2/catch_test_macros.hpp" +#include "jml/math/vector.hpp" +#include +#include + +SCENARIO("A matrix is multiplied with a vector", "[matrix][vector]") { + GIVEN("a matrix and vector with some values") { + jml::Matrix m(2, 3); + jml::Vector V(3); + + // TODO: check row and col + + // Initialize matrix to some values + // TODO: add fuzzing? + // TODO: it would also be nice to test the setters (no getters defined yet) + m.set_entry(0, 0, 1); + m.set_entry(1, 0, 2); + m.set_entry(0, 1, 3); + m.set_entry(1, 1, 4); + m.set_entry(0, 2, 5); + m.set_entry(1, 2, 6); + + // Initialize vector to some values + V.set_entry(0, 7); + V.set_entry(1, 8); + V.set_entry(2, 9); + + WHEN("matrix and vector are multiplied") { + jml::Vector ret = *m.multiply(std::make_unique(V)).get(); + + THEN("vector has length equal to number of rows") { + REQUIRE(ret.get_size() == 2); + } + + THEN("vector values are correct") { + REQUIRE(ret.get_entry(0) == 76); + REQUIRE(ret.get_entry(1) == 100); + } + } + } +} diff --git a/tests/math/matrix.cpp b/tests/math/matrix.cpp deleted file mode 100644 index dbf9de9..0000000 --- a/tests/math/matrix.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "catch2/catch_test_macros.hpp" - -TEST_CASE("nothing yet", "[math]") { - REQUIRE(2 == 2); -} \ No newline at end of file diff --git a/tests/meson.build b/tests/meson.build index 79890db..e28d564 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,4 +1,8 @@ -test_sources = ['core.cpp', - 'math/matrix.cpp'] -tests = executable('tests', test_sources, dependencies: [catch2_dep]) -test('tests', tests, args: ['--reporter', 'TAP'], protocol: 'tap') +core_test_sources = [ + 'core/math/test_matrix.cpp', +] + +tests = executable('core_tests', + core_test_sources, + dependencies: [catch2_dep, jmlcore_dep]) +test('core', tests, args: ['--reporter', 'TAP'], protocol: 'tap') From 569567305cdd6eb07b8689530c12dbd2f02a86e3 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 8 Oct 2023 11:42:38 -0700 Subject: [PATCH 05/30] fix: specify visibility for all symbols --- core/include/jml/jmldefs.h | 32 +++++++++++++++++++ .../include/jml/math/activation_functions.hpp | 12 ++++--- core/include/jml/math/matrix.hpp | 5 +-- core/include/jml/math/vector.hpp | 2 +- core/include/jml/model.hpp | 6 ++-- 5 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 core/include/jml/jmldefs.h diff --git a/core/include/jml/jmldefs.h b/core/include/jml/jmldefs.h new file mode 100644 index 0000000..93a0f5a --- /dev/null +++ b/core/include/jml/jmldefs.h @@ -0,0 +1,32 @@ +// Generic helper definitions for shared library support +#if defined _WIN32 || defined __CYGWIN__ + #define JML_HELPER_DLL_IMPORT __declspec(dllimport) + #define JML_HELPER_DLL_EXPORT __declspec(dllexport) + #define JML_HELPER_DLL_LOCAL +#else + #if __GNUC__ >= 4 + #define JML_HELPER_DLL_IMPORT __attribute__ ((visibility ("default"))) + #define JML_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) + #define JML_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden"))) + #else + #define JML_HELPER_DLL_IMPORT + #define JML_HELPER_DLL_EXPORT + #define JML_HELPER_DLL_LOCAL + #endif +#endif + +// Now we use the generic helper definitions above to define JML_API and JML_LOCAL. +// JML_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) +// JML_LOCAL is used for non-api symbols. + +#ifdef JML_DLL // defined if JML is compiled as a DLL + #ifdef JML_DLL_EXPORTS // defined if we are building the JML DLL (instead of using it) + #define JML_API JML_HELPER_DLL_EXPORT + #else + #define JML_API JML_HELPER_DLL_IMPORT + #endif // JML_DLL_EXPORTS + #define JML_LOCAL JML_HELPER_DLL_LOCAL +#else // JML_DLL is not defined: this means JML is a static lib. + #define JML_API + #define JML_LOCAL +#endif // JML_DLL diff --git a/core/include/jml/math/activation_functions.hpp b/core/include/jml/math/activation_functions.hpp index f719ac5..ea77b92 100644 --- a/core/include/jml/math/activation_functions.hpp +++ b/core/include/jml/math/activation_functions.hpp @@ -5,9 +5,11 @@ #pragma once +#include + namespace jml { -class ActivationFunction { +class JML_API ActivationFunction { public: virtual double f(double x) = 0; virtual double df(double x) = 0; @@ -16,7 +18,7 @@ class ActivationFunction { virtual double df_precomp(); }; -class FastSigmoid : public ActivationFunction { +class JML_API FastSigmoid : public ActivationFunction { // If you know you are going to call f at least once then df at least once // afterwards (In that order!), then you can use f_precomp and df_precomp // f_precomp will compute common values that are shared in df_precomp @@ -39,7 +41,7 @@ class FastSigmoid : public ActivationFunction { double precomputed; }; -class Sigmoid : public ActivationFunction { +class JML_API Sigmoid : public ActivationFunction { public: Sigmoid(); @@ -47,7 +49,7 @@ class Sigmoid : public ActivationFunction { double df(double x); }; -class ReLU : public ActivationFunction { +class JML_API ReLU : public ActivationFunction { public: ReLU(); @@ -55,7 +57,7 @@ class ReLU : public ActivationFunction { double df(double x); }; -class LeakyReLU : public ActivationFunction { +class JML_API LeakyReLU : public ActivationFunction { public: LeakyReLU(); LeakyReLU(double x); diff --git a/core/include/jml/math/matrix.hpp b/core/include/jml/math/matrix.hpp index 18a5119..97a39ab 100644 --- a/core/include/jml/math/matrix.hpp +++ b/core/include/jml/math/matrix.hpp @@ -10,10 +10,11 @@ #include #include +#include namespace jml { -class Matrix { +class JML_API Matrix { int m; // Number of rows int n; // Number of columns @@ -21,7 +22,7 @@ int n; // Number of columns double * entries; // This gets the array position for a particular row and column combination. -int get_position(int i, int j); +int JML_LOCAL get_position(int i, int j); public: Matrix(int m, int n); diff --git a/core/include/jml/math/vector.hpp b/core/include/jml/math/vector.hpp index ba2d02c..58bd47e 100644 --- a/core/include/jml/math/vector.hpp +++ b/core/include/jml/math/vector.hpp @@ -10,7 +10,7 @@ namespace jml { -class Vector { +class JML_API Vector { int length; // The number of entries in the vector diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index bd8bf88..6342749 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -33,7 +33,7 @@ namespace jml { }; */ // TODO: separate into different kinds of layers. -class Model_Layer { +class JML_API Model_Layer { friend class Model; @@ -45,13 +45,13 @@ friend class Model; }; // Transition class for loading in/saving model, includes parameters and activation func ID's (enum <-> func pointer mappings?) -class Model_Layer_Compressed { +class JML_API Model_Layer_Compressed { friend class Model_Layer; }; -class Model { +class JML_API Model { // friend class Model_Layer_Hidden; public: From f435caf1c475804ce030c5d3061bfe0052cf67c1 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:28:33 -0700 Subject: [PATCH 06/30] fix: add include guard --- core/include/jml/jmldefs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/include/jml/jmldefs.h b/core/include/jml/jmldefs.h index 93a0f5a..1222b57 100644 --- a/core/include/jml/jmldefs.h +++ b/core/include/jml/jmldefs.h @@ -1,3 +1,7 @@ +#ifndef JMLDEFS_H +#define JMLDEFS_H + +// stolen from https://gcc.gnu.org/wiki/Visibility // Generic helper definitions for shared library support #if defined _WIN32 || defined __CYGWIN__ #define JML_HELPER_DLL_IMPORT __declspec(dllimport) @@ -30,3 +34,5 @@ #define JML_API #define JML_LOCAL #endif // JML_DLL + +#endif From 2d7772cacf98aa04d36af81c99b8bf9c58ae73a4 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:17:06 -0700 Subject: [PATCH 07/30] feat: add cpp visibility macros to logger --- core/internal/logger.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/internal/logger.hpp b/core/internal/logger.hpp index 5b35147..ca98441 100644 --- a/core/internal/logger.hpp +++ b/core/internal/logger.hpp @@ -7,18 +7,19 @@ #pragma once +#include "jml/jmldefs.h" #include namespace jml { -enum Severity { DEBUG, INFO, WARN, ERR, FATAL }; +enum JML_LOCAL Severity { DEBUG, INFO, WARN, ERR, FATAL }; static const std::string severity_text[] = {"DEBUG", "INFO", "WARN", "ERR", "FATAL"}; -const std::string get_severity_text(enum Severity s); +const JML_LOCAL std::string get_severity_text(enum Severity s); -class Log { +class JML_LOCAL Log { private: enum Severity severity; std::string message; @@ -35,7 +36,7 @@ class Log { Log(enum Severity s, std::string m); }; -class Logger { +class JML_LOCAL Logger { private: enum Severity global_log_level; static Logger *_instance; From f2ed54b75ea5a7611bb940caf4438faf3d7a0428 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:17:33 -0700 Subject: [PATCH 08/30] fix: actually define the visbility macro when building shared lib --- core/meson.build | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/meson.build b/core/meson.build index 0f30001..5eea9a0 100644 --- a/core/meson.build +++ b/core/meson.build @@ -1,5 +1,16 @@ +# set c++ visiblity if building as a shared library +if get_option('default_library') == 'shared' + core_dll_defs = ['-DJML_DLL', '-DJML_DLL_EXPORTS'] +else + core_dll_defs = [] +endif + +# TODO: pkg-config stuff + core_sources = ['src/math/matrix.cpp', 'src/math/vector.cpp', 'src/math/activation_functions.cpp', 'src/model.cpp', 'src/logger.cpp', 'capi/cjml.cpp'] jmlcore = library('jmlcore', core_sources, include_directories: core_inc, - install: true) + install: true, + cpp_args: core_dll_defs, + gnu_symbol_visibility: 'inlineshidden') From b7eb113029327e214473030d14aa1498b7257dc3 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:34:18 -0700 Subject: [PATCH 09/30] fix: add missing include directives i sure hope nothing else is missing and nothing else was messed up by the merge --- core/include/jml/math/vector.hpp | 1 + core/include/jml/model.hpp | 1 + core/internal/logger.hpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/include/jml/math/vector.hpp b/core/include/jml/math/vector.hpp index 823fb7f..c7c1284 100644 --- a/core/include/jml/math/vector.hpp +++ b/core/include/jml/math/vector.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include namespace jml { diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index 6342749..3e5cdff 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -13,6 +13,7 @@ #include #include +#include namespace jml { diff --git a/core/internal/logger.hpp b/core/internal/logger.hpp index ca98441..7aed7d1 100644 --- a/core/internal/logger.hpp +++ b/core/internal/logger.hpp @@ -7,7 +7,7 @@ #pragma once -#include "jml/jmldefs.h" +#include #include namespace jml { From 8949e53617aa48bfcf83f4610dedcfcd562f69e9 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:49:09 -0700 Subject: [PATCH 10/30] wip: refactor meson build --- core/meson.build | 3 +++ meson.build | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/meson.build b/core/meson.build index 22e9472..0c7f3b0 100644 --- a/core/meson.build +++ b/core/meson.build @@ -1,3 +1,6 @@ +# includes +core_inc = include_directories('include', 'capi', 'internal') + # set c++ visiblity if building as a shared library if get_option('default_library') == 'shared' core_dll_defs = ['-DJML_DLL', '-DJML_DLL_EXPORTS'] diff --git a/meson.build b/meson.build index 85e58ac..f203610 100644 --- a/meson.build +++ b/meson.build @@ -2,8 +2,6 @@ project('jml', 'cpp', default_options: ['cpp_std=c++14'], meson_version: '>=1.2.0') -core_inc = include_directories('core/include', 'core/capi') - catch2_proj = subproject('catch2', default_options: {'tests': false}) catch2_dep = catch2_proj.get_variable('catch2_with_main_dep') From 42ccade40a6bca5fcaadc5a6b49a0deed03fcc7c Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:53:12 -0700 Subject: [PATCH 11/30] refactor: move logger.hpp to include directory to enable building --- core/{ => include/jml}/internal/logger.hpp | 0 core/include/jml/math/matrix.hpp | 2 +- core/include/jml/math/vector.hpp | 2 +- core/src/logger.cpp | 2 +- core/src/math/matrix.cpp | 2 +- core/src/math/vector.cpp | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename core/{ => include/jml}/internal/logger.hpp (100%) diff --git a/core/internal/logger.hpp b/core/include/jml/internal/logger.hpp similarity index 100% rename from core/internal/logger.hpp rename to core/include/jml/internal/logger.hpp diff --git a/core/include/jml/math/matrix.hpp b/core/include/jml/math/matrix.hpp index bcebab9..f864c42 100644 --- a/core/include/jml/math/matrix.hpp +++ b/core/include/jml/math/matrix.hpp @@ -11,7 +11,7 @@ #include #include -#include +#include namespace jml { diff --git a/core/include/jml/math/vector.hpp b/core/include/jml/math/vector.hpp index c7c1284..95c0e86 100644 --- a/core/include/jml/math/vector.hpp +++ b/core/include/jml/math/vector.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include namespace jml { diff --git a/core/src/logger.cpp b/core/src/logger.cpp index 2a6d0df..e6bded4 100644 --- a/core/src/logger.cpp +++ b/core/src/logger.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/core/src/math/matrix.cpp b/core/src/math/matrix.cpp index 9e31f7d..88d335c 100644 --- a/core/src/math/matrix.cpp +++ b/core/src/math/matrix.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include diff --git a/core/src/math/vector.cpp b/core/src/math/vector.cpp index d19787a..baf149c 100644 --- a/core/src/math/vector.cpp +++ b/core/src/math/vector.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include From 85f13ba0e87a46d8b8b469798ee6739f0b0454c1 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:49:09 -0700 Subject: [PATCH 12/30] wip: refactor meson build but on this branch --- core/meson.build | 3 +++ meson.build | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/meson.build b/core/meson.build index 5eea9a0..423374a 100644 --- a/core/meson.build +++ b/core/meson.build @@ -1,3 +1,6 @@ +# includes +core_inc = include_directories('include', 'capi', 'internal') + # set c++ visiblity if building as a shared library if get_option('default_library') == 'shared' core_dll_defs = ['-DJML_DLL', '-DJML_DLL_EXPORTS'] diff --git a/meson.build b/meson.build index aac513f..2a3a64b 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,4 @@ project('jml', 'cpp', default_options: ['cpp_std=c++14']) -core_inc = include_directories('core/include', 'core/capi') - subdir('core') From cd80cbe24622b68d4dc8ae4199fbe86fa5247a50 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:00:19 -0700 Subject: [PATCH 13/30] fix: remove non-existent directory from includes --- core/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/meson.build b/core/meson.build index 423374a..263f8b6 100644 --- a/core/meson.build +++ b/core/meson.build @@ -1,5 +1,5 @@ # includes -core_inc = include_directories('include', 'capi', 'internal') +core_inc = include_directories('include', 'capi') # set c++ visiblity if building as a shared library if get_option('default_library') == 'shared' From 8bcebcddef3495274a07fd90811e56bc486a90fb Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:26:10 -0700 Subject: [PATCH 14/30] fix: fix some memory allocation and constructor whoopsies in matrix and vector classes --- core/src/math/matrix.cpp | 4 ++-- core/src/math/vector.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/math/matrix.cpp b/core/src/math/matrix.cpp index 88d335c..96c978e 100644 --- a/core/src/math/matrix.cpp +++ b/core/src/math/matrix.cpp @@ -5,9 +5,9 @@ namespace jml { -Matrix::Matrix(int m, int n) { this->entries = new double(m * n); } +Matrix::Matrix(int m, int n): m(m), n(n) { this->entries = new double[m * n]; } -Matrix::~Matrix() { delete this->entries; } +Matrix::~Matrix() { delete[] this->entries; } int Matrix::get_position(int i, int j) { return i * this->n + j; } diff --git a/core/src/math/vector.cpp b/core/src/math/vector.cpp index baf149c..8d6014e 100644 --- a/core/src/math/vector.cpp +++ b/core/src/math/vector.cpp @@ -10,13 +10,13 @@ Logger *LOGGER = Logger::Instance(); Vector::Vector(int length) { - this->entries = new double(length); + this->entries = new double[length]; memset(this->entries, 0, length * sizeof(double)); this->length = length; } -Vector::~Vector() { delete this->entries; } +Vector::~Vector() { delete[] this->entries; } void Vector::apply(ActivationFunction *fn) { for (int i = 0; i < this->length; ++i) { From 0ab8cad991801ba5401a28634b4813f1032e44eb Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Tue, 10 Oct 2023 23:01:56 -0700 Subject: [PATCH 15/30] wip: test seems to pass after refactor --- core/include/jml/math/matrix.hpp | 2 +- core/include/jml/math/vector.hpp | 4 ++-- core/src/math/matrix.cpp | 8 +++---- core/src/math/vector.cpp | 4 ++-- core/src/model.cpp | 2 +- tests/core/math/test_matrix.cpp | 39 ++++++++++++++++++++++---------- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/core/include/jml/math/matrix.hpp b/core/include/jml/math/matrix.hpp index f864c42..a9eeda5 100644 --- a/core/include/jml/math/matrix.hpp +++ b/core/include/jml/math/matrix.hpp @@ -33,7 +33,7 @@ class JML_API Matrix { int get_n_rows(); int get_n_cols(); void set_entry(int i, int j, double value); - std::unique_ptr multiply(std::unique_ptr); + std::unique_ptr multiply(const Vector& in); }; } // namespace jml diff --git a/core/include/jml/math/vector.hpp b/core/include/jml/math/vector.hpp index 95c0e86..ad528a0 100644 --- a/core/include/jml/math/vector.hpp +++ b/core/include/jml/math/vector.hpp @@ -31,9 +31,9 @@ class JML_API Vector { // This adds every entry in v to this vector. void add(Vector& v); - int get_size(); + int get_size() const; - double get_entry(int pos); + double get_entry(int pos) const; void set_entry(int pos, double val); void add_entry(int pos, double val); // Adds val to the current value in pos. diff --git a/core/src/math/matrix.cpp b/core/src/math/matrix.cpp index 96c978e..055f89d 100644 --- a/core/src/math/matrix.cpp +++ b/core/src/math/matrix.cpp @@ -28,11 +28,11 @@ void Matrix::set_entry(int i, int j, double value) { this->entries[get_position(i, j)] = value; } -std::unique_ptr Matrix::multiply(std::unique_ptr in) { +std::unique_ptr Matrix::multiply(const Vector& in) { - if (in->get_size() != this->n) { + if (in.get_size() != this->n) { LOGGER->log(Log(WARN) << "Tried to multiply a vector of size " - << in->get_size() << " with a matrix containing " + << in.get_size() << " with a matrix containing " << this->n << " columns.\n"); } @@ -40,7 +40,7 @@ std::unique_ptr Matrix::multiply(std::unique_ptr in) { for (int i = 0; i < this->m; ++i) { for (int j = 0; j < this->n; ++j) - ret->add_entry(i, in->get_entry(j) * + ret->add_entry(i, in.get_entry(j) * this->entries[get_position(i, j)]); } diff --git a/core/src/math/vector.cpp b/core/src/math/vector.cpp index 8d6014e..79f3ad9 100644 --- a/core/src/math/vector.cpp +++ b/core/src/math/vector.cpp @@ -35,9 +35,9 @@ void Vector::add(Vector &v) { } } -int Vector::get_size() { return this->length; } +int Vector::get_size() const { return this->length; } -double Vector::get_entry(int pos) { +double Vector::get_entry(int pos) const { if (pos < 0 || pos >= this->length) { LOGGER->log(Log(ERR) << "Position " << pos << " out of bounds.\n"); } diff --git a/core/src/model.cpp b/core/src/model.cpp index a1c8a8e..b11914a 100644 --- a/core/src/model.cpp +++ b/core/src/model.cpp @@ -8,7 +8,7 @@ std::unique_ptr Model::apply(Vector& in) { std::unique_ptr outp; for (Model_Layer ml : this->layers) { - outp = ml.matrix.multiply(std::move(inp)); + outp = ml.matrix.multiply(*inp); inp = std::move(outp); inp->add(ml.bias_vector); inp->apply(ml.act.get()); diff --git a/tests/core/math/test_matrix.cpp b/tests/core/math/test_matrix.cpp index 5bb9df0..03835b9 100644 --- a/tests/core/math/test_matrix.cpp +++ b/tests/core/math/test_matrix.cpp @@ -3,11 +3,17 @@ #include #include -SCENARIO("A matrix is multiplied with a vector", "[matrix][vector]") { +SCENARIO("Matricies are correctly multiplied with vectors", "[matrix][vector]") { GIVEN("a matrix and vector with some values") { jml::Matrix m(2, 3); jml::Vector V(3); + THEN("the matrix and vector have the specified dimensions") { + REQUIRE(m.get_n_rows() == 2); + REQUIRE(m.get_n_cols() == 3); + REQUIRE(V.get_size() == 3); + } + // TODO: check row and col // Initialize matrix to some values @@ -20,22 +26,31 @@ SCENARIO("A matrix is multiplied with a vector", "[matrix][vector]") { m.set_entry(0, 2, 5); m.set_entry(1, 2, 6); - // Initialize vector to some values + WHEN("the vector is initialized"){// Initialize vector to some values V.set_entry(0, 7); V.set_entry(1, 8); V.set_entry(2, 9); - WHEN("matrix and vector are multiplied") { - jml::Vector ret = *m.multiply(std::make_unique(V)).get(); + THEN ("the vector entries are set as specified") { + REQUIRE(V.get_entry(0) == 7); + REQUIRE(V.get_entry(1) == 8); + REQUIRE(V.get_entry(2) == 9); + } + + + AND_WHEN("matrix and vector are multiplied") { + auto ret = m.multiply(V); - THEN("vector has length equal to number of rows") { - REQUIRE(ret.get_size() == 2); - } + THEN("vector has length equal to number of rows") { + REQUIRE(ret->get_size() == 2); - THEN("vector values are correct") { - REQUIRE(ret.get_entry(0) == 76); - REQUIRE(ret.get_entry(1) == 100); - } - } + AND_THEN("resultant vector values are correct") { + REQUIRE(ret->get_entry(0) == 76); + REQUIRE(ret->get_entry(1) == 100); + } + + } + + }} } } From aa3d72f3495dd92314e187efdd3f2bf7c5b2c9c0 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:11:55 -0700 Subject: [PATCH 16/30] feat!: add getter and test for matrix --- core/include/jml/math/matrix.hpp | 9 ++-- core/src/math/matrix.cpp | 20 ++++++-- tests/core/math/test_matrix.cpp | 84 ++++++++++++++++++-------------- 3 files changed, 69 insertions(+), 44 deletions(-) diff --git a/core/include/jml/math/matrix.hpp b/core/include/jml/math/matrix.hpp index a9eeda5..7b75c79 100644 --- a/core/include/jml/math/matrix.hpp +++ b/core/include/jml/math/matrix.hpp @@ -25,15 +25,16 @@ class JML_API Matrix { double *entries; // This gets the array position for a particular row and column combination. - int JML_LOCAL get_position(int i, int j); + int JML_LOCAL get_position(int i, int j) const; public: Matrix(int m, int n); ~Matrix(); - int get_n_rows(); - int get_n_cols(); + int get_n_rows() const; + int get_n_cols() const; void set_entry(int i, int j, double value); - std::unique_ptr multiply(const Vector& in); + double get_entry(const int i, const int j) const; + std::unique_ptr multiply(const Vector& in) const; }; } // namespace jml diff --git a/core/src/math/matrix.cpp b/core/src/math/matrix.cpp index 055f89d..0506596 100644 --- a/core/src/math/matrix.cpp +++ b/core/src/math/matrix.cpp @@ -9,11 +9,11 @@ Matrix::Matrix(int m, int n): m(m), n(n) { this->entries = new double[m * n]; } Matrix::~Matrix() { delete[] this->entries; } -int Matrix::get_position(int i, int j) { return i * this->n + j; } +int Matrix::get_position(int i, int j) const { return i * this->n + j; } -int Matrix::get_n_rows() { return m; } +int Matrix::get_n_rows() const { return m; } -int Matrix::get_n_cols() { return n; } +int Matrix::get_n_cols() const { return n; } void Matrix::set_entry(int i, int j, double value) { @@ -28,7 +28,19 @@ void Matrix::set_entry(int i, int j, double value) { this->entries[get_position(i, j)] = value; } -std::unique_ptr Matrix::multiply(const Vector& in) { +double Matrix::get_entry(const int i, const int j) const { + if (i < 0 || i >= this->m) { + LOGGER->log(Log() << ERR << "Row number " << i << " out of bounds.\n"); + } + if (j < 0 || j >= this->n) { + LOGGER->log(Log() << ERR << "Column number " << i + << " out of bounds.\n"); + } + + return this->entries[get_position(i, j)]; +} + +std::unique_ptr Matrix::multiply(const Vector& in) const { if (in.get_size() != this->n) { LOGGER->log(Log(WARN) << "Tried to multiply a vector of size " diff --git a/tests/core/math/test_matrix.cpp b/tests/core/math/test_matrix.cpp index 03835b9..d7f6c91 100644 --- a/tests/core/math/test_matrix.cpp +++ b/tests/core/math/test_matrix.cpp @@ -3,7 +3,30 @@ #include #include -SCENARIO("Matricies are correctly multiplied with vectors", "[matrix][vector]") { +TEST_CASE("matrix getters and setters", "[matrix]") { + // Create and initialize matrix + jml::Matrix m(2, 3); + + m.set_entry(0, 0, 1); + m.set_entry(1, 0, 2); + m.set_entry(0, 1, 3); + m.set_entry(1, 1, 4); + m.set_entry(0, 2, 5); + m.set_entry(1, 2, 6); + + REQUIRE(m.get_n_rows() == 2); + REQUIRE(m.get_n_cols() == 3); + + REQUIRE(m.get_entry(0, 0) == 1); + REQUIRE(m.get_entry(1, 0) == 2); + REQUIRE(m.get_entry(0, 1) == 3); + REQUIRE(m.get_entry(1, 1) == 4); + REQUIRE(m.get_entry(0, 2) == 5); + REQUIRE(m.get_entry(1, 2) == 6); +} + +SCENARIO("Matricies are correctly multiplied with vectors", + "[matrix][vector]") { GIVEN("a matrix and vector with some values") { jml::Matrix m(2, 3); jml::Vector V(3); @@ -13,44 +36,33 @@ SCENARIO("Matricies are correctly multiplied with vectors", "[matrix][vector]") REQUIRE(m.get_n_cols() == 3); REQUIRE(V.get_size() == 3); } + WHEN("the matrix and vector are initialized") { + // Initialize matrix to some values + // TODO: add fuzzing? + m.set_entry(0, 0, 1); + m.set_entry(1, 0, 2); + m.set_entry(0, 1, 3); + m.set_entry(1, 1, 4); + m.set_entry(0, 2, 5); + m.set_entry(1, 2, 6); - // TODO: check row and col - - // Initialize matrix to some values - // TODO: add fuzzing? - // TODO: it would also be nice to test the setters (no getters defined yet) - m.set_entry(0, 0, 1); - m.set_entry(1, 0, 2); - m.set_entry(0, 1, 3); - m.set_entry(1, 1, 4); - m.set_entry(0, 2, 5); - m.set_entry(1, 2, 6); - - WHEN("the vector is initialized"){// Initialize vector to some values - V.set_entry(0, 7); - V.set_entry(1, 8); - V.set_entry(2, 9); - - THEN ("the vector entries are set as specified") { - REQUIRE(V.get_entry(0) == 7); - REQUIRE(V.get_entry(1) == 8); - REQUIRE(V.get_entry(2) == 9); - } - - - AND_WHEN("matrix and vector are multiplied") { - auto ret = m.multiply(V); + // Initialize vector to some values + V.set_entry(0, 7); + V.set_entry(1, 8); + V.set_entry(2, 9); - THEN("vector has length equal to number of rows") { - REQUIRE(ret->get_size() == 2); + AND_WHEN("matrix and vector are multiplied") { + auto ret = m.multiply(V); - AND_THEN("resultant vector values are correct") { - REQUIRE(ret->get_entry(0) == 76); - REQUIRE(ret->get_entry(1) == 100); - } + THEN("vector has length equal to number of rows") { + REQUIRE(ret->get_size() == 2); - } - - }} + AND_THEN("resultant vector values are correct") { + REQUIRE(ret->get_entry(0) == 76); + REQUIRE(ret->get_entry(1) == 100); + } + } + } + } } } From 50e47d15a8a6dd8be129efac6ae67fa360c3894e Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:16:24 -0700 Subject: [PATCH 17/30] feat!: make getter and setter for matrix throw when oob --- core/src/math/matrix.cpp | 5 +++++ tests/core/math/test_matrix.cpp | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/core/src/math/matrix.cpp b/core/src/math/matrix.cpp index 0506596..c191836 100644 --- a/core/src/math/matrix.cpp +++ b/core/src/math/matrix.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace jml { @@ -19,10 +20,12 @@ void Matrix::set_entry(int i, int j, double value) { if (i < 0 || i >= this->m) { LOGGER->log(Log() << ERR << "Row number " << i << " out of bounds.\n"); + throw std::out_of_range("row out of bounds"); } if (j < 0 || j >= this->n) { LOGGER->log(Log() << ERR << "Column number " << i << " out of bounds.\n"); + throw std::out_of_range("column out of bounds"); } this->entries[get_position(i, j)] = value; @@ -31,10 +34,12 @@ void Matrix::set_entry(int i, int j, double value) { double Matrix::get_entry(const int i, const int j) const { if (i < 0 || i >= this->m) { LOGGER->log(Log() << ERR << "Row number " << i << " out of bounds.\n"); + throw std::out_of_range("row out of bounds"); } if (j < 0 || j >= this->n) { LOGGER->log(Log() << ERR << "Column number " << i << " out of bounds.\n"); + throw std::out_of_range("column out of bounds"); } return this->entries[get_position(i, j)]; diff --git a/tests/core/math/test_matrix.cpp b/tests/core/math/test_matrix.cpp index d7f6c91..5fa0b94 100644 --- a/tests/core/math/test_matrix.cpp +++ b/tests/core/math/test_matrix.cpp @@ -2,11 +2,14 @@ #include "jml/math/vector.hpp" #include #include +#include TEST_CASE("matrix getters and setters", "[matrix]") { // Create and initialize matrix jml::Matrix m(2, 3); + // Set matrix + // TODO: should probably check for no throw here m.set_entry(0, 0, 1); m.set_entry(1, 0, 2); m.set_entry(0, 1, 3); @@ -14,6 +17,9 @@ TEST_CASE("matrix getters and setters", "[matrix]") { m.set_entry(0, 2, 5); m.set_entry(1, 2, 6); + // make sure it throws when oob + REQUIRE_THROWS_AS(m.set_entry(999, 999, 1.0), std::out_of_range); + REQUIRE(m.get_n_rows() == 2); REQUIRE(m.get_n_cols() == 3); @@ -23,6 +29,8 @@ TEST_CASE("matrix getters and setters", "[matrix]") { REQUIRE(m.get_entry(1, 1) == 4); REQUIRE(m.get_entry(0, 2) == 5); REQUIRE(m.get_entry(1, 2) == 6); + + REQUIRE_THROWS_AS(m.get_entry(999, 999), std::out_of_range); } SCENARIO("Matricies are correctly multiplied with vectors", From 7046dd665a18a83788fad05e71fce41832dd70ae Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:27:45 -0700 Subject: [PATCH 18/30] refactor: use bdd-style macros for testing matrix getters and setters --- tests/core/math/test_matrix.cpp | 59 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/tests/core/math/test_matrix.cpp b/tests/core/math/test_matrix.cpp index 5fa0b94..2276a01 100644 --- a/tests/core/math/test_matrix.cpp +++ b/tests/core/math/test_matrix.cpp @@ -4,36 +4,47 @@ #include #include -TEST_CASE("matrix getters and setters", "[matrix]") { - // Create and initialize matrix - jml::Matrix m(2, 3); - - // Set matrix - // TODO: should probably check for no throw here - m.set_entry(0, 0, 1); - m.set_entry(1, 0, 2); - m.set_entry(0, 1, 3); - m.set_entry(1, 1, 4); - m.set_entry(0, 2, 5); - m.set_entry(1, 2, 6); +SCENARIO("Matrix entries can be set and get", "[matrix]") { + GIVEN("a matrix of some size") { + // Create and initialize matrix + jml::Matrix m(2, 3); - // make sure it throws when oob - REQUIRE_THROWS_AS(m.set_entry(999, 999, 1.0), std::out_of_range); + THEN("the size is correct") { + REQUIRE(m.get_n_rows() == 2); + REQUIRE(m.get_n_cols() == 3); + } - REQUIRE(m.get_n_rows() == 2); - REQUIRE(m.get_n_cols() == 3); + WHEN("the values are set") { + // Set matrix + // TODO: should probably check for no throw here + REQUIRE_NOTHROW([&m]() { + m.set_entry(0, 0, 1); + m.set_entry(1, 0, 2); + m.set_entry(0, 1, 3); + m.set_entry(1, 1, 4); + m.set_entry(0, 2, 5); + m.set_entry(1, 2, 6); + }()); + + THEN("the stored values are correct") { + REQUIRE(m.get_entry(0, 0) == 1); + REQUIRE(m.get_entry(1, 0) == 2); + REQUIRE(m.get_entry(0, 1) == 3); + REQUIRE(m.get_entry(1, 1) == 4); + REQUIRE(m.get_entry(0, 2) == 5); + REQUIRE(m.get_entry(1, 2) == 6); + } + } - REQUIRE(m.get_entry(0, 0) == 1); - REQUIRE(m.get_entry(1, 0) == 2); - REQUIRE(m.get_entry(0, 1) == 3); - REQUIRE(m.get_entry(1, 1) == 4); - REQUIRE(m.get_entry(0, 2) == 5); - REQUIRE(m.get_entry(1, 2) == 6); + THEN("getting and setting out-of-bound entries throws out of range") { + REQUIRE_THROWS_AS(m.set_entry(999, 999, 1.0), std::out_of_range); - REQUIRE_THROWS_AS(m.get_entry(999, 999), std::out_of_range); + REQUIRE_THROWS_AS(m.get_entry(999, 999), std::out_of_range); + } + } } -SCENARIO("Matricies are correctly multiplied with vectors", +SCENARIO("Matrix is correctly multiplied with vector", "[matrix][vector]") { GIVEN("a matrix and vector with some values") { jml::Matrix m(2, 3); From e8aef3e967f34671ac8e596a4525f8022b5f5c74 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:04:27 -0700 Subject: [PATCH 19/30] tests: add tests for activation functions --- tests/core/math/test_activation_functions.cpp | 59 +++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 60 insertions(+) create mode 100644 tests/core/math/test_activation_functions.cpp diff --git a/tests/core/math/test_activation_functions.cpp b/tests/core/math/test_activation_functions.cpp new file mode 100644 index 0000000..cc4fe34 --- /dev/null +++ b/tests/core/math/test_activation_functions.cpp @@ -0,0 +1,59 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_floating_point.hpp" +#include "jml/math/activation_functions.hpp" + +TEST_CASE("FastSigmoid function", "[activation_function]") { + jml::FastSigmoid act; + + // FIXME: I don't know what mathematically significant values to check so here's some random ones + // FIXME: FastSigmoid impl appears to have forgotten the constant offset + REQUIRE_THAT(act.f(-1), Catch::Matchers::WithinRel(-0.25, 0.00001)); + REQUIRE_THAT(act.f(0), Catch::Matchers::WithinRel(0, 0.00001)); + REQUIRE_THAT(act.f(1), Catch::Matchers::WithinRel(0.25, 0.00001)); +} + +TEST_CASE("Sigmoid activation function", "[activation_function]") { + jml::Sigmoid act; + + REQUIRE_THAT(act.f(-1), Catch::Matchers::WithinRel(0.2689414, 0.00001)); + REQUIRE_THAT(act.f(0), Catch::Matchers::WithinRel(0.5, 0.00001)); + REQUIRE_THAT(act.f(1), Catch::Matchers::WithinRel(0.7310586, 0.00001)); + + REQUIRE_THAT(act.df(-1), Catch::Matchers::WithinRel(0.1966119, 0.00001)); + REQUIRE_THAT(act.df(0), Catch::Matchers::WithinRel(0.25, 0.00001)); + REQUIRE_THAT(act.df(1), Catch::Matchers::WithinRel(0.1966119, 0.00001)); +} + +TEST_CASE("ReLU activation function", "[activation_function]") { + jml::ReLU act; + + REQUIRE(act.f(-1) == 0); + REQUIRE(act.f(0) == 0); + REQUIRE(act.f(1) == 1); + + REQUIRE(act.df(-1) == 0); + // REQUIRE(act.df(0) == 0); FIXME: is this UB? + REQUIRE(act.df(1) == 1); +} + +TEST_CASE("LeakyReLU activation function", "[activation_function]") { + jml::LeakyReLU act; + + REQUIRE_THAT(act.f(-1), Catch::Matchers::WithinRel(-0.01, 0.00001)); + REQUIRE(act.f(0) == 0); + REQUIRE(act.f(1) == 1); + + REQUIRE_THAT(act.df(-1), Catch::Matchers::WithinRel(0.01, 0.00001)); + // REQUIRE(act.df(0) == 0); FIXME: is this UB? + REQUIRE(act.df(1) == 1); +} + +TEST_CASE("LeakyReLU activation function with custom leak", "[activation_function]") { + jml::LeakyReLU act(0.0001); + + // TODO: is it necessary to check function values? + + REQUIRE_THAT(act.df(-1), Catch::Matchers::WithinRel(0.0001, 0.00001)); + REQUIRE(act.df(1) == 1); +} diff --git a/tests/meson.build b/tests/meson.build index e28d564..c9528d8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ core_test_sources = [ 'core/math/test_matrix.cpp', + 'core/math/test_activation_functions.cpp', ] tests = executable('core_tests', From 6942ceff246bec98951e5b4e505111a2ffe9a948 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:07:25 -0700 Subject: [PATCH 20/30] test: test jml::Vector --- tests/core/math/test_vector.cpp | 83 +++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 84 insertions(+) create mode 100644 tests/core/math/test_vector.cpp diff --git a/tests/core/math/test_vector.cpp b/tests/core/math/test_vector.cpp new file mode 100644 index 0000000..cf5c6b8 --- /dev/null +++ b/tests/core/math/test_vector.cpp @@ -0,0 +1,83 @@ +#include "catch2/catch_test_macros.hpp" +#include "jml/math/activation_functions.hpp" +#include "jml/math/vector.hpp" + +SCENARIO("Vector entries can be get, set, and added to") { + GIVEN("a vector of some length") { + jml::Vector V(3); + + WHEN("vector entries are set") { + REQUIRE_NOTHROW([&V]() { + V.set_entry(0, 7); + V.set_entry(1, 8); + V.set_entry(2, 9); + }()); + + THEN("the vector entries are set as specified") { + REQUIRE(V.get_entry(0) == 7); + REQUIRE(V.get_entry(1) == 8); + REQUIRE(V.get_entry(2) == 9); + } + + AND_WHEN("vector entries are added to") { + REQUIRE_NOTHROW(V.add_entry(0, 3)); + + THEN("the entry is updated") { REQUIRE(V.get_entry(0) == 10); } + } + } + } +} + +SCENARIO("Vector is added to another vector") { + GIVEN("two vectors of the same length and some values") { + jml::Vector v1(3), v2(3); + + // Initialize the two vectors + v1.set_entry(0, 0); + v1.set_entry(1, 1); + v1.set_entry(2, 2); + + v2.set_entry(0, 3); + v2.set_entry(1, 4); + v2.set_entry(2, 5); + + WHEN("the vectors are added") { + v1.add(v2); + + THEN("the first vector's entries are changed") { + REQUIRE(v1.get_entry(0) == 3); + REQUIRE(v1.get_entry(1) == 5); + REQUIRE(v1.get_entry(2) == 7); + } + + THEN("the second vector's entries are unchanged") { + REQUIRE(v2.get_entry(0) == 3); + REQUIRE(v2.get_entry(1) == 4); + REQUIRE(v2.get_entry(2) == 5); + } + } + + // Adding vectors of different lengths is UB for now + } +} + +SCENARIO("Activation function is applied to a vector") { + GIVEN("a vector and an activation function") { + jml::Vector v(3); + jml::ReLU act; // Use ReLU for simplicity + + v.set_entry(0, -10); + v.set_entry(1, 0); + v.set_entry(2, 10); + + WHEN("the activation function is applied to the vector") { + v.apply(&act); + + THEN("the vector entries arer correctly modified") { + REQUIRE(v.get_entry(0) == 0); + REQUIRE(v.get_entry(1) == 0); + REQUIRE(v.get_entry(2) == 10); + } + } + } +} diff --git a/tests/meson.build b/tests/meson.build index c9528d8..4c0579d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ core_test_sources = [ 'core/math/test_matrix.cpp', + 'core/math/test_vector.cpp', 'core/math/test_activation_functions.cpp', ] From 938d0c3e800f1852bd1149bda32047d0df22fe5e Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:27:35 -0700 Subject: [PATCH 21/30] feat: enable use of any type in iterator overload for add_testing_data --- core/include/jml/model.hpp | 6 ++---- core/src/model.cpp | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index 3e5cdff..aba0cda 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -60,10 +60,8 @@ class JML_API Model { std::unique_ptr apply(Vector& in); // Adds testing data using iterators, for the start and end locations of // the wanted input and output data lists. - void add_testing_data ( - std::vector::iterator inb, std::vector::iterator ine, - std::vector::iterator otb, std::vector::iterator ote - ); + template + void add_testing_data (Iter inb, Iter ine, Iter otb, Iter ote); void add_testing_data (std::vector ins, std::vector outs); void add_testing_datum(Vector in, Vector out); void clear_testing_data(); diff --git a/core/src/model.cpp b/core/src/model.cpp index b11914a..57ce2da 100644 --- a/core/src/model.cpp +++ b/core/src/model.cpp @@ -18,10 +18,8 @@ std::unique_ptr Model::apply(Vector& in) { } -void Model::add_testing_data ( - std::vector::iterator inb, std::vector::iterator ine, - std::vector::iterator otb, std::vector::iterator ote -) { +template +void Model::add_testing_data (Iter inb, Iter ine, Iter otb, Iter ote) { testing_data_inputs .insert(std::end(testing_data_inputs), inb, ine); testing_data_outputs.insert(std::end(testing_data_outputs), otb, ote); } From ac5428054ba9f58466904bdd0dcbbd73e462456b Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:33:08 -0700 Subject: [PATCH 22/30] feat: add testing data getter --- core/include/jml/model.hpp | 2 ++ core/src/model.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index aba0cda..a67646f 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -65,6 +65,8 @@ class JML_API Model { void add_testing_data (std::vector ins, std::vector outs); void add_testing_datum(Vector in, Vector out); void clear_testing_data(); + // Gets testing data. First vector is inputs, second vector is outputs. + const std::tuple, std::vector> get_testing_data() const; private: std::vector layers; // Array of layers diff --git a/core/src/model.cpp b/core/src/model.cpp index 57ce2da..264708b 100644 --- a/core/src/model.cpp +++ b/core/src/model.cpp @@ -38,4 +38,8 @@ void Model::clear_testing_data() { testing_data_outputs.clear(); } +const std::tuple, std::vector> Model::get_testing_data() const { + return {this->testing_data_inputs, this->testing_data_outputs}; +} + } From 6f9ebb7d9262cf84910749fb67e0d119e3d6e8ab Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 08:50:22 -0700 Subject: [PATCH 23/30] fix: move model implementation around so it builds and links --- core/include/jml/model.hpp | 2 ++ core/include/jml/model.tpp | 13 +++++++++++++ core/src/model.cpp | 8 ++------ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 core/include/jml/model.tpp diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index a67646f..b477877 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -76,3 +76,5 @@ class JML_API Model { }; } + +#include "model.tpp" diff --git a/core/include/jml/model.tpp b/core/include/jml/model.tpp new file mode 100644 index 0000000..d489491 --- /dev/null +++ b/core/include/jml/model.tpp @@ -0,0 +1,13 @@ +#pragma once + +#include "model.hpp" + +namespace jml { + +template +void Model::add_testing_data (Iter inb, Iter ine, Iter otb, Iter ote) { + testing_data_inputs .insert(std::end(testing_data_inputs), inb, ine); + testing_data_outputs.insert(std::end(testing_data_outputs), otb, ote); +} + +} diff --git a/core/src/model.cpp b/core/src/model.cpp index 264708b..218b552 100644 --- a/core/src/model.cpp +++ b/core/src/model.cpp @@ -2,6 +2,8 @@ namespace jml { +Model::Model() {} + std::unique_ptr Model::apply(Vector& in) { std::unique_ptr inp = std::make_unique(in); @@ -18,12 +20,6 @@ std::unique_ptr Model::apply(Vector& in) { } -template -void Model::add_testing_data (Iter inb, Iter ine, Iter otb, Iter ote) { - testing_data_inputs .insert(std::end(testing_data_inputs), inb, ine); - testing_data_outputs.insert(std::end(testing_data_outputs), otb, ote); -} - void Model::add_testing_data(std::vector ins, std::vector outs) { return add_testing_data(std::begin(ins), std::end(ins), std::begin(outs), std::end(outs)); } From 4cce9d829623a8e7fca90a0ea9427558d61326b2 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:07:23 -0700 Subject: [PATCH 24/30] feat: add copy constructor to Vector --- core/include/jml/math/vector.hpp | 1 + core/src/math/vector.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/core/include/jml/math/vector.hpp b/core/include/jml/math/vector.hpp index ad528a0..07aca1d 100644 --- a/core/include/jml/math/vector.hpp +++ b/core/include/jml/math/vector.hpp @@ -23,6 +23,7 @@ class JML_API Vector { public: Vector(int length); + Vector(const Vector& other); ~Vector(); // This applies a given function to every component in the vector. diff --git a/core/src/math/vector.cpp b/core/src/math/vector.cpp index 79f3ad9..d69fce9 100644 --- a/core/src/math/vector.cpp +++ b/core/src/math/vector.cpp @@ -16,6 +16,12 @@ Vector::Vector(int length) { this->length = length; } +Vector::Vector(const Vector& other) { + this->length = other.length; + this->entries = new double[this->length]; + memcpy(this->entries, other.entries, this->length * sizeof(double)); +} + Vector::~Vector() { delete[] this->entries; } void Vector::apply(ActivationFunction *fn) { From 81fa07a3411e16abbc98182329f95f3b66fb0742 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:10:03 -0700 Subject: [PATCH 25/30] tests: test Model --- tests/core/test_model.cpp | 212 ++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 213 insertions(+) create mode 100644 tests/core/test_model.cpp diff --git a/tests/core/test_model.cpp b/tests/core/test_model.cpp new file mode 100644 index 0000000..e63ed53 --- /dev/null +++ b/tests/core/test_model.cpp @@ -0,0 +1,212 @@ +#include "catch2/catch_test_macros.hpp" +#include "jml/math/vector.hpp" +#include "jml/model.hpp" +#include +#include + +SCENARIO("Testing data added to model") { + GIVEN("a model") { + jml::Model M; + + // some testing data + jml::Vector in1(3), in2(3), out1(2), out2(2); + std::vector ins, outs; + + in1.set_entry(0, 3); + in1.set_entry(1, 5); + in1.set_entry(2, 7); + in2.set_entry(0, 11); + in2.set_entry(1, 13); + in2.set_entry(2, 17); + out1.set_entry(0, 19); + out1.set_entry(1, 23); + out2.set_entry(0, 29); + out2.set_entry(1, 31); + + ins.push_back(in1); + ins.push_back(in2); + outs.push_back(out1); + outs.push_back(out2); + + // Another STL container to test for iterators + std::array array_ins = {in1, in2}, + array_outs = {out1, out2}; + + WHEN("testing datum is added") { + M.add_testing_datum(in1, out1); + + THEN("model has added testing datum") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 1); + REQUIRE(M_outs.size() == 1); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + } + + AND_WHEN("more testing datum is added") { + M.add_testing_datum(in2, out2); + + THEN("model has all added testing data") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 2); + REQUIRE(M_outs.size() == 2); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + + REQUIRE(M_ins[1].get_entry(0) == 11); + REQUIRE(M_ins[1].get_entry(1) == 13); + REQUIRE(M_ins[1].get_entry(2) == 17); + REQUIRE(M_outs[1].get_entry(0) == 29); + REQUIRE(M_outs[1].get_entry(1) == 31); + } + } + } + + WHEN("testing data is added with vector overload") { + M.add_testing_data(ins, outs); + + THEN("model has added testing data") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 2); + REQUIRE(M_outs.size() == 2); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_ins[1].get_entry(0) == 11); + REQUIRE(M_ins[1].get_entry(1) == 13); + REQUIRE(M_ins[1].get_entry(2) == 17); + + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + REQUIRE(M_outs[1].get_entry(0) == 29); + REQUIRE(M_outs[1].get_entry(1) == 31); + } + + AND_WHEN("more testing data is added") { + M.add_testing_data(ins, outs); + + THEN("model has all added testing data") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 4); + REQUIRE(M_outs.size() == 4); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_ins[1].get_entry(0) == 11); + REQUIRE(M_ins[1].get_entry(1) == 13); + REQUIRE(M_ins[1].get_entry(2) == 17); + + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + REQUIRE(M_outs[1].get_entry(0) == 29); + REQUIRE(M_outs[1].get_entry(1) == 31); + + REQUIRE(M_ins[2].get_entry(0) == 3); + REQUIRE(M_ins[2].get_entry(1) == 5); + REQUIRE(M_ins[2].get_entry(2) == 7); + REQUIRE(M_ins[3].get_entry(0) == 11); + REQUIRE(M_ins[3].get_entry(1) == 13); + REQUIRE(M_ins[3].get_entry(2) == 17); + + REQUIRE(M_outs[2].get_entry(0) == 19); + REQUIRE(M_outs[2].get_entry(1) == 23); + REQUIRE(M_outs[3].get_entry(0) == 29); + REQUIRE(M_outs[3].get_entry(1) == 31); + } + } + } + + WHEN("testing data is added with iterator overload") { + M.add_testing_data(array_ins.begin(), array_ins.end(), + array_outs.begin(), array_outs.end()); + + THEN("model has added testing data") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 2); + REQUIRE(M_outs.size() == 2); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_ins[1].get_entry(0) == 11); + REQUIRE(M_ins[1].get_entry(1) == 13); + REQUIRE(M_ins[1].get_entry(2) == 17); + + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + REQUIRE(M_outs[1].get_entry(0) == 29); + REQUIRE(M_outs[1].get_entry(1) == 31); + } + + AND_WHEN("more testing data is added") { + M.add_testing_data(ins.begin(), ins.end(), outs.begin(), + outs.end()); + + THEN("model has all added testing data") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 4); + REQUIRE(M_outs.size() == 4); + + REQUIRE(M_ins[0].get_entry(0) == 3); + REQUIRE(M_ins[0].get_entry(1) == 5); + REQUIRE(M_ins[0].get_entry(2) == 7); + REQUIRE(M_ins[1].get_entry(0) == 11); + REQUIRE(M_ins[1].get_entry(1) == 13); + REQUIRE(M_ins[1].get_entry(2) == 17); + + REQUIRE(M_outs[0].get_entry(0) == 19); + REQUIRE(M_outs[0].get_entry(1) == 23); + REQUIRE(M_outs[1].get_entry(0) == 29); + REQUIRE(M_outs[1].get_entry(1) == 31); + + REQUIRE(M_ins[2].get_entry(0) == 3); + REQUIRE(M_ins[2].get_entry(1) == 5); + REQUIRE(M_ins[2].get_entry(2) == 7); + REQUIRE(M_ins[3].get_entry(0) == 11); + REQUIRE(M_ins[3].get_entry(1) == 13); + REQUIRE(M_ins[3].get_entry(2) == 17); + + REQUIRE(M_outs[2].get_entry(0) == 19); + REQUIRE(M_outs[2].get_entry(1) == 23); + REQUIRE(M_outs[3].get_entry(0) == 29); + REQUIRE(M_outs[3].get_entry(1) == 31); + } + } + } + + WHEN("there is testing data and it is cleared") { + M.add_testing_datum(in1, in2); // hi data + M.clear_testing_data(); // bye data + + THEN("testing data in model is no more") { + std::vector M_ins, M_outs; + std::tie(M_ins, M_outs) = M.get_testing_data(); + + REQUIRE(M_ins.size() == 0); + REQUIRE(M_outs.size() == 0); + } + } + } +} diff --git a/tests/meson.build b/tests/meson.build index 4c0579d..5d2749f 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -2,6 +2,7 @@ core_test_sources = [ 'core/math/test_matrix.cpp', 'core/math/test_vector.cpp', 'core/math/test_activation_functions.cpp', + 'core/test_model.cpp', ] tests = executable('core_tests', From 1eb57ec791b0ef7aef3d420e7491a2053e805cd8 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:45:16 -0700 Subject: [PATCH 26/30] docs: add information about testing --- CONTRIBUTING.md | 11 +++++++++++ README.md | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33e1594..56ec112 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,3 +101,14 @@ view the API docs for discussion. The entire project's functionality is in the C++ namespace `jml`, and we prefer `snake_case` over `camelCase.` + +## Testing +Though tests are tedious to write, they ensure that our code (somewhat) +functions consistently and as expected. As such, we encourage you to write +tests for any code you add. JML uses the popular +[Catch2](https://github.com/catchorg/Catch2) framework for testing. Their docs, +located under `docs/` in their repository, should be all you need to write +tests (they also have a Discord server for support inquiries). How you write, +express, or format the tests does not matter, as long as they thoroughly test +the code (and bear some semblance of readability). Make sure to also add your +test source to the meson build file. diff --git a/README.md b/README.md index 046cd54..59e9c8e 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,11 @@ meson install -C build # again, replace `build` ``` ## Test -> [!WARNING] -> Add relevant information once testing framework is setup +This project uses (Catch2)[https://github.com/catchorg/Catch2] as its testing framework. Run `meson test` to test the project. ```bash meson test -C build # replace `build` ``` + +Unfortunately, meson doesn't provide good output when a test fails. Running the test executable manually is often more helpful. Look under `build/tests/` to find the right executable. The executable for testing the core library is `core_tests`. From 0ccdcf0b73ee39117d05404098ff6ab8d485b7dd Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:51:10 -0700 Subject: [PATCH 27/30] fix: add missing includes for tuple --- core/include/jml/model.hpp | 1 + core/src/model.cpp | 1 + tests/core/test_model.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/core/include/jml/model.hpp b/core/include/jml/model.hpp index b477877..e2f730b 100644 --- a/core/include/jml/model.hpp +++ b/core/include/jml/model.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include diff --git a/core/src/model.cpp b/core/src/model.cpp index 218b552..ebd97c7 100644 --- a/core/src/model.cpp +++ b/core/src/model.cpp @@ -1,4 +1,5 @@ #include +#include namespace jml { diff --git a/tests/core/test_model.cpp b/tests/core/test_model.cpp index e63ed53..4a2f78c 100644 --- a/tests/core/test_model.cpp +++ b/tests/core/test_model.cpp @@ -2,6 +2,7 @@ #include "jml/math/vector.hpp" #include "jml/model.hpp" #include +#include #include SCENARIO("Testing data added to model") { From 0581f79bf59b08dbbb8604d8cabad17779b22386 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 09:58:09 -0700 Subject: [PATCH 28/30] ci: upload testlog as artifact --- .github/workflows/build-test.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3499da3..4aa4911 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -35,6 +35,12 @@ jobs: - name: Test with Meson run: meson test -C build + - name: Upload test log + uses: actions/upload-artifact@v3 + with: + name: testlog + path: build/meson-logs/testlog.txt + macos: name: Build and Test on macOS runs-on: macos-latest @@ -62,6 +68,12 @@ jobs: - name: Test with Meson run: meson test -C build + - name: Upload test log + uses: actions/upload-artifact@v3 + with: + name: testlog + path: build/meson-logs/testlog.txt + windows-vs: name: Build and Test on Windows (Visual Studio) runs-on: windows-latest @@ -88,3 +100,9 @@ jobs: - name: Test with Meson run: meson test -C build + + - name: Upload test log + uses: actions/upload-artifact@v3 + with: + name: testlog + path: build/meson-logs/testlog.txt From 82cfe78cf3adf4d08185bd8ac036ed0cb95675d1 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 10:01:35 -0700 Subject: [PATCH 29/30] ci: change artifact name --- .github/workflows/build-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4aa4911..f118fa2 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -38,7 +38,7 @@ jobs: - name: Upload test log uses: actions/upload-artifact@v3 with: - name: testlog + name: testlog-linux path: build/meson-logs/testlog.txt macos: @@ -71,7 +71,7 @@ jobs: - name: Upload test log uses: actions/upload-artifact@v3 with: - name: testlog + name: testlog-macos path: build/meson-logs/testlog.txt windows-vs: @@ -104,5 +104,5 @@ jobs: - name: Upload test log uses: actions/upload-artifact@v3 with: - name: testlog + name: testlog-windows path: build/meson-logs/testlog.txt From a3581343c83c7564c546eb72646f11b288f803f8 Mon Sep 17 00:00:00 2001 From: Sophon96 <71684640+Sophon96@users.noreply.github.com> Date: Sun, 15 Oct 2023 11:39:36 -0700 Subject: [PATCH 30/30] docs: fix md link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59e9c8e..6c1565a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ meson install -C build # again, replace `build` ``` ## Test -This project uses (Catch2)[https://github.com/catchorg/Catch2] as its testing framework. +This project uses [Catch2](https://github.com/catchorg/Catch2) as its testing framework. Run `meson test` to test the project. ```bash