Mercurial > octave
changeset 27435:a8a5d2e8807f
Produce symmetric sequences for linspace (bug #56659).
* NEWS: Announce change.
* data.cc (Flinspace): Add new BIST tests.
* CRowVector.cc, dRowVector.cc, fCRowVector.cc, fRowVector.cc (linspace):
Change algorithm to construct sequence starting from both endpoints and
meeting in the middle to create symmetric sequences.
* logspace.m: Update BIST tests.
author | Rik <rik@octave.org> |
---|---|
date | Fri, 20 Sep 2019 09:03:14 -0700 |
parents | 57f5c5768eb3 |
children | ece17410605e |
files | NEWS libinterp/corefcn/data.cc liboctave/array/CRowVector.cc liboctave/array/dRowVector.cc liboctave/array/fCRowVector.cc liboctave/array/fRowVector.cc scripts/general/logspace.m |
diffstat | 7 files changed, 160 insertions(+), 47 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Sat Sep 21 17:17:02 2019 -0700 +++ b/NEWS Fri Sep 20 09:03:14 2019 -0700 @@ -13,6 +13,10 @@ accept a new sorting option `"stable"` which will return output values in the same order as the input, rather than in ascending order. +- The `linspace` function now produces symmetrical sequences when the + endpoints are symmetric. This is more intuitive and also compatible + with recent changes made in Matlab R2019b. + #### Graphics backend - Graphic primitives now accept a color property value of `"none"`
--- a/libinterp/corefcn/data.cc Sat Sep 21 17:17:02 2019 -0700 +++ b/libinterp/corefcn/data.cc Fri Sep 20 09:03:14 2019 -0700 @@ -5287,6 +5287,31 @@ %!assert (class (linspace (single (1), 2)), "single") %!assert (class (linspace (1, single (2))), "single") +## Test symmetry +%!test <*56659> +%! x = linspace (-1, 1, 10); +%! assert (all (x == -fliplr (x))); +%! x = linspace (-1, 1, 11); +%! assert (all (x == -fliplr (x))); + +%!test <*56659> +%! x = linspace (-1-1i, 1+1i, 10); +%! assert (all (x == -fliplr (x))); +%! x = linspace (-1-1i, 1+1i, 11); +%! assert (all (x == -fliplr (x))); + +%!test <*56659> +%! x = linspace (single (-1), 1, 10); +%! assert (all (x == -fliplr (x))); +%! x = linspace (single (-1), 1, 11); +%! assert (all (x == -fliplr (x))); + +%!test <*56659> +%! x = linspace (single (-1-1i), 1+1i, 10); +%! assert (all (x == -fliplr (x))); +%! x = linspace (single (-1-1i), 1+1i, 11); +%! assert (all (x == -fliplr (x))); + ## Test obscure Matlab compatibility options %!assert (linspace (0, 1, []), 1) %!assert (linspace (10, 20, 2), [10 20]) @@ -5300,14 +5325,21 @@ %!assert (1 ./ linspace (-0, 0, 4), [-Inf, Inf, Inf, Inf]) %!assert (linspace (Inf, Inf, 3), [Inf, Inf, Inf]) %!assert (linspace (-Inf, -Inf, 3), [-Inf, -Inf, -Inf]) -%!assert (linspace (-Inf, Inf, 3), [-Inf, NaN, Inf]) +%!assert (linspace (-Inf, Inf, 3), [-Inf, 0, Inf]) %!assert (linspace (Inf + 1i, Inf + 1i, 3), [Inf + 1i, Inf + 1i, Inf + 1i]) %!assert (linspace (-Inf + 1i, Inf + 1i, 3), [-Inf + 1i, NaN + 1i, Inf + 1i]) -%!assert (linspace (0, Inf, 3), [0, Inf, Inf]) -%!assert (linspace (0, -Inf, 3), [0, -Inf, -Inf]) -%!assert (linspace (-Inf, 0, 3), [-Inf, NaN, 0]) -%!assert (linspace (Inf, 0, 3), [Inf, NaN, 0]) -%!assert (linspace (Inf, -Inf, 3), [Inf, NaN, -Inf]) + +## FIXME: Octave is not fully Matlab-compatible for some combinations of +## Inf/-Inf endpoints. See bug #56933. This was dubbed "Won't Fix" +## so these tests have been removed from the test suite by commenting +## them out. If the behavior in the future is made compatible these +## tests can be re-instated. +##%!assert <56933> (linspace (-Inf, Inf, 4), [-Inf, -Inf, Inf, Inf]) +##%!assert <56933> (linspace (-Inf, Inf, 5), [-Inf, -Inf, 0, Inf, Inf]) +##%!assert <56933> (linspace (0, Inf, 4), [0, Inf, Inf, Inf]) +##%!assert <56933> (linspace (0, -Inf, 4), [0, -Inf, -Inf, -Inf]) +##%!assert <56933> (linspace (-Inf, 0, 4), [-Inf, NaN, NaN, 0]) +##%!assert <56933> (linspace (Inf, 0, 4), [Inf, NaN, NaN, 0]) %!error linspace () %!error linspace (1, 2, 3, 4)
--- a/liboctave/array/CRowVector.cc Sat Sep 21 17:17:02 2019 -0700 +++ b/liboctave/array/CRowVector.cc Fri Sep 20 09:03:14 2019 -0700 @@ -26,6 +26,7 @@ #include <istream> #include <ostream> +#include <type_traits> #include "Array-util.h" #include "lo-blas-proto.h" @@ -421,22 +422,39 @@ // other operations ComplexRowVector -linspace (const Complex& x1, const Complex& x2, octave_idx_type n) +linspace (const Complex& x1, const Complex& x2, octave_idx_type n_in) { NoAlias<ComplexRowVector> retval; - if (n < 1) + if (n_in < 1) return retval; - else - retval.clear (n); + else if (n_in == 1) + { + retval.resize (1, x2); + return retval; + } + // Use unsigned type (guaranteed n_in > 1 at this point) so that divisions + // by 2 can be replaced by compiler with shift right instructions. + typedef std::make_unsigned<octave_idx_type>::type unsigned_octave_idx_type; + + unsigned_octave_idx_type n = n_in; + + // Set endpoints, rather than calculate, for maximum accuracy. + retval.clear (n); retval(0) = x1; + retval(n-1) = x2; - Complex delta = (x1 == x2) ? 0 : (x2 - x1) / (n - 1.0); - for (octave_idx_type i = 1; i < n-1; i++) - retval(i) = x1 + static_cast<double> (i)*delta; - - retval(n-1) = x2; + // Construct linspace symmetrically from both ends. + Complex delta = (x2 - x1) / (n - 1.0); + unsigned_octave_idx_type n2 = n/2; + for (unsigned_octave_idx_type i = 1; i < n2; i++) + { + retval(i) = x1 + static_cast<double> (i)*delta; + retval(n-1-i) = x2 - static_cast<double> (i)*delta; + } + if (n % 2 == 1) // Middle element if number of elements is odd. + retval(n2) = (x1 == -x2 ? 0 : (x1 + x2) / 2.0); return retval; }
--- a/liboctave/array/dRowVector.cc Sat Sep 21 17:17:02 2019 -0700 +++ b/liboctave/array/dRowVector.cc Fri Sep 20 09:03:14 2019 -0700 @@ -26,6 +26,7 @@ #include <istream> #include <ostream> +#include <type_traits> #include "Array-util.h" #include "lo-blas-proto.h" @@ -264,22 +265,39 @@ // other operations RowVector -linspace (double x1, double x2, octave_idx_type n) +linspace (double x1, double x2, octave_idx_type n_in) { NoAlias<RowVector> retval; - if (n < 1) + if (n_in < 1) return retval; - else - retval.clear (n); + else if (n_in == 1) + { + retval.resize (1, x2); + return retval; + } + // Use unsigned type (guaranteed n_in > 1 at this point) so that divisions + // by 2 can be replaced by compiler with shift right instructions. + typedef std::make_unsigned<octave_idx_type>::type unsigned_octave_idx_type; + + unsigned_octave_idx_type n = n_in; + + // Set endpoints, rather than calculate, for maximum accuracy. + retval.clear (n); retval(0) = x1; + retval(n-1) = x2; - double delta = (x1 == x2) ? 0 : ((x2 - x1) / (n - 1)); - for (octave_idx_type i = 1; i < n-1; i++) - retval(i) = x1 + i*delta; - - retval(n-1) = x2; + // Construct linspace symmetrically from both ends. + double delta = (x2 - x1) / (n - 1); + unsigned_octave_idx_type n2 = n/2; + for (unsigned_octave_idx_type i = 1; i < n2; i++) + { + retval(i) = x1 + i*delta; + retval(n-1-i) = x2 - i*delta; + } + if (n % 2 == 1) // Middle element if number of elements is odd. + retval(n2) = (x1 == -x2 ? 0 : (x1 + x2) / 2); return retval; }
--- a/liboctave/array/fCRowVector.cc Sat Sep 21 17:17:02 2019 -0700 +++ b/liboctave/array/fCRowVector.cc Fri Sep 20 09:03:14 2019 -0700 @@ -26,6 +26,7 @@ #include <istream> #include <ostream> +#include <type_traits> #include "Array-util.h" #include "lo-blas-proto.h" @@ -423,22 +424,39 @@ // other operations FloatComplexRowVector -linspace (const FloatComplex& x1, const FloatComplex& x2, octave_idx_type n) +linspace (const FloatComplex& x1, const FloatComplex& x2, octave_idx_type n_in) { NoAlias<FloatComplexRowVector> retval; - if (n < 1) + if (n_in < 1) return retval; - else - retval.clear (n); + else if (n_in == 1) + { + retval.resize (1, x2); + return retval; + } + // Use unsigned type (guaranteed n_in > 1 at this point) so that divisions + // by 2 can be replaced by compiler with shift right instructions. + typedef std::make_unsigned<octave_idx_type>::type unsigned_octave_idx_type; + + unsigned_octave_idx_type n = n_in; + + // Set endpoints, rather than calculate, for maximum accuracy. + retval.clear (n); retval(0) = x1; + retval(n-1) = x2; - FloatComplex delta = (x1 == x2) ? 0 : (x2 - x1) / (n - 1.0f); - for (octave_idx_type i = 1; i < n-1; i++) - retval(i) = x1 + static_cast<float> (i)*delta; - - retval(n-1) = x2; + // Construct linspace symmetrically from both ends. + FloatComplex delta = (x2 - x1) / (n - 1.0f); + unsigned_octave_idx_type n2 = n/2; + for (unsigned_octave_idx_type i = 1; i < n2; i++) + { + retval(i) = x1 + static_cast<float> (i)*delta; + retval(n-1-i) = x2 - static_cast<float> (i)*delta; + } + if (n % 2 == 1) // Middle element if number of elements is odd. + retval(n2) = (x1 == -x2 ? 0 : (x1 + x2) / 2.0f); return retval; }
--- a/liboctave/array/fRowVector.cc Sat Sep 21 17:17:02 2019 -0700 +++ b/liboctave/array/fRowVector.cc Fri Sep 20 09:03:14 2019 -0700 @@ -26,6 +26,7 @@ #include <istream> #include <ostream> +#include <type_traits> #include "Array-util.h" #include "lo-blas-proto.h" @@ -264,22 +265,39 @@ // other operations FloatRowVector -linspace (float x1, float x2, octave_idx_type n) +linspace (float x1, float x2, octave_idx_type n_in) { NoAlias<FloatRowVector> retval; - if (n < 1) + if (n_in < 1) return retval; - else - retval.clear (n); + else if (n_in == 1) + { + retval.resize (1, x2); + return retval; + } + // Use unsigned type (guaranteed n_in > 1 at this point) so that divisions + // by 2 can be replaced by compiler with shift right instructions. + typedef std::make_unsigned<octave_idx_type>::type unsigned_octave_idx_type; + + unsigned_octave_idx_type n = n_in; + + // Set endpoints, rather than calculate, for maximum accuracy. + retval.clear (n); retval(0) = x1; + retval(n-1) = x2; - float delta = (x1 == x2) ? 0 : (x2 - x1) / (n - 1); - for (octave_idx_type i = 1; i < n-1; i++) - retval(i) = x1 + i*delta; - - retval(n-1) = x2; + // Construct linspace symmetrically from both ends. + float delta = (x2 - x1) / (n - 1); + unsigned_octave_idx_type n2 = n/2; + for (unsigned_octave_idx_type i = 1; i < n2; i++) + { + retval(i) = x1 + i*delta; + retval(n-1-i) = x2 - i*delta; + } + if (n % 2 == 1) // Middle element if number of elements is odd. + retval(n2) = (x1 == -x2 ? 0 : (x1 + x2) / 2); return retval; }
--- a/scripts/general/logspace.m Sat Sep 21 17:17:02 2019 -0700 +++ b/scripts/general/logspace.m Fri Sep 20 09:03:14 2019 -0700 @@ -97,16 +97,21 @@ %! assert (size (x2) == [1, 10] && abs (x2(1) - 10) < eps && abs (x2(10) - 100) < eps); %! assert (size (x3) == [1, 10] && abs (x3(1) - 10) < eps && abs (x3(10) - 0.01) < eps); %! assert (size (x4) == [1, 10] && abs (x4(1) - 10) < eps && abs (x4(10) - pi) < sqrt (eps)); + +## Edge cases %!assert (logspace (Inf, Inf, 3), [Inf, Inf, Inf]) -%!assert (logspace (-Inf, -Inf, 3), [0, 0, 0]) -%!assert (logspace (-Inf, Inf, 3), [0, NaN, Inf]) +%!assert (logspace (-Inf, Inf, 3), [0, 1, Inf]) %!assert (logspace (Inf + 1i, Inf + 1i, 3), repmat (complex (-Inf,Inf), [1, 3])) %!assert (logspace (-Inf + 1i, Inf + 1i, 3), [0, NaN + NaN * 1i, complex(-Inf, Inf)]) %!assert (logspace (0, Inf, 3), [1, Inf, Inf]) %!assert (logspace (0, -Inf, 3), [1, 0, 0]) -%!assert (logspace (-Inf, 0, 3), [0, NaN, 1]) -%!assert (logspace (Inf, 0, 3), [Inf, NaN, 1]) -%!assert (logspace (Inf, -Inf, 3), [Inf, NaN, 0]) +%!assert (logspace (Inf, -Inf, 3), [Inf, 1, 0]) + +## FIXME: These are bizarre corner cases for Matlab compatibility. See +## bug #56933. This is marked as "Won't Fix", but if linspace is updated at +## some point then these tests can be re-instated. +##%!assert (logspace (-Inf, 0, 3), [0, NaN, 1]) +##%!assert (logspace (Inf, 0, 3), [Inf, NaN, 1]) ## Test input validation %!error logspace ()