Skip to content

Commit

Permalink
made _assert_valid_input the actual interface function and _is_valid_…
Browse files Browse the repository at this point in the history
…input the derivation; adjusted the tests and docstrings accordingly
  • Loading branch information
Uwe Hernandez Acosta committed Oct 4, 2023
1 parent 196df3c commit c3d4456
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 46 deletions.
60 changes: 40 additions & 20 deletions src/interfaces/setup_interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ quantity depends on is kept constant.
Every subtype of `AbstractComputationSetup` should implement the interface function
```Julia
_is_valid_input(stp::AbstractComputationSetup, input) # default: true
_assert_valid_input(stp::AbstractComputationSetup, input)
```
which should return true iff the `input` is valid for the computation of the associated quantity (see [`_is_valid_input`](@ref) and [`_assert_valid_input`](@ref) for more details).
The default implementation always returns `true`. Provide a custom implementation if a different behavior is required.
which should throw and an exception subtyped from [`AbstractInvalidInputException`](@ref) if the `input` is not valid for the computation of the associated quantity (see [`_is_valid_input`](@ref) and [`_assert_valid_input`](@ref) for more details).
The default implementation does nothing, i.e. every input is valid by default. Provide a custom implementation if a different behavior is required.
## Actual computation
Expand Down Expand Up @@ -61,31 +61,24 @@ abstract type AbstractComputationSetup end
_is_computation_setup(::AbstractComputationSetup) = true

"""
Abstract base type for exceptions indicating invalid input. See [`InvalidInputError`](@ref) for a simple concrete implementation.
Concrete implementations should at least implement
_is_valid_input(stp::AbstractComputationSetup, input::Any)
Interface function, which returns true if the constraints of the `input` associated with the quantity of `stp` are met.
This function is called to validate the input of [`compute`](@ref) before calling [`_compute`](@ref).
!!! note "Default implementation"
Since no input validation is equivalent to every input being valid, this function returns `true` by default.
This behavior can be overwritten if actual validation is necessary.
```Julia
An assert version of this function is given by [`_assert_valid_input`](@ref), which directly uses the output of this function.
Base.showerror(io::IO, err::CustomInvalidError) where {CustomInvalidError<:AbstractInvalidInputException}
```
"""
@inline function _is_valid_input(stp::AbstractComputationSetup, input)
return true
end
abstract type AbstractInvalidInputException <: Exception end

"""
InvalidInputError(msg::String)
Exception which is thrown if a given input is invalid, e.g. passed to [`_assert_valid_input`](@ref).
"""
struct InvalidInputError <: Exception
struct InvalidInputError <: AbstractInvalidInputException
msg::String
end
Base.showerror(io::IO, err::InvalidInputError) = println(io, "InvalidInputError: $(err.msg).")
Expand All @@ -111,11 +104,38 @@ Interface function, which asserts that the given `input` is valid, and throws an
"""
@inline function _assert_valid_input(stp::AbstractComputationSetup,input)
_is_valid_input(stp,input) || throw(InvalidInputError("Something wrong with the input!\n setup:$stp \n input:$input"))

return nothing
end

"""
_is_valid_input(stp::AbstractComputationSetup, input::Any)
Interface function, which returns true if the constraints of the `input` associated with the quantity of `stp` are met.
This function is called to validate the input of [`compute`](@ref) before calling [`_compute`](@ref).
!!! note "Default implementation"
Since no input validation is equivalent to every input being valid, this function returns `true` by default.
This behavior can be overwritten if actual validation is necessary.
An assert version of this function is given by [`_assert_valid_input`](@ref), which directly uses the output of this function.
"""
@inline function _is_valid_input(stp::AbstractComputationSetup, input)
try
_assert_valid_input(stp,input)
catch e
if isa(e, AbstractInvalidInputException)
return false
end
@warn "The function _assert_valid_input throws an Exception, which is not an InvalidInputError! The Exception thrown is: "
throw(e)
end
return true
end

"""
function _post_processing(stp::AbstractComputationSetup, input::Any, result::Any)
Expand Down
45 changes: 19 additions & 26 deletions test/interfaces/setup_interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ RTOL = sqrt(eps())

_groundtruth_compute(x) = x
_groundtruth_input_validation(x) = (x>0)
struct TestException <: QEDprocesses.AbstractInvalidInputException end
function _groundtruth_valid_input_assert(x)
_groundtruth_input_validation(x)||throw(TestException())
nothing
end
_transform_to_invalid(x) = -abs(x)
_groundtruth_post_processing(x,y) = x+y

Expand All @@ -19,48 +24,42 @@ QEDprocesses._compute(stp::AbstractTestSetup, x) = _groundtruth_compute(x)
# setup with default implementations
struct TestSetupDefault <: AbstractTestSetup end

# setup with custom _is_valid_input
struct TestSetupCustomIsValidInput <: AbstractTestSetup end
QEDprocesses._is_valid_input(::TestSetupCustomIsValidInput, x) = _groundtruth_input_validation(x)

