diff --git a/CMakeLists.txt b/CMakeLists.txt index a32bf00..cd2805b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,17 +2,17 @@ cmake_minimum_required(VERSION 2.8) project(inform C) set(${PROJECT_NAME}_VERSION_MAJOR 0) -set(${PROJECT_NAME}_VERSION_MINOR 0.4) +set(${PROJECT_NAME}_VERSION_MINOR 0.5) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}) message(STATUS "inform version: ${${PROJECT_NAME}_VERSION}") -if (UNIX) +if (UNIX AND NOT APPLE) add_definitions("-Wall -Wextra -Werror -Wno-unused-parameter -std=gnu1x") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pg") -endif() - -if (APPLE) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -pg") +elseif (APPLE) + add_definitions("-Wall -Wextra -Werror -Wno-unused-parameter -std=gnu1x") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") set(CMAKE_MACOSX_RPATH ON) endif() diff --git a/appveyor.yml b/appveyor.yml index fe794d6..bdefe3b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.0.4.{build} +version: 0.0.5.{build} configuration: - Debug - Release diff --git a/dist/package.sh b/dist/package.sh index fafce3d..8b5dfdc 100755 --- a/dist/package.sh +++ b/dist/package.sh @@ -5,7 +5,6 @@ version=${version#"-- inform version: "} target=inform-$version prefix=dist/$target -tarball=$target"_linux-x86_64.tar.gz" cmake -H. -Bbuild/production -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$prefix > /dev/null make -C build/production all test @@ -14,6 +13,19 @@ cp dist/install.sh $prefix > /dev/null cp LICENSE $prefix > /dev/null cp README.md $prefix > /dev/null +system=`uname -s` +hardware=`uname -m` + +if [[ "$system" == "Darwin" ]]; then + platform="macosx" +elif [[ "$system" == "Linux" ]]; then + platform="linux" +else + echo "unsupported platform: $system" +fi + +tarball=$target"_"$platform"-"$hardware".tar.gz" + cd dist echo "Creating tarball: dist/$tarball" tar czf $tarball $target diff --git a/include/inform/conditional_entropy.h b/include/inform/conditional_entropy.h new file mode 100644 index 0000000..d64a6d2 --- /dev/null +++ b/include/inform/conditional_entropy.h @@ -0,0 +1,29 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Compute the conditional entropy between two timeseries, using the + * first as the condition. + */ +EXPORT double inform_conditional_entropy(int const *xs, int const *ys, + size_t n, int bx, int by, double b, inform_error *err); + +/** + * Compute the local conditional entropy between two timeseries, using the + * first as the condition. + */ +EXPORT double *inform_local_conditional_entropy(int const *xs, int const *ys, + size_t n, int bx, int by, double b, double *mi, inform_error *err); + +#ifdef __cplusplus +} +#endif diff --git a/include/inform/relative_entropy.h b/include/inform/relative_entropy.h new file mode 100644 index 0000000..a9b0084 --- /dev/null +++ b/include/inform/relative_entropy.h @@ -0,0 +1,29 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Compute the relative entropy between two timeseries, each considered as + * a timeseries of samples from two distributions. + */ +EXPORT double inform_relative_entropy(int const *xs, int const *ys, size_t n, + int b, double base, inform_error *err); + +/** + * Compute the pointwise relative entropy between two timeseries, each + * considered as a timeseries of samples from two distributions. + */ +EXPORT double *inform_local_relative_entropy(int const *xs, int const *ys, + size_t n, int b, double base, double *re, inform_error *err); + +#ifdef __cplusplus +} +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d3997c..548965f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,12 @@ set(${PROJECT_NAME}_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/active_info.c ${CMAKE_CURRENT_SOURCE_DIR}/block_entropy.c + ${CMAKE_CURRENT_SOURCE_DIR}/conditional_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/dist.c ${CMAKE_CURRENT_SOURCE_DIR}/entropy_rate.c ${CMAKE_CURRENT_SOURCE_DIR}/error.c ${CMAKE_CURRENT_SOURCE_DIR}/mutual_info.c + ${CMAKE_CURRENT_SOURCE_DIR}/relative_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/shannon.c ${CMAKE_CURRENT_SOURCE_DIR}/transfer_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/utilities/binning.c diff --git a/src/conditional_entropy.c b/src/conditional_entropy.c new file mode 100644 index 0000000..c6205d1 --- /dev/null +++ b/src/conditional_entropy.c @@ -0,0 +1,121 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#include +#include + +static bool check_arguments(int const *xs, int const *ys, size_t n, int bx, + int by, inform_error *err) +{ + if (xs == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ETIMESERIES, true); + } + else if (ys == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ETIMESERIES, true); + } + else if (n < 1) + { + INFORM_ERROR_RETURN(err, INFORM_ESHORTSERIES, true); + } + else if (bx < 2) + { + INFORM_ERROR_RETURN(err, INFORM_EBASE, true); + } + else if (by < 2) + { + INFORM_ERROR_RETURN(err, INFORM_EBASE, true); + } + for (size_t i = 0; i < n; ++i) + { + if (xs[i] < 0 || ys[i] < 0) + { + INFORM_ERROR_RETURN(err, INFORM_ENEGSTATE, true); + } + else if (bx <= xs[i] || by <= ys[i]) + { + INFORM_ERROR_RETURN(err, INFORM_EBADSTATE, true); + } + } + return false; +} + +inline static bool allocate(int bx, int by, inform_dist **x, inform_dist **xy, + inform_error *err) +{ + if ((*x = inform_dist_alloc(bx)) == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, true); + } + if ((*xy = inform_dist_alloc(bx * by)) == NULL) + { + inform_dist_free(*x); + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, true); + } + return false; +} + +inline static void accumulate(int const *xs, int const *ys, size_t n, int by, + inform_dist *x, inform_dist *xy) +{ + x->counts = n; + xy->counts = n; + + for (size_t i = 0; i < n; ++i) + { + x->histogram[xs[i]]++; + xy->histogram[xs[i]*by + ys[i]]++; + } +} + +inline static void free_all(inform_dist **x, inform_dist **xy) +{ + inform_dist_free(*x); + inform_dist_free(*xy); +} + +double inform_conditional_entropy(int const *xs, int const *ys, size_t n, + int bx, int by, double b, inform_error *err) +{ + if (check_arguments(xs, ys, n, bx, by, err)) return NAN; + + inform_dist *x = NULL, *xy = NULL; + if (allocate(bx, by, &x, &xy, err)) return NAN; + + accumulate(xs, ys, n, by, x, xy); + + double ce = inform_shannon_ce(xy, x, (double) b); + + free_all(&x, &xy); + + return ce; +} + +double *inform_local_conditional_entropy(int const *xs, int const *ys, + size_t n, int bx, int by, double b, double *ce, inform_error *err) +{ + if (check_arguments(xs, ys, n, bx, by, err)) return NULL; + + if (ce == NULL) + { + ce = malloc(n * sizeof(double)); + if (ce == NULL) + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, NULL); + } + + inform_dist *x = NULL, *xy = NULL; + if (allocate(bx, by, &x, &xy, err)) return NULL; + + accumulate(xs, ys, n, by, x, xy); + + for (size_t i = 0; i < n; ++i) + { + int z = xs[i]*by + ys[i]; + ce[i] = inform_shannon_pce(xy, x, z, xs[i], (double) b); + } + + free_all(&x, &xy); + + return ce; +} \ No newline at end of file diff --git a/src/mutual_info.c b/src/mutual_info.c index 7961f79..3f9e144 100644 --- a/src/mutual_info.c +++ b/src/mutual_info.c @@ -84,8 +84,6 @@ inline static void free_all(inform_dist **x, inform_dist **y, inform_dist **xy) inform_dist_free(*xy); } -#include - double inform_mutual_info(int const *xs, int const *ys, size_t n, int bx, int by, double b, inform_error *err) { diff --git a/src/relative_entropy.c b/src/relative_entropy.c new file mode 100644 index 0000000..0964016 --- /dev/null +++ b/src/relative_entropy.c @@ -0,0 +1,116 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#include +#include + +static bool check_arguments(int const *xs, int const *ys, size_t n, int b, + inform_error *err) +{ + if (xs == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ETIMESERIES, true); + } + else if (ys == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ETIMESERIES, true); + } + else if (n < 1) + { + INFORM_ERROR_RETURN(err, INFORM_ESHORTSERIES, true); + } + else if (b < 2) + { + INFORM_ERROR_RETURN(err, INFORM_EBASE, true); + } + for (size_t i = 0; i < n; ++i) + { + if (xs[i] < 0 || ys[i] < 0) + { + INFORM_ERROR_RETURN(err, INFORM_ENEGSTATE, true); + } + else if (b <= xs[i] || b <= ys[i]) + { + INFORM_ERROR_RETURN(err, INFORM_EBADSTATE, true); + } + } + return false; +} + +inline static bool allocate(int b, inform_dist **x, inform_dist **y, + inform_error *err) +{ + if ((*x = inform_dist_alloc(b)) == NULL) + { + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, true); + } + if ((*y = inform_dist_alloc(b)) == NULL) + { + inform_dist_free(*x); + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, true); + } + return false; +} + +inline static void accumulate(int const *xs, int const *ys, size_t n, + inform_dist *x, inform_dist *y) +{ + x->counts = n; + y->counts = n; + + for (size_t i = 0; i < n; ++i) + { + x->histogram[xs[i]]++; + y->histogram[ys[i]]++; + } +} + +inline static void free_all(inform_dist **x, inform_dist **y) +{ + inform_dist_free(*x); + inform_dist_free(*y); +} + +double inform_relative_entropy(int const *xs, int const *ys, size_t n, int b, + double base, inform_error *err) +{ + if (check_arguments(xs, ys, n, b, err)) return NAN; + + inform_dist *x = NULL, *y = NULL; + if (allocate(b, &x, &y, err)) return NAN; + + accumulate(xs, ys, n, x, y); + + double re = inform_shannon_re(x, y, base); + + free_all(&x, &y); + + return re; +} + +double *inform_local_relative_entropy(int const *xs, int const *ys, size_t n, + int b, double base, double *re, inform_error *err) +{ + if (check_arguments(xs, ys, n, b, err)) return NULL; + + if (re == NULL) + { + re = malloc(n * sizeof(double)); + if (re == NULL) + INFORM_ERROR_RETURN(err, INFORM_ENOMEM, NULL); + } + + inform_dist *x = NULL, *y = NULL; + if (allocate(b, &x, &y, err)) return NULL; + + accumulate(xs, ys, n, x, y); + + for (size_t i = 0; i < (size_t) b; ++i) + { + re[i] = inform_shannon_pre(x, y, i, base); + } + + free_all(&x, &y); + + return re; +} \ No newline at end of file diff --git a/src/shannon.c b/src/shannon.c index e5baafb..fed1706 100644 --- a/src/shannon.c +++ b/src/shannon.c @@ -98,7 +98,7 @@ double inform_shannon_pre(inform_dist const *p, inform_dist const *q, { double u = inform_dist_prob(p, event); double v = inform_dist_prob(q, event); - return -log2(u/v) / log2(base); + return log2(u/v) / log2(base); } return NAN; } diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index a258b06..22b529a 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -2,10 +2,12 @@ set(${PROJECT_NAME}_UNITTEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/active_info.c ${CMAKE_CURRENT_SOURCE_DIR}/block_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/canary.c + ${CMAKE_CURRENT_SOURCE_DIR}/conditional_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/dist.c ${CMAKE_CURRENT_SOURCE_DIR}/entropy_rate.c ${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/mutual_info.c + ${CMAKE_CURRENT_SOURCE_DIR}/relative_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/shannon.c ${CMAKE_CURRENT_SOURCE_DIR}/transfer_entropy.c ${CMAKE_CURRENT_SOURCE_DIR}/util.c diff --git a/test/unittests/conditional_entropy.c b/test/unittests/conditional_entropy.c new file mode 100644 index 0000000..5775dbf --- /dev/null +++ b/test/unittests/conditional_entropy.c @@ -0,0 +1,297 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#include "util.h" +#include +#include +#include + +UNIT(ConditionalEntropyNULLSeries) +{ + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(NULL, NULL, 3, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy((int[]){0,0,1}, NULL, 3, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); +} + +UNIT(ConditionalEntropySeriesTooShort) +{ + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, xs, 0, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ESHORTSERIES, err); +} + +UNIT(ConditionalEntropyInvalidBase) +{ + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + for (int i = 0; i < 2; ++i) + { + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, xs, 8, i, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, xs, 8, 2, i, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + } +} + +UNIT(ConditionalEntropyNegativeState) +{ + int const xs[] = {1,1,0,0,-1,0,0,1}; + int const ys[] = {1,1,0,0, 1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, ys, 8, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(ys, xs, 8, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); +} + +UNIT(ConditionalEntropyBadState) +{ + int const xs[] = {1,2,0,0,1,0,0,1}; + int const ys[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, ys, 8, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_conditional_entropy(xs, ys, 8, 2, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); +} + +UNIT(ConditionalEntropy) +{ + inform_error err = INFORM_SUCCESS; + + ASSERT_DBL_NEAR_TOL(0.899985, inform_conditional_entropy((int[]){0,0,1,1,1,1,0,0,0}, + (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.972765, inform_conditional_entropy((int[]){1,0,0,1,0,0,1,0,0}, + (int[]){0,0,1,1,1,1,0,0,0}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.000000, inform_conditional_entropy((int[]){0,0,0,0,1,1,1,1}, + (int[]){1,1,1,1,0,0,0,0}, 8, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.000000, inform_conditional_entropy((int[]){0,0,1,1,1,1,0,0,0}, + (int[]){1,1,0,0,0,0,1,1,1}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.918296, inform_conditional_entropy((int[]){1,1,0,1,0,1,1,1,0}, + (int[]){1,1,0,0,0,1,0,1,1}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.918296, inform_conditional_entropy((int[]){0,0,0,0,0,0,0,0,0}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.845516, inform_conditional_entropy((int[]){1,1,1,1,0,0,0,0,1}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.899985, inform_conditional_entropy((int[]){1,1,0,0,1,1,0,0,1}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.000000, inform_conditional_entropy((int[]){0,1,0,1,0,1,0,1}, + (int[]){0,2,0,2,0,2,0,2}, 8, 2, 3, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.918296, + inform_conditional_entropy((int[]){0,0,0,0,0,0,1,1,1,1,1,1}, + (int[]){0,0,0,0,1,1,1,1,2,2,2,2}, 12, 2, 3, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.444444, inform_conditional_entropy((int[]){0,0,1,1,2,1,1,0,0}, + (int[]){0,0,0,1,1,1,0,0,0}, 9, 3, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.666667, inform_conditional_entropy((int[]){0,1,0,0,1,0,0,1,0}, + (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.606844, inform_conditional_entropy((int[]){1,0,0,1,0,0,1,0}, + (int[]){2,0,1,2,0,1,2,0}, 8, 2, 3, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); +} + +UNIT(LocalConditionalEntropyNULLSeries) +{ + double ce[8]; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(NULL, NULL, 3, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy((int[]){0,0,1}, NULL, 3, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); +} + +UNIT(LocalConditionalEntropySeriesTooShort) +{ + double ce[8]; + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, xs, 0, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ESHORTSERIES, err); +} + +UNIT(LocalConditionalEntropyInvalidBase) +{ + double ce[8]; + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + for (int i = 0; i < 2; ++i) + { + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, xs, 8, i, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, xs, 8, 2, i, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + } +} + +UNIT(LocalConditionalEntropyNegativeState) +{ + double ce[8]; + int const xs[] = {1,1,0,0,-1,0,0,1}; + int const ys[] = {1,1,0,0, 1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, ys, 8, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(ys, xs, 8, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); +} + +UNIT(LocalConditionalEntropyBadState) +{ + double ce[8]; + int const xs[] = {1,2,0,0,1,0,0,1}; + int const ys[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, ys, 8, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_conditional_entropy(xs, ys, 8, 2, 2, 2, ce, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); +} + +UNIT(LocalConditionalEntropyAllocatesOutput) +{ + int const xs[] = {0,0,1,1,1,1,0,0,0}; + int const ys[] = {1,1,0,0,0,0,1,1,1}; + inform_error err = INFORM_SUCCESS; + double *ce = inform_local_conditional_entropy(xs, ys, 9, 2, 2, 2, NULL, &err); + ASSERT_NOT_NULL(ce); + ASSERT_EQUAL(INFORM_SUCCESS, err); + free(ce); +} + +UNIT(LocalConditionalEntropy) +{ + inform_error err = INFORM_SUCCESS; + double ce_8[8]; + double ce_9[9]; + double ce_12[12]; + + inform_local_conditional_entropy((int[]){0,0,1,1,1,1,0,0,0}, (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.899985, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){1,0,0,1,0,0,1,0,0}, (int[]){0,0,1,1,1,1,0,0,0}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.972765, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,0,0,0,1,1,1,1}, (int[]){1,1,1,1,0,0,0,0}, 8, 2, 2, 2, ce_8, &err); + ASSERT_DBL_NEAR_TOL(0.000000, AVERAGE(ce_8), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,0,1,1,1,1,0,0,0}, (int[]){1,1,0,0,0,0,1,1,1}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.000000, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){1,1,0,1,0,1,1,1,0}, (int[]){1,1,0,0,0,1,0,1,1}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.918296, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,0,0,0,0,0,0,0,0}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.918296, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){1,1,1,1,0,0,0,0,1}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.845516, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){1,1,0,0,1,1,0,0,1}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.899985, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,1,0,1,0,1,0,1}, (int[]){0,2,0,2,0,2,0,2}, 8, 2, 3, 2, ce_8, &err); + ASSERT_DBL_NEAR_TOL(0.000000, AVERAGE(ce_8), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,0,0,0,0,0,1,1,1,1,1,1}, (int[]){0,0,0,0,1,1,1,1,2,2,2,2}, 12, 2, 3, 2, ce_12, &err); + ASSERT_DBL_NEAR_TOL(0.918296, AVERAGE(ce_12), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,0,1,1,2,1,1,0,0}, (int[]){0,0,0,1,1,1,0,0,0}, 9, 3, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.444444, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){0,1,0,0,1,0,0,1,0}, (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, 2, ce_9, &err); + ASSERT_DBL_NEAR_TOL(0.666667, AVERAGE(ce_9), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_conditional_entropy((int[]){1,0,0,1,0,0,1,0}, (int[]){2,0,1,2,0,1,2,0}, 8, 2, 3, 2, ce_8, &err); + ASSERT_DBL_NEAR_TOL(0.606844, AVERAGE(ce_8), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); +} + +BEGIN_SUITE(ConditionalEntropy) + ADD_UNIT(ConditionalEntropyNULLSeries) + ADD_UNIT(ConditionalEntropySeriesTooShort) + ADD_UNIT(ConditionalEntropyInvalidBase) + ADD_UNIT(ConditionalEntropyNegativeState) + ADD_UNIT(ConditionalEntropyBadState) + ADD_UNIT(ConditionalEntropy) + ADD_UNIT(LocalConditionalEntropyNULLSeries) + ADD_UNIT(LocalConditionalEntropySeriesTooShort) + ADD_UNIT(LocalConditionalEntropyInvalidBase) + ADD_UNIT(LocalConditionalEntropyNegativeState) + ADD_UNIT(LocalConditionalEntropyBadState) + ADD_UNIT(LocalConditionalEntropyAllocatesOutput) + ADD_UNIT(LocalConditionalEntropy) +END_SUITE diff --git a/test/unittests/main.c b/test/unittests/main.c index f1f894c..85e7077 100644 --- a/test/unittests/main.c +++ b/test/unittests/main.c @@ -6,10 +6,12 @@ IMPORT_SUITE(ActiveInformation); IMPORT_SUITE(BlockEntropy); IMPORT_SUITE(Canary); +IMPORT_SUITE(ConditionalEntropy); IMPORT_SUITE(Distribution); IMPORT_SUITE(Entropy); IMPORT_SUITE(EntropyRate); IMPORT_SUITE(MutualInfo); +IMPORT_SUITE(RelativeEntropy); IMPORT_SUITE(TransferEntropy); IMPORT_SUITE(Utilities); @@ -17,10 +19,12 @@ BEGIN_REGISTRATION REGISTER(ActiveInformation) REGISTER(BlockEntropy) REGISTER(Canary) + REGISTER(ConditionalEntropy) REGISTER(Distribution) REGISTER(Entropy) REGISTER(EntropyRate) REGISTER(MutualInfo) + REGISTER(RelativeEntropy) REGISTER(TransferEntropy) REGISTER(Utilities) END_REGISTRATION diff --git a/test/unittests/relative_entropy.c b/test/unittests/relative_entropy.c new file mode 100644 index 0000000..875e9c1 --- /dev/null +++ b/test/unittests/relative_entropy.c @@ -0,0 +1,314 @@ +// Copyright 2016 ELIFE. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. +#include "util.h" +#include +#include +#include + +UNIT(RelativeEntropyNULLSeries) +{ + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(NULL, NULL, 3, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy((int[]){0,0,1}, NULL, 3, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); +} + +UNIT(RelativeEntropySeriesTooShort) +{ + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(xs, xs, 0, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ESHORTSERIES, err); +} + +UNIT(RelativeEntropyInvalidBase) +{ + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + for (int i = 0; i < 2; ++i) + { + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(xs, xs, 8, i, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + } +} + +UNIT(RelativeEntropyNegativeState) +{ + int const xs[] = {1,1,0,0,-1,0,0,1}; + int const ys[] = {1,1,0,0, 1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(xs, ys, 8, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(ys, xs, 8, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); +} + +UNIT(RelativeEntropyBadState) +{ + int const xs[] = {1,2,0,0,1,0,0,1}; + int const ys[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(xs, ys, 8, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_TRUE(isnan(inform_relative_entropy(xs, ys, 8, 2, 2, &err))); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); +} + +UNIT(RelativeEntropy) +{ + inform_error err = INFORM_SUCCESS; + + ASSERT_DBL_NEAR_TOL(0.038330, inform_relative_entropy((int[]){0,0,1,1,1,1,0,0,0}, + (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.037010, inform_relative_entropy((int[]){1,0,0,1,0,0,1,0,0}, + (int[]){0,0,1,1,1,1,0,0,0}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.000000, inform_relative_entropy((int[]){0,0,0,0,1,1,1,1}, + (int[]){1,1,1,1,0,0,0,0}, 8, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.035770, inform_relative_entropy((int[]){0,0,1,1,1,1,0,0,0}, + (int[]){1,1,0,0,0,0,1,1,1}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.037010, inform_relative_entropy((int[]){1,1,0,1,0,1,1,1,0}, + (int[]){1,1,0,0,0,1,0,1,1}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(1.584963, inform_relative_entropy((int[]){0,0,0,0,0,0,0,0,0}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.038331, inform_relative_entropy((int[]){1,1,1,1,0,0,0,0,1}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.038331, inform_relative_entropy((int[]){1,1,0,0,1,1,0,0,1}, + (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_TRUE(isnan(inform_relative_entropy((int[]){0,1,0,1,0,1,0,1}, + (int[]){0,2,0,2,0,2,0,2}, 8, 3, 2, &err))); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.584963, + inform_relative_entropy((int[]){0,0,0,0,0,0,1,1,1,1,1,1}, + (int[]){0,0,0,0,1,1,1,1,2,2,2,2}, 12, 3, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_TRUE(isnan(inform_relative_entropy((int[]){0,0,1,1,2,1,1,0,0}, + (int[]){0,0,0,1,1,1,0,0,0}, 9, 3, 2, &err))); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.000000, inform_relative_entropy((int[]){0,1,0,0,1,0,0,1,0}, + (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + ASSERT_DBL_NEAR_TOL(0.679964, inform_relative_entropy((int[]){1,0,0,1,0,0,1,0}, + (int[]){2,0,1,2,0,1,2,0}, 8, 3, 2, &err), 1e-6); + ASSERT_EQUAL(INFORM_SUCCESS, err); +} + +UNIT(LocalRelativeEntropyNULLSeries) +{ + double re[8]; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(NULL, NULL, 3, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy((int[]){0,0,1}, NULL, 3, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ETIMESERIES, err); +} + +UNIT(LocalRelativeEntropySeriesTooShort) +{ + double re[8]; + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(xs, xs, 0, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ESHORTSERIES, err); +} + +UNIT(LocalRelativeEntropyInvalidBase) +{ + double re[8]; + int const xs[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + for (int i = 0; i < 2; ++i) + { + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(xs, xs, 8, i, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBASE, err); + } +} + +UNIT(LocalRelativeEntropyNegativeState) +{ + double re[8]; + int const xs[] = {1,1,0,0,-1,0,0,1}; + int const ys[] = {1,1,0,0, 1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(xs, ys, 8, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(ys, xs, 8, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_ENEGSTATE, err); +} + +UNIT(LocalRelativeEntropyBadState) +{ + double re[8]; + int const xs[] = {1,2,0,0,1,0,0,1}; + int const ys[] = {1,1,0,0,1,0,0,1}; + inform_error err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(xs, ys, 8, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); + + err = INFORM_SUCCESS; + ASSERT_NULL(inform_local_relative_entropy(xs, ys, 8, 2, 2, re, &err)); + ASSERT_TRUE(inform_failed(&err)); + ASSERT_EQUAL(INFORM_EBADSTATE, err); +} + +UNIT(LocalRelativeEntropyAllocatesOutput) +{ + int const xs[] = {0,0,1,1,1,1,0,0,0}; + int const ys[] = {1,1,0,0,0,0,1,1,1}; + inform_error err = INFORM_SUCCESS; + double *re = inform_local_relative_entropy(xs, ys, 9, 2, 2, NULL, &err); + ASSERT_NOT_NULL(re); + ASSERT_EQUAL(INFORM_SUCCESS, err); + free(re); +} + +bool dbl_near_tol_arrays(double const *xs, double const *ys, size_t n, double tol) +{ + for (size_t i = 0; i < n; ++i) + { + if (isnan(xs[i]) && isnan(ys[i])) + { + continue; + } + else if (isinf(xs[i]) && isinf(ys[i])) + { + continue; + } + else if (isnan(xs[i]) || isnan(ys[i])) + { + return false; + } + else if (isinf(xs[i]) || isinf(ys[i])) + { + return false; + } + else if (fabs(xs[i] - ys[i]) > tol) + { + return false; + } + } + return true; +} + +UNIT(LocalRelativeEntropy) +{ + inform_error err = INFORM_SUCCESS; + double re_2[2]; + double re_3[3]; + + inform_local_relative_entropy((int[]){0,0,1,1,1,1,0,0,0}, (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){-0.263034, 0.415037}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){1,0,0,1,0,0,1,0,0}, (int[]){0,0,1,1,1,1,0,0,0}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.263034, -0.415037}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,0,0,0,1,1,1,1}, (int[]){1,1,1,1,0,0,0,0}, 8, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.000000, 0.000000}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,0,1,1,1,1,0,0,0}, (int[]){1,1,0,0,0,0,1,1,1}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.321928, -0.321928}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){1,1,0,1,0,1,1,1,0}, (int[]){1,1,0,0,0,1,0,1,1}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){-0.415037, 0.263034}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,0,0,0,0,0,0,0,0}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){1.584963, -INFINITY}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){1,1,1,1,0,0,0,0,1}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.415037, -0.263034}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){1,1,0,0,1,1,0,0,1}, (int[]){1,1,1,0,0,0,1,1,1}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.415037, -0.263034}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,1,0,1,0,1,0,1}, (int[]){0,2,0,2,0,2,0,2}, 8, 3, 2, re_3, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.000000, INFINITY, -INFINITY}, re_3, 3, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,0,0,0,0,0,1,1,1,1,1,1}, (int[]){0,0,0,0,1,1,1,1,2,2,2,2}, 12, 3, 2, re_3, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.584963, 0.584963, -INFINITY}, re_3, 3, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,0,1,1,2,1,1,0,0}, (int[]){0,0,0,1,1,1,0,0,0}, 9, 3, 2, re_3, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){-0.584963, 0.415037, INFINITY}, re_3, 3, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){0,1,0,0,1,0,0,1,0}, (int[]){1,0,0,1,0,0,1,0,0}, 9, 2, 2, re_2, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.000000, 0.000000, -INFINITY}, re_2, 2, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); + + inform_local_relative_entropy((int[]){1,0,0,1,0,0,1,0}, (int[]){2,0,1,2,0,1,2,0}, 8, 3, 2, re_3, &err); + ASSERT_TRUE(dbl_near_tol_arrays((double[]){0.736966, 0.584963, -INFINITY}, re_3, 3, 1e-6)); + ASSERT_EQUAL(INFORM_SUCCESS, err); +} + +BEGIN_SUITE(RelativeEntropy) + ADD_UNIT(RelativeEntropyNULLSeries) + ADD_UNIT(RelativeEntropySeriesTooShort) + ADD_UNIT(RelativeEntropyInvalidBase) + ADD_UNIT(RelativeEntropyNegativeState) + ADD_UNIT(RelativeEntropyBadState) + ADD_UNIT(RelativeEntropy) + ADD_UNIT(LocalRelativeEntropyNULLSeries) + ADD_UNIT(LocalRelativeEntropySeriesTooShort) + ADD_UNIT(LocalRelativeEntropyInvalidBase) + ADD_UNIT(LocalRelativeEntropyNegativeState) + ADD_UNIT(LocalRelativeEntropyBadState) + ADD_UNIT(LocalRelativeEntropyAllocatesOutput) + ADD_UNIT(LocalRelativeEntropy) +END_SUITE