Compare commits

...
14 Commits
Author SHA1 Message Date
conrad d1e03a5f1b fix spelling 2025-10-30 09:59:01 +10:00
conrad 9abc29ce78 simplifications 2025-09-29 22:51:10 +10:00
conrad bb2a42e6d1 add brief explanation of fill_form 2025-09-29 12:24:10 +10:00
conrad e677071ca3 fix name 2025-09-25 13:36:11 +10:00
conrad 141e1f1e36 patch bump 2025-09-22 12:02:20 +10:00
conrad 369041632a use quasi_unwrap for consistency with Base::print() 2025-09-19 00:46:49 +10:00
conrad dbb6c5f583 better limits on integer representation of floating point values 2025-09-16 11:51:48 +10:00
conrad def86fdf86 use internal function 2025-09-15 10:16:19 +10:00
conrad bb8bdf3458 patch bump 2025-09-08 13:41:04 +10:00
conrad 3edd07b103 remove docs for redundant stable_sort_index() 2025-09-07 23:20:29 +10:00
conrad 962f1385b1 reduce number of trials 2025-09-05 14:12:33 +10:00
conrad d449a8d6a0 handle corner-case 2025-09-05 11:30:42 +10:00
conrad 4c66933553 faster handling of tubes 2025-09-05 00:29:53 +10:00
conrad 4424452a27 openmp parallelisation for matrix mul with non-BLAS element types 2025-09-02 10:50:01 +10:00
11 changed files with 506 additions and 115 deletions
+45 -77
View File
@@ -701,16 +701,6 @@ For convenience, the following matrix typedefs are defined:
<code>Mat&lt;<a href="#uword">sword</a>&gt;</code>
</td>
</tr>
</tbody>
</table>
</li>
<br>
<li>
If supported by compiler and hardware, additional typedefs for matrices with half-precision element types are defined as:
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: right;">
<code>hmat</code>
@@ -721,6 +711,9 @@ If supported by compiler and hardware, additional typedefs for matrices with hal
<td style="vertical-align: top;">
<code>Mat&lt;<a href="#fp16_type">fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
<tr>
<td style="vertical-align: top; text-align: right;">
@@ -732,6 +725,9 @@ If supported by compiler and hardware, additional typedefs for matrices with hal
<td style="vertical-align: top;">
<code>Mat&lt;<a href="#fp16_type">cx_fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
</tbody>
</table>
@@ -776,7 +772,7 @@ Constructors:
</li>
<br>
<li>
The elements can be explicitly initialised during construction by specifying <i>fill_form</i>,
Matrix elements can be explicitly initialised during construction by specifying <i>fill_form</i>,
which is one of:
<ul>
<table>
@@ -1095,20 +1091,6 @@ For convenience, the following column vector typedefs are defined:
<code>Col&lt;<a href="#uword">sword</a>&gt;</code>
</td>
</tr>
</tbody>
</table>
</li>
<br>
<li>
The <b><i>vec</i></b> and <b><i>colvec</i></b> types have the <b>same meaning</b> and are used <b>interchangeably</b>
</li>
<br>
<li>
If supported by compiler and hardware, additional typedefs for column vectors with half-precision element types are defined as:
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: right;">
<code>hvec</code>
@@ -1125,6 +1107,9 @@ If supported by compiler and hardware, additional typedefs for column vectors wi
<td style="vertical-align: top;">
<code>Col&lt;<a href="#fp16_type">fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
<tr>
<td style="vertical-align: top; text-align: right;">
@@ -1142,12 +1127,19 @@ If supported by compiler and hardware, additional typedefs for column vectors wi
<td style="vertical-align: top;">
<code>Col&lt;<a href="#fp16_type">cx_fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
</tbody>
</table>
</li>
<br>
<li>
The <b><i>vec</i></b> and <b><i>colvec</i></b> types have the <b>same meaning</b> and are used <b>interchangeably</b>
</li>
<br>
<li>
In this documentation, the types <i>vec</i> or <i>colvec</i> are used for convenience; it is possible to use other column vector types instead, eg. <i>fvec</i>, <i>fcolvec</i>
</li>
<br>
@@ -1178,11 +1170,7 @@ Constructors:
</li>
<br>
<li>
<b>Caveat:</b>
<ul>
<li>in Armadillo &gt;= 10.5, the default initialisation is <code>fill::zeros</code></li>
<li>in Armadillo &lt;= 10.4, the default initialisation is <code>fill::none</code></li>
</ul>
Vector elements can be explicitly initialised during construction by specifying <i>fill_form</i>, as per the <a href="#Mat">Mat</a> class
</li>
<br>
<a name="adv_constructors_col"></a>
@@ -1390,16 +1378,6 @@ For convenience, the following row vector typedefs are defined:
<code>Row&lt;<a href="#uword">sword</a>&gt;</code>
</td>
</tr>
</tbody>
</table>
</li>
<br>
<li>
If supported by compiler and hardware, additional typedefs for row vectors with half-precision element types are defined as:
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: right;">
<code>hrowvec</code>
@@ -1410,6 +1388,9 @@ If supported by compiler and hardware, additional typedefs for row vectors with
<td style="vertical-align: top;">
<code>Row&lt;<a href="#fp16_type">fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
<tr>
<td style="vertical-align: top; text-align: right;">
@@ -1421,6 +1402,9 @@ If supported by compiler and hardware, additional typedefs for row vectors with
<td style="vertical-align: top;">
<code>Row&lt;<a href="#fp16_type">cx_fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
</tbody>
</table>
@@ -1458,11 +1442,7 @@ Constructors:
</li>
<br>
<li>
<b>Caveat:</b>
<ul>
<li>in Armadillo &gt;= 10.5, the default initialisation is <code>fill::zeros</code></li>
<li>in Armadillo &lt;= 10.4, the default initialisation is <code>fill::none</code></li>
</ul>
Vector elements can be explicitly initialised during construction by specifying <i>fill_form</i>, as per the <a href="#Mat">Mat</a> class
</li>
<br>
<a name="adv_constructors_row"></a>
@@ -1680,16 +1660,6 @@ For convenience, the following cube typedefs are defined:
<code>Cube&lt;<a href="#uword">sword</a>&gt;</code>
</td>
</tr>
</tbody>
</table>
</li>
<br>
<li>
If supported by compiler and hardware, additional typedefs for cubes with half-precision element types are defined as:
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: right;">
<code>hcube</code>
@@ -1700,6 +1670,9 @@ If supported by compiler and hardware, additional typedefs for cubes with half-p
<td style="vertical-align: top;">
<code>Cube&lt;<a href="#fp16_type">fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
<tr>
<td style="vertical-align: top; text-align: right;">
@@ -1711,6 +1684,9 @@ If supported by compiler and hardware, additional typedefs for cubes with half-p
<td style="vertical-align: top;">
<code>Cube&lt;<a href="#fp16_type">cx_fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
</tbody>
</table>
@@ -1740,7 +1716,7 @@ Constructors:
</li>
<br>
<li>
The elements can be explicitly initialised during construction by specifying <i>fill_form</i>,
Cube elements can be explicitly initialised during construction by specifying <i>fill_form</i>,
which is one of:
<ul>
<table>
@@ -1966,7 +1942,8 @@ The root sparse matrix class is <b>SpMat&lt;</b><i>type</i><b>&gt;</b>, where <i
<br>
<li>
For convenience, the following sparse matrix typedefs are defined:
<ul>
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
@@ -2057,17 +2034,6 @@ For convenience, the following sparse matrix typedefs are defined:
<code>SpMat&lt;<a href="#uword">sword</a>&gt;</code>
</td>
</tr>
</tbody>
</table>
</ul>
</li>
<br>
<li>
If supported by compiler and hardware, additional typedefs for sparse matrices with half-precision element types are defined as:
<br>
<br>
<table style="text-align: left;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: right;">
<code>sp_hmat</code>
@@ -2078,6 +2044,9 @@ If supported by compiler and hardware, additional typedefs for sparse matrices w
<td style="vertical-align: top;">
<code>SpMat&lt;<a href="#fp16_type">fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
<tr>
<td style="vertical-align: top; text-align: right;">
@@ -2089,6 +2058,9 @@ If supported by compiler and hardware, additional typedefs for sparse matrices w
<td style="vertical-align: top;">
<code>SpMat&lt;<a href="#fp16_type">cx_fp16</a>&gt;</code>
</td>
<td style="vertical-align: top;">
&nbsp;(only available if supported by compiler and hardware)
</td>
</tr>
</tbody>
</table>
@@ -8118,7 +8090,7 @@ Generate a scalar, vector, matrix or cube with the elements set to random values
<td style="vertical-align: top;">&nbsp;</td><td style="vertical-align: top;">&nbsp;</td><td style="vertical-align: top;"><i>x<sup>&thinsp;a-1</sup> exp(&thinsp;-x&thinsp;/&thinsp;b&thinsp;)</i></td>
</tr>
<tr>
<td style="vertical-align: top;"><i>p<font size=+1>(</font>x&thinsp;|&thinsp;a,b<font size=+1>)</font></i></td><td style="vertical-align: top;">&nbsp;<font size=+1>=</font>&nbsp;</td><td style="vertical-align: top;"><font size=+1><b><hr></b></font></td>
<td style="vertical-align: top;"><i>p<font size=+1>(</font>&thinsp;x&thinsp;|&thinsp;a,b&thinsp;<font size=+1>)</font></i></td><td style="vertical-align: top;">&nbsp;<font size=+1>=</font>&nbsp;</td><td style="vertical-align: top;"><font size=+1><b><hr></b></font></td>
</tr>
<tr>
<td style="vertical-align: top;">&nbsp;</td><td style="vertical-align: top;">&nbsp;</td><td style="vertical-align: top; text-align: center;"><i>b<sup>&thinsp;a</sup> &Gamma;(a)</i></td>
@@ -8176,6 +8148,7 @@ fmat B2 = randg&lt;fmat&gt;(10, 10, distr_param(2,1));
<li><a href="#imbue">.imbue()</a></li>
<li><a href="#size">size()</a></li>
<li><a href="#rng_seed">RNG seed setting</a></li>
<li><a href="https://mathworld.wolfram.com/GammaDistribution.html">gamma distribution in MathWorld</a></li>
<li><a href="https://en.wikipedia.org/wiki/Gamma_distribution">gamma distribution in Wikipedia</a></li>
</ul>
</li>
@@ -11876,9 +11849,6 @@ See also:
<a name="sort_index"></a>
<b>sort_index( X )</b>
<br><b>sort_index( X, sort_direction )</b>
<br>
<br><b>stable_sort_index( X )</b>
<br><b>stable_sort_index( X, sort_direction )</b>
<ul>
<li>Return a vector which describes the sorted order of the elements of <i>X</i>
(ie. it contains the indices of the elements of <i>X</i>)
@@ -11894,8 +11864,6 @@ See also:
<br>
<li>The <i>sort_direction</i> argument is optional; <i>sort_direction</i> is either <code>"ascend"</code> or <code>"descend"</code>; by default <code>"ascend"</code> is used</li>
<br>
<li>The <i>stable_sort_index()</i> variant preserves the relative order of elements with equivalent values</li>
<br>
<li>For matrices and vectors with complex numbers, sorting is via absolute values</li>
<br>
<li>
@@ -18717,7 +18685,7 @@ rudimentary <i>fp16</i> hardware support is present on x86-64 CPUs with the F16C
<br>
</ul>
<li>
If no compiler or hardware support is detected, support for <i>fp16</i> element types is <b>not</b> automatically enableds
If no compiler or hardware support is detected, support for <i>fp16</i> element types is <b>not</b> automatically enabled
</li>
<br>
<li>
@@ -21448,7 +21416,7 @@ see the <a href="https://arma.sourceforge.net/faq.html#license">Questions page</
<li>added handling of <a href="#diag">diagonal views</a> by sparse matrices
<li>expanded <a href="#repmat">repmat()</a> to handle sparse matrices
<li>expanded <a href="#join">join_rows()</a> and <a href="#join">join_cols()</a> to handle sparse matrices
<li><a href="#sort_index">sort_index()</a> and <a href="#sort_index">stable_sort_index()</a> have been placed in the delayed operations framework for increased efficiency
<li><a href="#sort_index">sort_index()</a> has been placed in the delayed operations framework for increased efficiency
<li>use of <a href="#uword">64 bit integers</a> is automatically enabled when using a C++11 compiler</li>
</ul>
</li>
@@ -21660,7 +21628,7 @@ eg. <i>mat&nbsp;X(4,&nbsp;5,&nbsp;fill::zeros)</i></li>
<li>faster handling of compound expressions with submatrices and subcubes</li>
<li>faster <a href="#trace">trace()</a></li>
<li>added support for loading matrices as text files with <i>NaN</i> and <i>Inf</i> elements</li>
<li>added <a href="#sort_index">stable_sort_index()</a>, which preserves the relative order of elements with equivalent values</li>
<!-- <li>added <a href="#sort_index">stable_sort_index()</a>, which preserves the relative order of elements with equivalent values</li> -->
<li>added handling of <a href="#SpMat">sparse matrices</a> by <a href="#stats_fns">mean()</a>, <a href="#stats_fns">var()</a>, <a href="#norm">norm()</a>, <a href="#abs">abs()</a>, <a href="#misc_fns">square()</a>, <a href="#misc_fns">sqrt()</a></li>
<li>added saving and loading of sparse matrices in <i>arma_binary</i> format</li>
</ul>
+1 -1
View File
@@ -23,7 +23,7 @@
#define ARMA_VERSION_MAJOR 15
#define ARMA_VERSION_MINOR 0
#define ARMA_VERSION_PATCH 1
#define ARMA_VERSION_PATCH 3
#define ARMA_VERSION_NAME "Medium Roast"
+3
View File
@@ -63,6 +63,9 @@ struct diskio
template<typename eT> inline static std::streamsize prepare_stream(std::ostream& f);
template<typename eT> inline static constexpr eT real_as_int_lower_limit();
template<typename eT> inline static constexpr eT real_as_int_upper_limit();
//
// matrix saving
+52 -27
View File
@@ -688,7 +688,32 @@ diskio::prepare_stream(std::ostream& f)
return cell_width;
}
template<typename eT>
inline
constexpr
eT
diskio::real_as_int_lower_limit()
{
constexpr eT eT_int_accuracy_lower_limit = -( (is_fp16<eT>::value) ? eT(0x800) : ( (is_float<eT>::value) ? eT(0x1000000) : eT(0x20000000000000) ) );
return (std::max)( eT(std::numeric_limits<int>::lowest()), eT_int_accuracy_lower_limit );
}
template<typename eT>
inline
constexpr
eT
diskio::real_as_int_upper_limit()
{
constexpr eT eT_int_accuracy_upper_limit = (is_fp16<eT>::value) ? eT(0x800) : ( (is_float<eT>::value) ? eT(0x1000000) : eT(0x20000000000000) );
return (std::min)( eT(std::numeric_limits<int>::max()), eT_int_accuracy_upper_limit );
}
@@ -935,8 +960,8 @@ diskio::save_csv_ascii(const Mat<eT>& x, std::ostream& f, const char separator)
uword x_n_rows = x.n_rows;
uword x_n_cols = x.n_cols;
const eT eT_int_lowest = eT(std::numeric_limits<int>::lowest());
const eT eT_int_max = eT(std::numeric_limits<int>::max());
constexpr eT eT_int_lower = diskio::real_as_int_lower_limit<eT>();
constexpr eT eT_int_upper = diskio::real_as_int_upper_limit<eT>();
for(uword row=0; row < x_n_rows; ++row)
{
@@ -944,7 +969,7 @@ diskio::save_csv_ascii(const Mat<eT>& x, std::ostream& f, const char separator)
{
const eT val = x.at(row,col);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lowest) && (val < eT_int_max) && (eT(int(val)) == val);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lower) && (val < eT_int_upper) && (eT(int(val)) == val);
(is_real_int) ? arma_ostream::raw_print_elem(f, int(val)) : arma_ostream::raw_print_elem(f, val);
@@ -977,8 +1002,8 @@ diskio::save_csv_ascii(const Mat< std::complex<T> >& x, std::ostream& f, const c
diskio::prepare_stream<eT>(f);
const T T_int_lowest = T(std::numeric_limits<int>::lowest());
const T T_int_max = T(std::numeric_limits<int>::max());
constexpr T T_int_lower = diskio::real_as_int_lower_limit<T>();
constexpr T T_int_upper = diskio::real_as_int_upper_limit<T>();
uword x_n_rows = x.n_rows;
uword x_n_cols = x.n_cols;
@@ -994,8 +1019,8 @@ diskio::save_csv_ascii(const Mat< std::complex<T> >& x, std::ostream& f, const c
const T abs_i = (val_i < T(0)) ? T(-val_i) : T(val_i);
const char sgn_i = (val_i < T(0)) ? char('-') : char('+');
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lowest) && (val_r < T_int_max) && (T(int(val_r)) == val_r);
const bool abs_i_is_real_int = (is_real<T>::yes) && arma_isfinite(abs_i) && (abs_i < T_int_max) && (T(int(abs_i)) == abs_i);
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lower) && (val_r < T_int_upper) && (T(int(val_r)) == val_r);
const bool abs_i_is_real_int = (is_real<T>::yes) && arma_isfinite(abs_i) && (abs_i < T_int_upper) && (T(int(abs_i)) == abs_i);
(val_r_is_real_int) ? arma_ostream::raw_print_elem(f, int(val_r)) : arma_ostream::raw_print_elem(f, val_r);
@@ -1061,9 +1086,9 @@ diskio::save_coord_ascii(const Mat<eT>& x, std::ostream& f)
diskio::prepare_stream<eT>(f);
const eT eT_zero = eT(0);
const eT eT_int_lowest = eT(std::numeric_limits<int>::lowest());
const eT eT_int_max = eT(std::numeric_limits<int>::max());
constexpr eT eT_zero = eT(0);
constexpr eT eT_int_lower = diskio::real_as_int_lower_limit<eT>();
constexpr eT eT_int_upper = diskio::real_as_int_upper_limit<eT>();
for(uword col=0; col < x.n_cols; ++col)
for(uword row=0; row < x.n_rows; ++row)
@@ -1075,7 +1100,7 @@ diskio::save_coord_ascii(const Mat<eT>& x, std::ostream& f)
f << row; f.put(' ');
f << col; f.put(' ');
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lowest) && (val < eT_int_max) && (eT(int(val)) == val);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lower) && (val < eT_int_upper) && (eT(int(val)) == val);
(is_real_int) ? arma_ostream::raw_print_elem(f, int(val)) : arma_ostream::raw_print_elem(f, val);
@@ -1116,9 +1141,9 @@ diskio::save_coord_ascii(const Mat< std::complex<T> >& x, std::ostream& f)
diskio::prepare_stream<eT>(f);
const eT eT_zero = eT(0);
const T T_int_lowest = T(std::numeric_limits<int>::lowest());
const T T_int_max = T(std::numeric_limits<int>::max());
constexpr eT eT_zero = eT(0);
constexpr T T_int_lower = diskio::real_as_int_lower_limit<T>();
constexpr T T_int_upper = diskio::real_as_int_upper_limit<T>();
for(uword col=0; col < x.n_cols; ++col)
for(uword row=0; row < x.n_rows; ++row)
@@ -1133,8 +1158,8 @@ diskio::save_coord_ascii(const Mat< std::complex<T> >& x, std::ostream& f)
const T val_r = std::real(val);
const T val_i = std::imag(val);
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lowest) && (val_r < T_int_max) && (T(int(val_r)) == val_r);
const bool val_i_is_real_int = (is_real<T>::yes) && arma_isfinite(val_i) && (val_i > T_int_lowest) && (val_i < T_int_max) && (T(int(val_i)) == val_i);
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lower) && (val_r < T_int_upper) && (T(int(val_r)) == val_r);
const bool val_i_is_real_int = (is_real<T>::yes) && arma_isfinite(val_i) && (val_i > T_int_lower) && (val_i < T_int_upper) && (T(int(val_i)) == val_i);
(val_r_is_real_int) ? arma_ostream::raw_print_elem(f, int(val_r)) : arma_ostream::raw_print_elem(f, val_r);
@@ -2966,9 +2991,9 @@ diskio::save_csv_ascii(const SpMat<eT>& x, std::ostream& f, const char separator
uword x_n_rows = x.n_rows;
uword x_n_cols = x.n_cols;
const eT eT_zero = eT(0);
const eT eT_int_lowest = eT(std::numeric_limits<int>::lowest());
const eT eT_int_max = eT(std::numeric_limits<int>::max());
constexpr eT eT_zero = eT(0);
constexpr eT eT_int_lower = diskio::real_as_int_lower_limit<eT>();
constexpr eT eT_int_upper = diskio::real_as_int_upper_limit<eT>();
for(uword row=0; row < x_n_rows; ++row)
{
@@ -2982,7 +3007,7 @@ diskio::save_csv_ascii(const SpMat<eT>& x, std::ostream& f, const char separator
}
else
{
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lowest) && (val < eT_int_max) && (eT(int(val)) == val);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lower) && (val < eT_int_upper) && (eT(int(val)) == val);
(is_real_int) ? arma_ostream::raw_print_elem(f, int(val)) : arma_ostream::raw_print_elem(f, val);
}
@@ -3064,8 +3089,8 @@ diskio::save_coord_ascii(const SpMat<eT>& x, std::ostream& f)
diskio::prepare_stream<eT>(f);
const eT eT_int_lowest = eT(std::numeric_limits<int>::lowest());
const eT eT_int_max = eT(std::numeric_limits<int>::max());
constexpr eT eT_int_lower = diskio::real_as_int_lower_limit<eT>();
constexpr eT eT_int_upper = diskio::real_as_int_upper_limit<eT>();
typename SpMat<eT>::const_iterator iter = x.begin();
typename SpMat<eT>::const_iterator iter_end = x.end();
@@ -3077,7 +3102,7 @@ diskio::save_coord_ascii(const SpMat<eT>& x, std::ostream& f)
const eT val = (*iter);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lowest) && (val < eT_int_max) && (eT(int(val)) == val);
const bool is_real_int = (is_real<eT>::yes) && arma_isfinite(val) && (val > eT_int_lower) && (val < eT_int_upper) && (eT(int(val)) == val);
(is_real_int) ? arma_ostream::raw_print_elem(f, int(val)) : arma_ostream::raw_print_elem(f, val);
@@ -3120,8 +3145,8 @@ diskio::save_coord_ascii(const SpMat< std::complex<T> >& x, std::ostream& f)
diskio::prepare_stream<eT>(f);
const T T_int_lowest = T(std::numeric_limits<int>::lowest());
const T T_int_max = T(std::numeric_limits<int>::max());
constexpr T T_int_lower = diskio::real_as_int_lower_limit<T>();
constexpr T T_int_upper = diskio::real_as_int_upper_limit<T>();
typename SpMat<eT>::const_iterator iter = x.begin();
typename SpMat<eT>::const_iterator iter_end = x.end();
@@ -3136,8 +3161,8 @@ diskio::save_coord_ascii(const SpMat< std::complex<T> >& x, std::ostream& f)
const T val_r = std::real(val);
const T val_i = std::imag(val);
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lowest) && (val_r < T_int_max) && (T(int(val_r)) == val_r);
const bool val_i_is_real_int = (is_real<T>::yes) && arma_isfinite(val_i) && (val_i > T_int_lowest) && (val_i < T_int_max) && (T(int(val_i)) == val_i);
const bool val_r_is_real_int = (is_real<T>::yes) && arma_isfinite(val_r) && (val_r > T_int_lower) && (val_r < T_int_upper) && (T(int(val_r)) == val_r);
const bool val_i_is_real_int = (is_real<T>::yes) && arma_isfinite(val_i) && (val_i > T_int_lower) && (val_i < T_int_upper) && (T(int(val_i)) == val_i);
(val_r_is_real_int) ? arma_ostream::raw_print_elem(f, int(val_r)) : arma_ostream::raw_print_elem(f, val_r);
+3 -3
View File
@@ -17,7 +17,7 @@
//! \addtogroup fn_shift
//! \addtogroup fn_circshift
//! @{
@@ -25,7 +25,7 @@
// TODO: deprecate in favour of circshift()
template<typename T1>
arma_warn_unused
arma_inline
inline
typename
enable_if2
<
@@ -50,7 +50,7 @@ shift
template<typename T1>
arma_warn_unused
arma_inline
inline
typename
enable_if2
<
+1 -1
View File
@@ -109,7 +109,7 @@ glue_hist::apply_noalias(Mat<uword>& out, const Mat<eT>& X, const Mat<eT>& C, co
{
const eT val = X_mem[i];
if(is_finite(val))
if(arma_isfinite(val))
{
eT opt_dist = (val >= center_0) ? (val - center_0) : (center_0 - val);
uword opt_index = 0;
+166 -1
View File
@@ -59,6 +59,156 @@ struct gemm_emul_tinysq
struct gemm_emul_large_mp_helper
{
template<typename eT>
arma_hot
inline
static
void
copy_row(eT* out_mem, const Mat<eT>& in, const uword row)
{
const uword n_rows = in.n_rows;
const uword n_cols = in.n_cols;
const eT* in_mem_row = in.memptr() + row;
for(uword i=0; i < n_cols; ++i)
{
out_mem[i] = (*in_mem_row);
in_mem_row += n_rows;
}
}
};
#if defined(ARMA_USE_OPENMP)
//! emulation of gemm(), for non-complex matrices only, as it assumes only simple transposes (ie. doesn't do hermitian transposes)
//! parallelised version
template<const bool do_trans_A=false, const bool do_trans_B=false, const bool use_alpha=false, const bool use_beta=false>
struct gemm_emul_large_mp
{
template<typename eT, typename TA, typename TB>
arma_hot
inline
static
void
apply
(
Mat<eT>& C,
const TA& A,
const TB& B,
const eT alpha = eT(1),
const eT beta = eT(0)
)
{
arma_debug_sigprint();
const uword A_n_rows = A.n_rows;
const uword A_n_cols = A.n_cols;
const uword B_n_rows = B.n_rows;
const uword B_n_cols = B.n_cols;
if( (do_trans_A == false) && (do_trans_B == false) )
{
const uword n_threads = uword(mp_thread_limit::get());
podarray<eT> tmp(A_n_cols * n_threads, arma_nozeros_indicator());
eT* tmp_mem = tmp.memptr();
#pragma omp parallel for schedule(static) num_threads(int(n_threads))
for(uword row_A=0; row_A < A_n_rows; ++row_A)
{
const uword thread_id = uword(omp_get_thread_num());
eT* A_rowdata = tmp_mem + (A_n_cols * thread_id);
gemm_emul_large_mp_helper::copy_row(A_rowdata, A, row_A);
for(uword col_B=0; col_B < B_n_cols; ++col_B)
{
const eT acc = op_dot::direct_dot(B_n_rows, A_rowdata, B.colptr(col_B));
if( (use_alpha == false) && (use_beta == false) ) { C.at(row_A,col_B) = acc; }
else if( (use_alpha == true ) && (use_beta == false) ) { C.at(row_A,col_B) = alpha*acc; }
else if( (use_alpha == false) && (use_beta == true ) ) { C.at(row_A,col_B) = acc + beta*C.at(row_A,col_B); }
else if( (use_alpha == true ) && (use_beta == true ) ) { C.at(row_A,col_B) = alpha*acc + beta*C.at(row_A,col_B); }
}
}
}
else
if( (do_trans_A == true) && (do_trans_B == false) )
{
const int n_threads = mp_thread_limit::get();
#pragma omp parallel for schedule(static) num_threads(n_threads)
for(uword col_A=0; col_A < A_n_cols; ++col_A)
{
// col_A is interpreted as row_A when storing the results in matrix C
const eT* A_coldata = A.colptr(col_A);
for(uword col_B=0; col_B < B_n_cols; ++col_B)
{
const eT acc = op_dot::direct_dot(B_n_rows, A_coldata, B.colptr(col_B));
if( (use_alpha == false) && (use_beta == false) ) { C.at(col_A,col_B) = acc; }
else if( (use_alpha == true ) && (use_beta == false) ) { C.at(col_A,col_B) = alpha*acc; }
else if( (use_alpha == false) && (use_beta == true ) ) { C.at(col_A,col_B) = acc + beta*C.at(col_A,col_B); }
else if( (use_alpha == true ) && (use_beta == true ) ) { C.at(col_A,col_B) = alpha*acc + beta*C.at(col_A,col_B); }
}
}
}
else
if( (do_trans_A == false) && (do_trans_B == true) )
{
Mat<eT> BB;
op_strans::apply_mat_noalias(BB, B);
gemm_emul_large_mp<false, false, use_alpha, use_beta>::apply(C, A, BB, alpha, beta);
}
else
if( (do_trans_A == true) && (do_trans_B == true) )
{
// using trans(A)*trans(B) = trans(B*A) equivalency; assuming no hermitian transpose
const uword n_threads = uword(mp_thread_limit::get());
podarray<eT> tmp(B_n_cols * n_threads, arma_nozeros_indicator());
eT* tmp_mem = tmp.memptr();
#pragma omp parallel for schedule(static) num_threads(int(n_threads))
for(uword row_B=0; row_B < B_n_rows; ++row_B)
{
const uword thread_id = uword(omp_get_thread_num());
eT* B_rowdata = tmp_mem + (B_n_cols * thread_id);
gemm_emul_large_mp_helper::copy_row(B_rowdata, B, row_B);
for(uword col_A=0; col_A < A_n_cols; ++col_A)
{
const eT acc = op_dot::direct_dot(A_n_rows, B_rowdata, A.colptr(col_A));
if( (use_alpha == false) && (use_beta == false) ) { C.at(col_A,row_B) = acc; }
else if( (use_alpha == true ) && (use_beta == false) ) { C.at(col_A,row_B) = alpha*acc; }
else if( (use_alpha == false) && (use_beta == true ) ) { C.at(col_A,row_B) = acc + beta*C.at(col_A,row_B); }
else if( (use_alpha == true ) && (use_beta == true ) ) { C.at(col_A,row_B) = alpha*acc + beta*C.at(col_A,row_B); }
}
}
}
}
};
#endif
//! emulation of gemm(), for non-complex matrices only, as it assumes only simple transposes (ie. doesn't do hermitian transposes)
template<const bool do_trans_A=false, const bool do_trans_B=false, const bool use_alpha=false, const bool use_beta=false>
struct gemm_emul_large
@@ -78,13 +228,28 @@ struct gemm_emul_large
)
{
arma_debug_sigprint();
const uword A_n_rows = A.n_rows;
const uword A_n_cols = A.n_cols;
const uword B_n_rows = B.n_rows;
const uword B_n_cols = B.n_cols;
#if defined(ARMA_USE_OPENMP)
{
// TODO: replace with more sophisticated threshold mechanism
constexpr uword threshold = uword(30);
if( (A_n_rows >= threshold) && (A_n_cols >= threshold) && (B_n_rows >= threshold) && (B_n_cols >= threshold) && (mp_thread_limit::in_parallel() == false) )
{
gemm_emul_large_mp<do_trans_A, do_trans_B, use_alpha, use_beta>::apply(C,A,B,alpha,beta);
return;
}
}
#endif
if( (do_trans_A == false) && (do_trans_B == false) )
{
arma_aligned podarray<eT> tmp(A_n_cols);
+83
View File
@@ -203,6 +203,74 @@ struct gemv_emul_helper
#if defined(ARMA_USE_OPENMP)
//! Partial emulation of BLAS gemv().
//! 'y' is assumed to have been set to the correct size (ie. taking into account the transpose)
//! parallelised version
template<const bool do_trans_A=false, const bool use_alpha=false, const bool use_beta=false>
struct gemv_emul_mp
{
template<typename eT, typename TA>
arma_hot
inline
static
void
apply( eT* y, const TA& A, const eT* x, const eT alpha = eT(1), const eT beta = eT(0) )
{
arma_debug_sigprint();
const int n_threads = mp_thread_limit::get();
const uword A_n_rows = A.n_rows;
const uword A_n_cols = A.n_cols;
if(do_trans_A == false)
{
#pragma omp parallel for schedule(static) num_threads(n_threads)
for(uword row=0; row < A_n_rows; ++row)
{
const eT acc = gemv_emul_helper::dot_row_col(A, x, row, A_n_cols);
if( (use_alpha == false) && (use_beta == false) ) { y[row] = acc; }
else if( (use_alpha == true ) && (use_beta == false) ) { y[row] = alpha*acc; }
else if( (use_alpha == false) && (use_beta == true ) ) { y[row] = acc + beta*y[row]; }
else if( (use_alpha == true ) && (use_beta == true ) ) { y[row] = alpha*acc + beta*y[row]; }
}
}
else
if(do_trans_A == true)
{
if(is_cx<eT>::no)
{
#pragma omp parallel for schedule(static) num_threads(n_threads)
for(uword col=0; col < A_n_cols; ++col)
{
// col is interpreted as row when storing the results in 'y'
const eT acc = op_dot::direct_dot(A_n_rows, A.colptr(col), x);
if( (use_alpha == false) && (use_beta == false) ) { y[col] = acc; }
else if( (use_alpha == true ) && (use_beta == false) ) { y[col] = alpha*acc; }
else if( (use_alpha == false) && (use_beta == true ) ) { y[col] = acc + beta*y[col]; }
else if( (use_alpha == true ) && (use_beta == true ) ) { y[col] = alpha*acc + beta*y[col]; }
}
}
else
{
Mat<eT> AA;
op_htrans::apply_mat_noalias(AA, A);
gemv_emul_mp<false, use_alpha, use_beta>::apply(y, AA, x, alpha, beta);
}
}
}
};
#endif
//! \brief
//! Partial emulation of BLAS gemv().
//! 'y' is assumed to have been set to the correct size (ie. taking into account the transpose)
@@ -222,6 +290,21 @@ struct gemv_emul
const uword A_n_rows = A.n_rows;
const uword A_n_cols = A.n_cols;
#if defined(ARMA_USE_OPENMP)
{
// TODO: replace with more sophisticated threshold mechanism
constexpr uword threshold = uword(200);
if( (A_n_rows >= threshold) && (A_n_cols >= threshold) && (mp_thread_limit::in_parallel() == false) )
{
gemv_emul_mp<do_trans_A, use_alpha, use_beta>::apply(y, A, x, alpha, beta);
return;
}
}
#endif
if(do_trans_A == false)
{
if(A_n_rows == 1)
+20 -4
View File
@@ -365,14 +365,30 @@ op_vectorise_cube_col::apply_subview(Mat<eT>& out, const subview_cube<eT>& sv)
out.set_size(sv.n_elem, 1);
if(sv.n_elem == 0) { return; }
eT* out_mem = out.memptr();
for(uword s=0; s < sv_ns; ++s)
for(uword c=0; c < sv_nc; ++c)
if( (sv_nr == 1) && (sv_nc == 1) && (sv.aux_slice1 == 0) )
{
arrayops::copy(out_mem, sv.slice_colptr(s,c), sv_nr);
const uword sv_m_n_elem_slice = sv.m.n_elem_slice;
out_mem += sv_nr;
const eT* sv_m_ptr = &( sv.m.at(sv.aux_row1, sv.aux_col1, 0) );
for(uword s=0; s < sv_ns; ++s)
{
out_mem[s] = (*sv_m_ptr); sv_m_ptr += sv_m_n_elem_slice;
}
}
else
{
for(uword s=0; s < sv_ns; ++s)
for(uword c=0; c < sv_nc; ++c)
{
arrayops::copy(out_mem, sv.slice_colptr(s,c), sv_nr);
out_mem += sv_nr;
}
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ operator<< (std::ostream& o, const Base<eT,T1>& X)
{
arma_debug_sigprint();
const unwrap<T1> tmp(X.get_ref());
const quasi_unwrap<T1> tmp(X.get_ref());
arma_ostream::print(o, tmp.M, true);
+131
View File
@@ -969,3 +969,134 @@ TEMPLATE_TEST_CASE("mat_mul_fp_compare", "[mat_mul]", TEST_FLOAT_TYPES)
REQUIRE( accu(abs(diff3)) == Approx(eT(0)).margin(margin * diff3.n_elem) );
REQUIRE( accu(abs(diff4)) == Approx(eT(0)).margin(margin * diff4.n_elem) );
}
#if defined(ARMA_USE_BLAS)
TEMPLATE_TEST_CASE("mat_mul_int_compare", "[mat_mul]", u32, s32, u64, s64)
{
typedef TestType eT;
const uword n_trials = 5;
for (uword trial = 0; trial < n_trials; ++trial)
{
uword m = randi<uword>(distr_param(10, 500));
uword n = randi<uword>(distr_param(10, 500));
uword k = randi<uword>(distr_param(10, 500));
Mat<eT> A = randi<Mat<eT>>(m, n, distr_param(0, 100));
Mat<eT> At = randi<Mat<eT>>(n, m, distr_param(0, 100));
Mat<eT> B = randi<Mat<eT>>(n, k, distr_param(0, 100));
Mat<eT> Bt = randi<Mat<eT>>(k, n, distr_param(0, 100));
// compare against BLAS implementation
mat Ad = conv_to<mat>::from(A);
mat Atd = conv_to<mat>::from(At);
mat Bd = conv_to<mat>::from(B);
mat Btd = conv_to<mat>::from(Bt);
Mat<eT> C = A * B;
mat Cd = Ad * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = A * Bt.t();
Cd = Ad * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = At.t() * B;
Cd = Atd.t() * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = At.t() * Bt.t();
Cd = Atd.t() * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
// now test variants with alpha
C = 2 * A * B;
Cd = 2 * Ad * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = 2 * A * Bt.t();
Cd = 2 * Ad * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = 2 * At.t() * B;
Cd = 2 * Atd.t() * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C = 2 * At.t() * Bt.t();
Cd = 2 * Atd.t() * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
// variants with beta
C.ones(A.n_rows, B.n_cols);
C = 2 * C + A * B;
Cd.ones(Ad.n_rows, Bd.n_cols);
Cd = 2 * Cd + Ad * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(A.n_rows, Bt.n_rows);
C = 2 * C + A * Bt.t();
Cd.ones(Ad.n_rows, Btd.n_rows);
Cd = 2 * Cd + Ad * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(At.n_cols, B.n_cols);
C = 2 * C + At.t() * B;
Cd.ones(Atd.n_cols, Bd.n_cols);
Cd = 2 * Cd + Atd.t() * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(At.n_cols, Bt.n_rows);
C = 2 * C + At.t() * Bt.t();
Cd.ones(Atd.n_cols, Btd.n_rows);
Cd = 2 * Cd + Atd.t() * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
// variants with both alpha and beta
C.ones(A.n_rows, B.n_cols);
C = 2 * C + 3 * A * B;
Cd.ones(Ad.n_rows, Bd.n_cols);
Cd = 2 * Cd + 3 * Ad * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(A.n_rows, Bt.n_rows);
C = 2 * C + 3 * A * Bt.t();
Cd.ones(Ad.n_rows, Btd.n_rows);
Cd = 2 * Cd + 3 * Ad * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(At.n_cols, B.n_cols);
C = 2 * C + 3 * At.t() * B;
Cd.ones(Atd.n_cols, Bd.n_cols);
Cd = 2 * Cd + 3 * Atd.t() * Bd;
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
C.ones(At.n_cols, Bt.n_rows);
C = 2 * C + 3 * At.t() * Bt.t();
Cd.ones(Atd.n_cols, Btd.n_rows);
Cd = 2 * Cd + 3 * Atd.t() * Btd.t();
REQUIRE( approx_equal( conv_to<mat>::from(C), Cd, "both", 1e-5, 1e-5 ) );
}
}
#endif