From 1daed03049a6f0c9079628dc66c84e74ba5b352f Mon Sep 17 00:00:00 2001 From: Luis Benet Date: Sat, 18 Feb 2023 11:24:09 -0600 Subject: [PATCH] Overflow error when hashtables are inconsistent (#314) * Throw OverflowError when index_tables is negative * Fix is error message and addtests * Mention the error and in_base_safe to docs * Minor fix in tests * Bump patch version --- Project.toml | 2 +- docs/src/api.md | 1 + docs/src/userguide.md | 36 ++++++++++++++++++++---------------- src/hash_tables.jl | 22 +++++++++++++++++++++- test/intervals.jl | 2 +- test/manyvariables.jl | 29 ++++++++++++++++++++++------- 6 files changed, 66 insertions(+), 26 deletions(-) diff --git a/Project.toml b/Project.toml index 268eef8b..ef29d24e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TaylorSeries" uuid = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea" repo = "https://github.com/JuliaDiff/TaylorSeries.jl.git" -version = "0.13.0" +version = "0.13.1" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/docs/src/api.md b/docs/src/api.md index c71cc28b..3154a6d6 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -65,6 +65,7 @@ _InternalMutFuncs generate_tables generate_index_vectors in_base +in_base_safe make_inverse_dict resize_coeffs1! resize_coeffsHP! diff --git a/docs/src/userguide.md b/docs/src/userguide.md index 28e6b689..1cdf7397 100644 --- a/docs/src/userguide.md +++ b/docs/src/userguide.md @@ -49,12 +49,12 @@ t = shift_taylor(0.0) # Independent variable `t` ``` !!! warning - The information about the maximum order considered is displayed using a big-𝒪 notation. + The information about the maximum order considered is displayed using a big-𝒪 notation. The convention followed when different orders are combined, and when certain functions - are used in a way that they reduce the order (like [`differentiate`](@ref)), is to be consistent - with the mathematics and the big-𝒪 notation, i.e., to propagate the lowest order. - -In some cases, it is desirable to not display the big-𝒪 notation. The function [`displayBigO`](@ref) + are used in a way that they reduce the order (like [`differentiate`](@ref)), is to be consistent + with the mathematics and the big-𝒪 notation, i.e., to propagate the lowest order. + +In some cases, it is desirable to not display the big-𝒪 notation. The function [`displayBigO`](@ref) allows to control whether it is displayed or not. ```@repl userguide displayBigO(false) # turn-off displaying big O notation @@ -78,14 +78,14 @@ The definition of `shift_taylor(a)` uses the method [`Taylor1([::Type{Float64}], order::Int)`](@ref), which is a shortcut to define the independent variable of a Taylor expansion, of given type and order (the default is `Float64`). -Defining the independent variable in advance is one of the easiest +Defining the independent variable in advance is one of the easiest ways to use the package. The usual arithmetic operators (`+`, `-`, `*`, `/`, `^`, `==`) have been extended to work with the [`Taylor1`](@ref) type, including promotions that involve `Number`s. The operations return a valid Taylor expansion, consistent -with the order of the series. This is apparent in the penultimate example -below, where the fist non-zero coefficient is beyond the order of the expansion, +with the order of the series. This is apparent in the penultimate example +below, where the fist non-zero coefficient is beyond the order of the expansion, and hence the result is zero. ```@repl userguide @@ -100,9 +100,9 @@ t^6 # t is of order 5 t^2 / t # The result is of order 4, instead of 5 ``` -Note that the last example returns a `Taylor1` series of order 4, instead -of order 5; this is be consistent with the number of known coefficients of the -returned series, since the result corresponds to factorize `t` in the numerator +Note that the last example returns a `Taylor1` series of order 4, instead +of order 5; this is be consistent with the number of known coefficients of the +returned series, since the result corresponds to factorize `t` in the numerator to cancel the same factor in the denominator. If no valid Taylor expansion can be computed an error is thrown, for instance, @@ -121,8 +121,8 @@ are `exp`, `log`, `sqrt`, the trigonometric functions `sinh`, `cosh` and `tanh` and their inverses; more functions will be added in the future. Note that this way of obtaining the Taylor coefficients is not a *lazy* way, in particular for many independent -variables. Yet, the implementation is efficient enough, especially for the -integration of ordinary differential equations, which is among the +variables. Yet, the implementation is efficient enough, especially for the +integration of ordinary differential equations, which is among the applications we have in mind (see [TaylorIntegration.jl](https://github.com/PerezHz/TaylorIntegration.jl)). @@ -152,7 +152,7 @@ getcoeff(expon, 0) == expon[0] rationalize(expon[3]) ``` -Note that certain arithmetic operations, or the application of some functions, +Note that certain arithmetic operations, or the application of some functions, may change the order of the result, as mentioned above. ```@repl userguide t # order 5 independent variable @@ -164,7 +164,7 @@ sqrt(t^2) # returns an order 2 series expansion Differentiating and integrating is straightforward for polynomial expansions in one variable, using [`differentiate`](@ref) and [`integrate`](@ref). (The function [`derivative`](@ref) is a synonym of `differentiate`.) These -functions return the corresponding [`Taylor1`](@ref) expansions. +functions return the corresponding [`Taylor1`](@ref) expansions. The order of the derivative of a `Taylor1` is reduced by 1. For the integral, an integration constant may be set by the user (the default is zero); the order of the integrated polynomial @@ -199,7 +199,7 @@ eBig = evaluate( exp(tBig), one(BigFloat) ) ℯ - eBig ``` -Another way to evaluate the value of a `Taylor1` polynomial `p` at a given value `x`, +Another way to evaluate the value of a `Taylor1` polynomial `p` at a given value `x`, is to call `p` as if it was a function, i.e., `p(x)`: ```@repl userguide @@ -281,6 +281,10 @@ by `set_variables` initially. get_variables(2) # vector of independent variables of order 2 ``` +!!! warning + An `OverflowError` is thrown when the construction of the internal tables is not + fully consistent, avoiding silent errors; see [issue #85](https://github.com/JuliaDiff/TaylorSeries.jl/issues/85). + The function [`show_params_TaylorN`](@ref) displays the current values of the parameters, in an info block. diff --git a/src/hash_tables.jl b/src/hash_tables.jl index 13a3fd26..0b191461 100644 --- a/src/hash_tables.jl +++ b/src/hash_tables.jl @@ -33,7 +33,7 @@ of the corresponding monomial in `coeffs_table`. function generate_tables(num_vars, order) coeff_table = [generate_index_vectors(num_vars, i) for i in 0:order] - index_table = Vector{Int}[map(x->in_base(order, x), coeffs) for coeffs in coeff_table] + index_table = Vector{Int}[map(x->in_base_safe(order, x), coeffs) for coeffs in coeff_table] pos_table = map(make_inverse_dict, index_table) size_table = map(length, index_table) @@ -91,6 +91,8 @@ function in_base(order, v) result = 0 + all(iszero.(v)) && return result + for i in v result = result*order + i end @@ -98,6 +100,24 @@ function in_base(order, v) result end +""" + in_base_safe(order, v) + +Same as `in_base` assuring positivity of the result; +used for constructing `index_table`. +""" +function in_base_safe(order, v) + result = in_base(order, v) + + iszero(v) && return result + + (result <= 0) && throw(OverflowError(""" + Using numvars=$(length(v)) at order=$(order) produces + a non-positive index_table entry: $result.""")) + + return result +end + const coeff_table, index_table, size_table, pos_table = generate_tables(get_numvars(), get_order()) diff --git a/test/intervals.jl b/test/intervals.jl index 18d2e93c..81914ae9 100644 --- a/test/intervals.jl +++ b/test/intervals.jl @@ -91,7 +91,7 @@ eeuler = Base.MathConstants.e @test diam(g1(-1..1)) < diam(gt(ii)) # Test display for Taylor1{Complex{Interval{T}}} - vc = [complex(1.5 .. 2, 0 ), complex(-2 .. -1, -1 .. 1 ), + vc = [complex(1.5 .. 2, 0..0 ), complex(-2 .. -1, -1 .. 1 ), complex( -1 .. 1.5, -1 .. 1.5), complex( 0..0, -1 .. 1.5)] displayBigO(false) @test string(Taylor1(vc, 5)) == diff --git a/test/manyvariables.jl b/test/manyvariables.jl index bcbe9104..4b909108 100644 --- a/test/manyvariables.jl +++ b/test/manyvariables.jl @@ -6,13 +6,7 @@ using TaylorSeries using Test using LinearAlgebra -@testset "Tests for HomogeneousPolynomial and TaylorN" begin - eeuler = Base.MathConstants.e - - @test HomogeneousPolynomial <: AbstractSeries - @test HomogeneousPolynomial{Int} <: AbstractSeries{Int} - @test TaylorN{Float64} <: AbstractSeries{Float64} - +@testset "Test hash tables" begin set_variables("x", numvars=2, order=6) _taylorNparams = TaylorSeries.ParamsTaylorN(6, 2, String["x₁", "x₂"]) @test _taylorNparams.order == get_order() @@ -20,6 +14,18 @@ using LinearAlgebra @test _taylorNparams.variable_names == get_variable_names() @test _taylorNparams.variable_symbols == get_variable_symbols() + @test_throws OverflowError(""" + Using numvars=65 at order=1 produces + a non-positive index_table entry: 0.""") set_variables("δ", order=1, numvars=65) + @test_throws OverflowError(""" + Using numvars=41 at order=2 produces + a non-positive index_table entry: -6289078614652622815.""") set_variables("δ", order=2, numvars=41) + @test_throws OverflowError(""" + Using numvars=32 at order=3 produces + a non-positive index_table entry: -9223372036854775808.""") set_variables("δ", order=3, numvars=32) + set_variables("x", numvars=2, order=40) + @test all(allunique.(TS.index_table[:])) + @test eltype(set_variables(Int, "x", numvars=2, order=6)) == TaylorN{Int} @test eltype(set_variables("x", numvars=2, order=6)) == TaylorN{Float64} @test eltype(set_variables(BigInt, "x y", order=6)) == TaylorN{BigInt} @@ -35,7 +41,16 @@ using LinearAlgebra @test TaylorSeries.index_table[2][1] == 7 @test TaylorSeries.in_base(get_order(),[2,1]) == 15 @test TaylorSeries.pos_table[4][15] == 2 +end + +@testset "Tests for HomogeneousPolynomial and TaylorN" begin + eeuler = Base.MathConstants.e + + @test HomogeneousPolynomial <: AbstractSeries + @test HomogeneousPolynomial{Int} <: AbstractSeries{Int} + @test TaylorN{Float64} <: AbstractSeries{Float64} + set_variables([:x,:y], order=6) @test get_order() == 6 @test get_numvars() == 2