diff --git a/docs.html b/docs.html
index e7837272..db9aa430 100644
--- a/docs.html
+++ b/docs.html
@@ -562,9 +562,10 @@ Conrad Sanderson and Ryan Curtin.
| output streams | | streams for printing warnings and errors |
| uword / sword | | shorthand for unsigned and signed integers |
| cx_double / cx_float | | shorthand for std::complex<double> and std::complex<float> |
+| fp16 | | shorthand for low-precision types, if supported |
| Matlab/Armadillo syntax differences | | examples of Matlab syntax and conceptually corresponding Armadillo syntax |
| example program | | short example program |
-| config.hpp | | configuration options |
+| config.hpp | | configuration options |
| API additions | | API stability and list of API additions |
@@ -604,6 +605,10 @@ The root matrix class is Mat<type>, where type
+
+When compiling with C++23 support, type can also be the low-precision type std::float16_t, if hardware support is detected;
+see the low-precision support section for more details
+
For convenience the following typedefs have been defined:
@@ -698,6 +703,17 @@ For convenience the following typedefs have been defined:
Mat<sword>
+
+
+ fp16_mat
+ |
+
+ =
+ |
+
+ Mat<fp16> (only if supported by compiler and hardware)
+ |
+
@@ -1060,6 +1076,23 @@ For convenience the following typedefs have been defined:
Col<sword>
+
+
+ fp16_vec
+ |
+
+ =
+ |
+
+ fp16_colvec
+ |
+
+ =
+ |
+
+ Col<fp16>
+ |
+
@@ -1311,6 +1344,17 @@ For convenience the following typedefs have been defined:
Row<sword>
+
+
+ fp16_rowvec
+ |
+
+ =
+ |
+
+ Row<fp16> (only if supported by compiler and hardware)
+ |
+
@@ -18394,6 +18438,86 @@ cx_double val = X(2,3);
+
+
+fp16
+
+-
+Convenience short form (typedef) for low-precision element types; compiler and hardware support is required
+
+
+
+-
+The low-precision fp16 type can be used with all Armadillo functions that do not require LAPACK (e.g., almost any operation that is not a decomposition like
svd() or similar)
+
+
+-
+To enable automatic support for low-precision elements, both of the following are necessary:
+
+
+- compiler C++23 support (e.g. compile with
-std=c++23 or similar)
+
+- hardware support for native FP16 operation (not emulated):
+
+- ARM devices require the
+fp16 extension (typically found on ARMv8.2-A+ devices)
+- x86_64 devices require the AVX512-FP16 extension
+
+
+
+-
+If no compiler or hardware support is detected, fp16 will not be available
+
+
+-
+The C++ low-precision types
std::float16_t is preferentially used to define the fp16 type,
+but in certain situations other underlying intrinsic types such as _Float16, __fp16, or similar will be used
+
+
+-
+NOTE:
+Low-precision types can have numerical stability issues,
+and are not suitable for all applications:
+fp16 only has a range from roughly -65536 to 65536!
+
+
+-
+The
ARMA_FORCE_USE_FP16 macro can be defined before including Armadillo
+to force emulated support even when native hardware support is not detected
+
+- Emulated FP16 support can be very slow!
+- Manually forcing support is only recommended when Armadillo's auto-detection is failing;
+- in which case, please send an email to the developers to improve the auto-detection!
+
+
+
+-
+Example:
+
+fp16_mat X(5, 5, fill::randu);
+
+X(1,2) = fp16(1.5);
+
+fp16 val = X(2,3);
+
+
+
+- See also:
+
+
+
+
+
+
+
Examples of Matlab/Octave syntax and conceptually corresponding Armadillo syntax
@@ -20287,6 +20411,7 @@ List of additions and changes for each version:
Version 15.0:
diff --git a/include/armadillo b/include/armadillo
index 497ceec1..57c34805 100644
--- a/include/armadillo
+++ b/include/armadillo
@@ -62,6 +62,10 @@
// #include
// #endif
+#if defined(ARMA_HAVE_CXX23)
+ #include
+#endif
+
#if ( defined(__unix__) || defined(__unix) || defined(_POSIX_C_SOURCE) || (defined(__APPLE__) && defined(__MACH__)) ) && !defined(_WIN32)
#include
#endif
diff --git a/include/armadillo_bits/SpMat_meat.hpp b/include/armadillo_bits/SpMat_meat.hpp
index 9f381b27..16134348 100644
--- a/include/armadillo_bits/SpMat_meat.hpp
+++ b/include/armadillo_bits/SpMat_meat.hpp
@@ -54,6 +54,9 @@ SpMat::~SpMat()
if(values ) { memory::release(access::rw(values)); }
if(row_indices) { memory::release(access::rw(row_indices)); }
if(col_ptrs ) { memory::release(access::rw(col_ptrs)); }
+
+ // FP16 is not currently supported for sparse matrices
+ arma_type_check(( is_fp16::value == true ));
}
diff --git a/include/armadillo_bits/arma_cmath.hpp b/include/armadillo_bits/arma_cmath.hpp
index 7ac283b4..79849181 100644
--- a/include/armadillo_bits/arma_cmath.hpp
+++ b/include/armadillo_bits/arma_cmath.hpp
@@ -198,6 +198,21 @@ arma_isnan(const std::complex& x)
+//
+// wrappers for arma_pow()---see FP16 specialization below
+
+
+
+template
+inline
+eT
+arma_pow(eT base, pow_eT pow)
+ {
+ return std::pow(base, pow);
+ }
+
+
+
//
// implementation of arma_sign()
@@ -417,4 +432,87 @@ struct arma_arg< std::complex >
+//
+// wrappers for low-precision fp16
+
+#if defined(ARMA_HAVE_FP16)
+
+template<>
+inline
+bool
+arma_isfinite(fp16 x)
+ {
+ // Technically not required until C++23 but basically every compiler supports it.
+ // (this is true for almost every fp16 overload below)
+ return std::isfinite(x);
+ }
+
+
+
+template<>
+inline
+bool
+arma_isinf(fp16 x)
+ {
+ return std::isinf(x);
+ }
+
+
+
+template<>
+inline
+bool
+arma_isnan(fp16 x)
+ {
+ return std::isnan(x);
+ }
+
+
+
+template
+inline
+fp16
+arma_pow(fp16 base, pow_eT pow)
+ {
+ return std::pow(base, fp16(pow));
+ }
+
+
+
+template<>
+inline
+fp16
+arma_hypot(const fp16 x, const fp16 y)
+ {
+ return std::hypot(x, y);
+ }
+
+
+
+template<>
+inline
+fp16
+arma_sinc(const fp16 x)
+ {
+ return arma_sinc_generic(x);
+ }
+
+
+
+template<>
+struct arma_arg
+ {
+ static
+ inline
+ fp16
+ eval(const fp16 x)
+ {
+ return std::arg(x);
+ }
+ };
+
+#endif
+
+
+
//! @}
diff --git a/include/armadillo_bits/diskio_meat.hpp b/include/armadillo_bits/diskio_meat.hpp
index b447ccb8..262173cd 100644
--- a/include/armadillo_bits/diskio_meat.hpp
+++ b/include/armadillo_bits/diskio_meat.hpp
@@ -22,7 +22,7 @@
//! Generate the first line of the header used for saving matrices in text format.
//! Format: "ARMA_MAT_TXT_ABXYZ".
-//! A is one of: I (for integral types) or F (for floating point types).
+//! A is one of: I (for integral types) or F (for floating point types)
//! B is one of: U (for unsigned types), S (for signed types), N (for not applicable) or C (for complex types).
//! XYZ specifies the width of each element in terms of bytes, eg. "008" indicates eight bytes.
template
@@ -40,6 +40,7 @@ diskio::gen_txt_header(const Mat&)
const char* ARMA_MAT_TXT_IS004 = "ARMA_MAT_TXT_IS004";
const char* ARMA_MAT_TXT_IU008 = "ARMA_MAT_TXT_IU008";
const char* ARMA_MAT_TXT_IS008 = "ARMA_MAT_TXT_IS008";
+ const char* ARMA_MAT_TXT_FN002 = "ARMA_MAT_TXT_FN002";
const char* ARMA_MAT_TXT_FN004 = "ARMA_MAT_TXT_FN004";
const char* ARMA_MAT_TXT_FN008 = "ARMA_MAT_TXT_FN008";
const char* ARMA_MAT_TXT_FC008 = "ARMA_MAT_TXT_FC008";
@@ -61,6 +62,7 @@ diskio::gen_txt_header(const Mat&)
else if(is_slng_t_64::value) { header = const_cast(ARMA_MAT_TXT_IS008); }
else if( is_float::value) { header = const_cast(ARMA_MAT_TXT_FN004); }
else if( is_double::value) { header = const_cast(ARMA_MAT_TXT_FN008); }
+ else if( is_fp16::value) { header = const_cast(ARMA_MAT_TXT_FN002); }
else if( is_cx_float::value) { header = const_cast(ARMA_MAT_TXT_FC008); }
else if(is_cx_double::value) { header = const_cast(ARMA_MAT_TXT_FC016); }
@@ -89,6 +91,7 @@ diskio::gen_bin_header(const Mat&)
const char* ARMA_MAT_BIN_IS004 = "ARMA_MAT_BIN_IS004";
const char* ARMA_MAT_BIN_IU008 = "ARMA_MAT_BIN_IU008";
const char* ARMA_MAT_BIN_IS008 = "ARMA_MAT_BIN_IS008";
+ const char* ARMA_MAT_BIN_FN002 = "ARMA_MAT_BIN_FN002";
const char* ARMA_MAT_BIN_FN004 = "ARMA_MAT_BIN_FN004";
const char* ARMA_MAT_BIN_FN008 = "ARMA_MAT_BIN_FN008";
const char* ARMA_MAT_BIN_FC008 = "ARMA_MAT_BIN_FC008";
@@ -110,6 +113,7 @@ diskio::gen_bin_header(const Mat&)
else if(is_slng_t_64::value) { header = const_cast(ARMA_MAT_BIN_IS008); }
else if( is_float::value) { header = const_cast(ARMA_MAT_BIN_FN004); }
else if( is_double::value) { header = const_cast(ARMA_MAT_BIN_FN008); }
+ else if( is_fp16::value) { header = const_cast(ARMA_MAT_BIN_FN002); }
else if( is_cx_float::value) { header = const_cast(ARMA_MAT_BIN_FC008); }
else if(is_cx_double::value) { header = const_cast(ARMA_MAT_BIN_FC016); }
@@ -138,10 +142,11 @@ diskio::gen_bin_header(const SpMat&)
const char* ARMA_SPM_BIN_IS004 = "ARMA_SPM_BIN_IS004";
const char* ARMA_SPM_BIN_IU008 = "ARMA_SPM_BIN_IU008";
const char* ARMA_SPM_BIN_IS008 = "ARMA_SPM_BIN_IS008";
+ //const char* ARMA_SPM_BIN_FN002 = "ARMA_SPM_BIN_FN002";
const char* ARMA_SPM_BIN_FN004 = "ARMA_SPM_BIN_FN004";
const char* ARMA_SPM_BIN_FN008 = "ARMA_SPM_BIN_FN008";
const char* ARMA_SPM_BIN_FC008 = "ARMA_SPM_BIN_FC008";
- const char* ARMA_SPM_BIN_FC016 = "ARMA_SPM_BIN_FC016";
+ const char* ARMA_SPM_BIN_FC016 = "ARMA_SPM_BIN_FC016";
char* header = nullptr;
@@ -159,6 +164,7 @@ diskio::gen_bin_header(const SpMat&)
else if(is_slng_t_64::value) { header = const_cast(ARMA_SPM_BIN_IS008); }
else if( is_float::value) { header = const_cast(ARMA_SPM_BIN_FN004); }
else if( is_double::value) { header = const_cast(ARMA_SPM_BIN_FN008); }
+ //else if( is_fp16::value) { header = const_cast(ARMA_SPM_BIN_FN002); }
else if( is_cx_float::value) { header = const_cast(ARMA_SPM_BIN_FC008); }
else if(is_cx_double::value) { header = const_cast(ARMA_SPM_BIN_FC016); }
@@ -186,6 +192,7 @@ diskio::gen_txt_header(const Cube&)
const char* ARMA_CUB_TXT_IS004 = "ARMA_CUB_TXT_IS004";
const char* ARMA_CUB_TXT_IU008 = "ARMA_CUB_TXT_IU008";
const char* ARMA_CUB_TXT_IS008 = "ARMA_CUB_TXT_IS008";
+ const char* ARMA_CUB_TXT_FN002 = "ARMA_CUB_TXT_FN002";
const char* ARMA_CUB_TXT_FN004 = "ARMA_CUB_TXT_FN004";
const char* ARMA_CUB_TXT_FN008 = "ARMA_CUB_TXT_FN008";
const char* ARMA_CUB_TXT_FC008 = "ARMA_CUB_TXT_FC008";
@@ -207,6 +214,7 @@ diskio::gen_txt_header(const Cube&)
else if(is_slng_t_64::value) { header = const_cast(ARMA_CUB_TXT_IS008); }
else if( is_float::value) { header = const_cast(ARMA_CUB_TXT_FN004); }
else if( is_double::value) { header = const_cast(ARMA_CUB_TXT_FN008); }
+ else if( is_fp16::value) { header = const_cast(ARMA_CUB_TXT_FN002); }
else if( is_cx_float::value) { header = const_cast(ARMA_CUB_TXT_FC008); }
else if(is_cx_double::value) { header = const_cast(ARMA_CUB_TXT_FC016); }
@@ -235,6 +243,7 @@ diskio::gen_bin_header(const Cube&)
const char* ARMA_CUB_BIN_IS004 = "ARMA_CUB_BIN_IS004";
const char* ARMA_CUB_BIN_IU008 = "ARMA_CUB_BIN_IU008";
const char* ARMA_CUB_BIN_IS008 = "ARMA_CUB_BIN_IS008";
+ const char* ARMA_CUB_BIN_FN002 = "ARMA_CUB_BIN_FN002";
const char* ARMA_CUB_BIN_FN004 = "ARMA_CUB_BIN_FN004";
const char* ARMA_CUB_BIN_FN008 = "ARMA_CUB_BIN_FN008";
const char* ARMA_CUB_BIN_FC008 = "ARMA_CUB_BIN_FC008";
@@ -256,6 +265,7 @@ diskio::gen_bin_header(const Cube&)
else if(is_slng_t_64::value) { header = const_cast(ARMA_CUB_BIN_IS008); }
else if( is_float::value) { header = const_cast(ARMA_CUB_BIN_FN004); }
else if( is_double::value) { header = const_cast(ARMA_CUB_BIN_FN008); }
+ else if( is_fp16::value) { header = const_cast(ARMA_CUB_BIN_FN002); }
else if( is_cx_float::value) { header = const_cast(ARMA_CUB_BIN_FC008); }
else if(is_cx_double::value) { header = const_cast(ARMA_CUB_BIN_FC016); }
@@ -655,7 +665,7 @@ diskio::prepare_stream(std::ostream& f)
// NOTE: for 'float' the optimum settings are f.precision(8) and cell_width = 15
// NOTE: however, to avoid introducing errors in case single precision data is loaded as double precision,
- // NOTE: the same settings must be used for both 'float' and 'double'
+ // NOTE: the same settings must be used for both 'float' and 'double' (and other floating-point types)
}
else
if(is_cx::value)
diff --git a/include/armadillo_bits/fill.hpp b/include/armadillo_bits/fill.hpp
index 8b410977..a7a46559 100644
--- a/include/armadillo_bits/fill.hpp
+++ b/include/armadillo_bits/fill.hpp
@@ -49,6 +49,9 @@ namespace fill
template<> struct allow_conversion, double> { static constexpr bool value = false; };
template<> struct allow_conversion, float > { static constexpr bool value = false; };
+ #if defined(ARMA_HAVE_FP16)
+ template<> struct allow_conversion, fp16 > { static constexpr bool value = false; };
+ #endif
template<> struct allow_conversion, u64 > { static constexpr bool value = false; };
template<> struct allow_conversion, s64 > { static constexpr bool value = false; };
template<> struct allow_conversion, u32 > { static constexpr bool value = false; };
@@ -60,6 +63,9 @@ namespace fill
template<> struct allow_conversion, double> { static constexpr bool value = false; };
template<> struct allow_conversion, float > { static constexpr bool value = false; };
+ #if defined(ARMA_HAVE_FP16)
+ template<> struct allow_conversion, fp16 > { static constexpr bool value = false; };
+ #endif
template<> struct allow_conversion, u64 > { static constexpr bool value = false; };
template<> struct allow_conversion, s64 > { static constexpr bool value = false; };
template<> struct allow_conversion, u32 > { static constexpr bool value = false; };
@@ -74,6 +80,9 @@ namespace fill
template inline bool isfinite_wrapper(eT ) { return true; }
template<> inline bool isfinite_wrapper(float x) { return std::isfinite(x); }
template<> inline bool isfinite_wrapper(double x) { return std::isfinite(x); }
+ #if defined(ARMA_HAVE_FP16)
+ template<> inline bool isfinite_wrapper(fp16 x) { return std::isfinite(x); }
+ #endif
template inline bool isfinite_wrapper(std::complex& x) { return std::isfinite(x.real()) && std::isfinite(x.imag()); }
//
diff --git a/include/armadillo_bits/fn_chi2rnd.hpp b/include/armadillo_bits/fn_chi2rnd.hpp
index 4e185785..23d88be6 100644
--- a/include/armadillo_bits/fn_chi2rnd.hpp
+++ b/include/armadillo_bits/fn_chi2rnd.hpp
@@ -42,10 +42,20 @@ typename arma_real_only::result
chi2rnd(const eT df)
{
arma_debug_sigprint();
-
- op_chi2rnd_varying_df generator;
-
- return generator(df);
+
+ if(is_fp16::yes)
+ {
+ // std::chi_squared_distribution is undefined for types other than float, double, and long double
+ op_chi2rnd_varying_df generator;
+
+ return eT(generator(df));
+ }
+ else
+ {
+ op_chi2rnd_varying_df generator;
+
+ return generator(df);
+ }
}
diff --git a/include/armadillo_bits/fn_expmat.hpp b/include/armadillo_bits/fn_expmat.hpp
index cab6891b..d7c13a34 100644
--- a/include/armadillo_bits/fn_expmat.hpp
+++ b/include/armadillo_bits/fn_expmat.hpp
@@ -26,7 +26,7 @@ inline
typename
enable_if2
<
- is_real::value,
+ is_blas_real::value,
const Op
>::result
expmat(const Base& A)
@@ -43,7 +43,7 @@ inline
typename
enable_if2
<
- is_real::value,
+ is_blas_real::value,
bool
>::result
expmat(Mat& B, const Base& A)
diff --git a/include/armadillo_bits/fn_svds.hpp b/include/armadillo_bits/fn_svds.hpp
index 06288443..4153a2c4 100644
--- a/include/armadillo_bits/fn_svds.hpp
+++ b/include/armadillo_bits/fn_svds.hpp
@@ -25,14 +25,14 @@ inline
bool
svds_helper
(
- Mat& U,
- Col& S,
- Mat& V,
- const SpBase& X,
- const uword k,
- const typename T1::pod_type tol,
- const bool calc_UV,
- const typename arma_real_only::result* junk = nullptr
+ Mat& U,
+ Col& S,
+ Mat& V,
+ const SpBase& X,
+ const uword k,
+ const typename T1::pod_type tol,
+ const bool calc_UV,
+ const typename arma_blas_real_only::result* junk = nullptr
)
{
arma_debug_sigprint();
@@ -270,13 +270,13 @@ inline
bool
svds
(
- Mat& U,
- Col& S,
- Mat& V,
- const SpBase& X,
- const uword k,
- const typename T1::pod_type tol = 0.0,
- const typename arma_real_or_cx_only::result* junk = nullptr
+ Mat& U,
+ Col& S,
+ Mat& V,
+ const SpBase& X,
+ const uword k,
+ const typename T1::pod_type tol = 0.0,
+ const typename arma_blas_real_or_cx_only::result* junk = nullptr
)
{
arma_debug_sigprint();
@@ -297,11 +297,11 @@ inline
bool
svds
(
- Col& S,
- const SpBase& X,
- const uword k,
- const typename T1::pod_type tol = 0.0,
- const typename arma_real_or_cx_only::result* junk = nullptr
+ Col& S,
+ const SpBase& X,
+ const uword k,
+ const typename T1::pod_type tol = 0.0,
+ const typename arma_blas_real_or_cx_only::result* junk = nullptr
)
{
arma_debug_sigprint();
@@ -326,10 +326,10 @@ inline
Col
svds
(
- const SpBase& X,
- const uword k,
- const typename T1::pod_type tol = 0.0,
- const typename arma_real_or_cx_only::result* junk = nullptr
+ const SpBase& X,
+ const uword k,
+ const typename T1::pod_type tol = 0.0,
+ const typename arma_blas_real_or_cx_only::result* junk = nullptr
)
{
arma_debug_sigprint();
diff --git a/include/armadillo_bits/hdf5_misc.hpp b/include/armadillo_bits/hdf5_misc.hpp
index 0dd4a7a1..9bc5e10b 100644
--- a/include/armadillo_bits/hdf5_misc.hpp
+++ b/include/armadillo_bits/hdf5_misc.hpp
@@ -139,6 +139,24 @@ get_hdf5_type< double >()
+#if defined(ARMA_HAVE_FP16) && defined(H5_HAVE__FLOAT16)
+template<>
+inline
+hid_t
+get_hdf5_type< fp16 >()
+ {
+ return H5Tcopy(H5T_NATIVE_FLOAT16);
+ }
+#endif
+
+
+
+// NOTE: HDF5 has discussed adding bf16 support but it is not yet available.
+// https://github.com/HDFGroup/hdf5/issues/5317
+// https://forum.hdfgroup.org/t/hdf5-rfc-adding-support-for-16-bit-floating-point-and-complex-number-datatypes-to-hdf5/11975/29
+
+
+
//! Utility hid_t since HOFFSET() won't work with std::complex.
template
struct hdf5_complex_t
@@ -264,6 +282,14 @@ is_supported_arma_hdf5_type(hid_t datatype)
H5Tclose(search_type);
if(is_equal) { return true; }
+ // check types that may or may not be supported
+ #if defined(ARMA_HAVE_FP16) && defined(H5_HAVE__FLOAT16)
+ search_type = get_hdf5_type();
+ is_equal = ( H5Tequal(datatype, search_type) > 0 );
+ H5Tclose(search_type);
+ if(is_equal) { return true; }
+ #endif
+
return false;
}
diff --git a/include/armadillo_bits/mul_herk.hpp b/include/armadillo_bits/mul_herk.hpp
index 442f734d..61c236c1 100644
--- a/include/armadillo_bits/mul_herk.hpp
+++ b/include/armadillo_bits/mul_herk.hpp
@@ -484,7 +484,7 @@ class herk
{
herk::apply_blas_type(C,A,alpha,beta);
}
-
+
};
diff --git a/include/armadillo_bits/op_chi2rnd_meat.hpp b/include/armadillo_bits/op_chi2rnd_meat.hpp
index 93925832..a6fcec3d 100644
--- a/include/armadillo_bits/op_chi2rnd_meat.hpp
+++ b/include/armadillo_bits/op_chi2rnd_meat.hpp
@@ -56,8 +56,10 @@ op_chi2rnd::apply_noalias(Mat& out, const Proxy& P)
arma_debug_sigprint();
typedef typename T1::elem_type eT;
+ // we can only make a generator for float/double/long double types
+ typedef typename promote_type::result gT;
- op_chi2rnd_varying_df generator;
+ op_chi2rnd_varying_df generator;
const uword n_rows = P.get_n_rows();
const uword n_cols = P.get_n_cols();
@@ -74,7 +76,7 @@ op_chi2rnd::apply_noalias(Mat& out, const Proxy& P)
for(uword i=0; i& out, const Proxy& P)
for(uword col=0; col < n_cols; ++col)
for(uword row=0; row < n_rows; ++row)
{
- (*out_mem) = generator( P.at(row,col) ); ++out_mem;
+ (*out_mem) = eT( generator( P.at(row,col) ) ); ++out_mem;
}
}
}
diff --git a/include/armadillo_bits/op_dot_bones.hpp b/include/armadillo_bits/op_dot_bones.hpp
index d4b0674b..c8e05bad 100644
--- a/include/armadillo_bits/op_dot_bones.hpp
+++ b/include/armadillo_bits/op_dot_bones.hpp
@@ -38,9 +38,13 @@ class op_dot
direct_dot_arma(const uword n_elem, const eT* const A, const eT* const B);
template
- arma_hot inline static typename arma_real_only::result
+ arma_hot inline static typename arma_blas_real_only::result
direct_dot(const uword n_elem, const eT* const A, const eT* const B);
-
+
+ template
+ arma_hot inline static typename arma_fp16_only::result
+ direct_dot(const uword n_elem, const eT* const A, const eT* const B);
+
template
arma_hot inline static typename arma_cx_only::result
direct_dot(const uword n_elem, const eT* const A, const eT* const B);
@@ -91,8 +95,11 @@ class op_cdot
arma_hot inline static eT direct_cdot_arma(const uword n_elem, const eT* const A, const eT* const B);
template
- arma_hot inline static eT direct_cdot(const uword n_elem, const eT* const A, const eT* const B);
-
+ arma_hot inline static eT direct_cdot(const uword n_elem, const eT* const A, const eT* const B, const typename arma_blas_real_or_cx_only::result* junk = 0);
+
+ template
+ arma_hot inline static eT direct_cdot(const uword n_elem, const eT* const A, const eT* const B, const typename arma_fp16_only::result* junk = 0);
+
template
arma_hot inline static typename T1::elem_type apply (const T1& X, const T2& Y);
diff --git a/include/armadillo_bits/op_dot_meat.hpp b/include/armadillo_bits/op_dot_meat.hpp
index abbb6e94..e857c666 100644
--- a/include/armadillo_bits/op_dot_meat.hpp
+++ b/include/armadillo_bits/op_dot_meat.hpp
@@ -98,16 +98,16 @@ op_dot::direct_dot_arma(const uword n_elem, const eT* const A, const eT* const B
-//! for two arrays, float and double version
+//! for two arrays, floating-point version
template
inline
-typename arma_real_only::result
+typename arma_blas_real_only::result
op_dot::direct_dot(const uword n_elem, const eT* const A, const eT* const B)
{
arma_debug_sigprint();
if(n_elem <= 32u) { return op_dot::direct_dot_arma(n_elem, A, B); }
-
+
#if defined(ARMA_USE_ATLAS)
{
arma_debug_print("atlas::cblas_dot()");
@@ -129,6 +129,19 @@ op_dot::direct_dot(const uword n_elem, const eT* const A, const eT* const B)
+//! for two arrays, fp16 version
+template
+inline
+typename arma_fp16_only::result
+op_dot::direct_dot(const uword n_elem, const eT* const A, const eT* const B)
+ {
+ arma_debug_sigprint();
+
+ return op_dot::direct_dot_arma(n_elem, A, B);
+ }
+
+
+
//! for two arrays, complex version
template
inline
@@ -447,9 +460,10 @@ op_cdot::direct_cdot_arma(const uword n_elem, const eT* const A, const eT* const
template
inline
eT
-op_cdot::direct_cdot(const uword n_elem, const eT* const A, const eT* const B)
+op_cdot::direct_cdot(const uword n_elem, const eT* const A, const eT* const B, const typename arma_blas_real_or_cx_only::result* junk)
{
arma_debug_sigprint();
+ arma_ignore(junk);
if(n_elem <= 32u) { return op_cdot::direct_cdot_arma(n_elem, A, B); }
@@ -485,6 +499,19 @@ op_cdot::direct_cdot(const uword n_elem, const eT* const A, const eT* const B)
+template
+inline
+eT
+op_cdot::direct_cdot(const uword n_elem, const eT* const A, const eT* const B, const typename arma_fp16_only::result* junk)
+ {
+ arma_debug_sigprint();
+ arma_ignore(junk);
+
+ return op_cdot::direct_cdot_arma(n_elem, A, B);
+ }
+
+
+
template
inline
typename T1::elem_type
diff --git a/include/armadillo_bits/op_norm2est_meat.hpp b/include/armadillo_bits/op_norm2est_meat.hpp
index 953cb168..8b7275aa 100644
--- a/include/armadillo_bits/op_norm2est_meat.hpp
+++ b/include/armadillo_bits/op_norm2est_meat.hpp
@@ -120,12 +120,24 @@ op_norm2est::norm2est
if((A.n_rows == 1) || (A.n_cols == 1)) { return op_norm::vec_norm_2( Proxy< Mat >(A) ); }
- norm2est_randu_filler randu_filler;
+ // low-precision types cannot be used for norm2est_randu_filler
+ // (std::uniform_real_distribution is undefined for types not float/double/long double)
+ norm2est_randu_filler< typename promote_type::result > randu_filler;
Col x(A.n_rows, fill::none);
Col y(A.n_cols, fill::none);
- randu_filler.fill(y.memptr(), y.n_elem);
+ if (is_fp16::yes)
+ {
+ // randu_filler can only fill floats, so do that and then convert
+ Col tmp(y.n_elem);
+ randu_filler.fill(tmp.memptr(), tmp.n_elem);
+ arrayops::convert(y.memptr(), tmp.memptr(), tmp.n_elem);
+ }
+ else
+ {
+ randu_filler.fill(y.memptr(), y.n_elem);
+ }
T est_old = 0;
T est_cur = 0;
diff --git a/include/armadillo_bits/op_norm_bones.hpp b/include/armadillo_bits/op_norm_bones.hpp
index f4023383..c9e25087 100644
--- a/include/armadillo_bits/op_norm_bones.hpp
+++ b/include/armadillo_bits/op_norm_bones.hpp
@@ -27,12 +27,14 @@ class op_norm
template arma_hot inline static typename T1::pod_type vec_norm_1(const Proxy& P, const typename arma_not_cx::result* junk = nullptr);
template arma_hot inline static typename T1::pod_type vec_norm_1(const Proxy& P, const typename arma_cx_only::result* junk = nullptr);
- template arma_hot inline static eT vec_norm_1_direct_std(const Mat& X);
+ template arma_hot inline static eT vec_norm_1_direct_std(const Mat& X, const typename arma_blas_real_only::result* junk = nullptr);
+ template arma_hot inline static eT vec_norm_1_direct_std(const Mat& X, const typename arma_fp16_only::result* junk = nullptr);
template arma_hot inline static eT vec_norm_1_direct_mem(const uword N, const eT* A);
template arma_hot inline static typename T1::pod_type vec_norm_2(const Proxy& P, const typename arma_not_cx::result* junk = nullptr);
template arma_hot inline static typename T1::pod_type vec_norm_2(const Proxy& P, const typename arma_cx_only::result* junk = nullptr);
- template arma_hot inline static eT vec_norm_2_direct_std(const Mat& X);
+ template arma_hot inline static eT vec_norm_2_direct_std(const Mat& X, const typename arma_blas_real_only::result* junk = nullptr);
+ template arma_hot inline static eT vec_norm_2_direct_std(const Mat& X, const typename arma_fp16_only::result* junk = nullptr);
template arma_hot inline static eT vec_norm_2_direct_mem(const uword N, const eT* A);
template arma_hot inline static eT vec_norm_2_direct_robust(const Mat& X);
@@ -42,7 +44,8 @@ class op_norm
template arma_hot inline static typename T1::pod_type vec_norm_min(const Proxy& P);
template inline static typename get_pod_type::result mat_norm_1(const Mat& X);
- template inline static typename get_pod_type::result mat_norm_2(const Mat& X);
+ template inline static typename get_pod_type::result mat_norm_2(const Mat& X, typename arma_blas_real_or_cx_only::result* junk = nullptr);
+ template inline static typename get_pod_type::result mat_norm_2(const Mat& X, typename arma_fp16_only::result* junk = nullptr);
template inline static typename get_pod_type::result mat_norm_inf(const Mat& X);
};
diff --git a/include/armadillo_bits/op_norm_meat.hpp b/include/armadillo_bits/op_norm_meat.hpp
index 210dfd8f..d64b5942 100644
--- a/include/armadillo_bits/op_norm_meat.hpp
+++ b/include/armadillo_bits/op_norm_meat.hpp
@@ -217,15 +217,16 @@ op_norm::vec_norm_1(const Proxy& P, const typename arma_cx_only
inline
eT
-op_norm::vec_norm_1_direct_std(const Mat& X)
+op_norm::vec_norm_1_direct_std(const Mat& X, const typename arma_blas_real_only::result* junk)
{
arma_debug_sigprint();
+ arma_ignore(junk);
const uword N = X.n_elem;
const eT* A = X.memptr();
eT out_val = eT(0);
-
+
#if defined(ARMA_USE_ATLAS)
{
arma_debug_print("atlas::cblas_asum()");
@@ -254,6 +255,24 @@ op_norm::vec_norm_1_direct_std(const Mat& X)
+template
+inline
+eT
+op_norm::vec_norm_1_direct_std(const Mat& X, const typename arma_fp16_only::result* junk)
+ {
+ arma_debug_sigprint();
+ arma_ignore(junk);
+
+ const uword N = X.n_elem;
+ const eT* A = X.memptr();
+
+ // fp16 support must be direct non-BLAS
+ eT out_val = op_norm::vec_norm_1_direct_mem(N,A);
+ return (out_val <= eT(0)) ? eT(0) : out_val;
+ }
+
+
+
template
inline
eT
@@ -518,9 +537,10 @@ op_norm::vec_norm_2(const Proxy& P, const typename arma_cx_only
inline
eT
-op_norm::vec_norm_2_direct_std(const Mat& X)
+op_norm::vec_norm_2_direct_std(const Mat& X, const typename arma_blas_real_only::result* junk)
{
arma_debug_sigprint();
+ arma_ignore(junk);
const uword N = X.n_elem;
const eT* A = X.memptr();
@@ -564,6 +584,34 @@ op_norm::vec_norm_2_direct_std(const Mat& X)
+template
+inline
+eT
+op_norm::vec_norm_2_direct_std(const Mat& X, const typename arma_fp16_only::result* junk)
+ {
+ arma_debug_sigprint();
+ arma_ignore(junk);
+
+ const uword N = X.n_elem;
+ const eT* A = X.memptr();
+
+ // fp16 support must be non-BLAS
+ eT out_val = op_norm::vec_norm_2_direct_mem(N,A);
+
+ if( (out_val != eT(0)) && arma_isfinite(out_val) )
+ {
+ return (out_val < eT(0)) ? eT(0) : out_val;
+ }
+ else
+ {
+ arma_debug_print("detected possible underflow or overflow");
+
+ return op_norm::vec_norm_2_direct_robust(X);
+ }
+ }
+
+
+
template