# setup with custom _assert_valid_input
struct TestSetupCustomAssertValidInput<: AbstractTestSetup end
QEDprocesses._is_valid_input(::TestSetupCustomAssertValidInput, x) = _groundtruth_input_validation(x)
struct TestException <: Exception end
function QEDprocesses._assert_valid_input(stp::TestSetupCustomAssertValidInput, x)
QEDprocesses._is_valid_input(stp,x)||throw(TestException())
return nothing
end
QEDprocesses._assert_valid_input(stp::TestSetupCustomAssertValidInput, x) = _groundtruth_valid_input_assert(x)

# setup with custom post processing
struct TestSetupCustomPostProcessing<: AbstractTestSetup end
QEDprocesses._post_processing(::TestSetupCustomPostProcessing,x,y) = _groundtruth_post_processing(x,y)

# setup with custom input validation and post processing
struct TestSetupCustom <: AbstractTestSetup end
QEDprocesses._is_valid_input(::TestSetupCustom, x) = _groundtruth_input_validation(x)
QEDprocesses._assert_valid_input(stp::TestSetupCustom, x) = _groundtruth_valid_input_assert(x)
QEDprocesses._post_processing(::TestSetupCustom,x,y) = _groundtruth_post_processing(x,y)

# setup which fail on computation with default implementations
struct TestSetupFAIL <: AbstractComputationSetup end

# setup which fail on computation with custom input validation
# setup which fail on computation with custom input validation, where the
# invalid input will be caught before the computation.
struct TestSetupCustomValidationFAIL <: AbstractComputationSetup end
QEDprocesses._is_valid_input(::TestSetupCustomValidationFAIL, x) = _groundtruth_input_validation(x)
QEDprocesses._assert_valid_input(stp::TestSetupCustomValidationFAIL, x) = _groundtruth_valid_input_assert(x)

# setup which fail on computation with custom post processing
struct TestSetupCustomPostProcessingFAIL <: AbstractComputationSetup end
QEDprocesses._post_processing(::TestSetupCustomPostProcessingFAIL,x,y) = _groundtruth_post_processing(x,y)
@testset "general computation setup interface" begin
@testset "interface fail" begin
rnd_input = rand(RNG)

@test_throws MethodError QEDprocesses._compute(TestSetupFAIL(), rnd_input)
@test_throws MethodError compute(TestSetupFAIL(), rnd_input)

@test_throws MethodError QEDprocesses._compute(TestSetupCustomValidationFAIL(), rnd_input)
@test_throws MethodError compute(TestSetupCustomValidationFAIL(), rnd_input)
# invalid input should be caught without throwing a MethodError
@test_throws InvalidInputError compute(TestSetupCustomValidationFAIL(), _transform_to_invalid(rnd_input))
@test_throws TestException compute(TestSetupCustomValidationFAIL(), _transform_to_invalid(rnd_input))


@test_throws MethodError QEDprocesses._compute(TestSetupCustomPostProcessingFAIL(), rnd_input)
@test_throws MethodError compute(TestSetupCustomPostProcessingFAIL(), rnd_input)
Expand All @@ -78,19 +77,13 @@ QEDprocesses._post_processing(::TestSetupCustomPostProcessingFAIL,x,y) = _ground
end

@testset "custom input validation" begin
stp = TestSetupCustomIsValidInput()
stp = TestSetupCustomAssertValidInput()
rnd_input = rand(RNG)
@test QEDprocesses._is_valid_input(stp, _groundtruth_input_validation(rnd_input))
@test !QEDprocesses._is_valid_input(stp, !_groundtruth_input_validation(rnd_input))
@test isapprox(compute(stp, rnd_input), _groundtruth_compute(rnd_input), atol=ATOL,rtol=RTOL)
@test_throws InvalidInputError QEDprocesses._assert_valid_input(stp, _transform_to_invalid(rnd_input))
@test_throws InvalidInputError compute(stp, _transform_to_invalid(rnd_input))

stp2 = TestSetupCustomAssertValidInput()
rnd_input2 = rand(RNG)
@test QEDprocesses._assert_valid_input(stp2,rnd_input2)==nothing
@test_throws TestException QEDprocesses._assert_valid_input(stp2,_transform_to_invalid(rnd_input2))
@test_throws TestException compute(stp2, _transform_to_invalid(rnd_input2))
@test QEDprocesses._assert_valid_input(stp,rnd_input)==nothing
@test_throws TestException QEDprocesses._assert_valid_input(stp,_transform_to_invalid(rnd_input))
@test_throws TestException compute(stp, _transform_to_invalid(rnd_input))

end

Expand All @@ -106,10 +99,10 @@ QEDprocesses._post_processing(::TestSetupCustomPostProcessingFAIL,x,y) = _ground
stp = TestSetupCustom()
rnd_input = rand(RNG)
rnd_output = rand(RNG)

@test QEDprocesses._is_valid_input(stp, _groundtruth_input_validation(rnd_input))
@test !QEDprocesses._is_valid_input(stp, !_groundtruth_input_validation(rnd_input))
@test_throws InvalidInputError compute(stp, _transform_to_invalid(rnd_input))
@test_throws TestException() compute(stp, _transform_to_invalid(rnd_input))
@test isapprox(QEDprocesses._post_processing(stp,rnd_input,rnd_output), _groundtruth_post_processing(rnd_input,rnd_output))
@test isapprox(compute(stp,rnd_input), _groundtruth_post_processing(rnd_input,_groundtruth_compute(rnd_input)))
end
Expand Down

0 comments on commit c3d4456

Please sign in to comment.