Skip to content

Commit 2e10db2

Browse files
authored
InputOutputCurve function evaluation (#517)
* InputOutputCurve function evaluation * put domain check inside function
1 parent bef6973 commit 2e10db2

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

src/function_data.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,10 @@ _eval_fd_impl(
652652
"Evaluate the `PiecewiseLinearData` or `PiecewiseStepData` at a given x-coordinate"
653653
function (fd::Union{PiecewiseLinearData, PiecewiseStepData})(x::Real)
654654
lb, ub = get_domain(fd)
655-
(x < lb || x > ub) &&
655+
# defend against floating point precision issues at the boundaries.
656+
((lb <= x <= ub) || isapprox(x, lb) || isapprox(x, ub)) ||
656657
throw(ArgumentError("x=$x is outside the domain [$lb, $ub]"))
658+
x = clamp(x, lb, ub)
657659
x_coords = get_x_coords(fd)
658660
y_coords = get_y_coords(fd)
659661
i_leq = searchsortedlast(x_coords, x) # uses binary search!

src/value_curve.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ InputOutputCurve{T}(
4141
) where {(T <: Union{QuadraticFunctionData, LinearFunctionData, PiecewiseLinearData})} =
4242
InputOutputCurve{T}(function_data, nothing)
4343

44+
"""
45+
Evaluate the `InputOutputCurve` at a given input value `x`.
46+
"""
47+
(ioc::InputOutputCurve)(x::Real) = get_function_data(ioc)(x)
48+
4449
"""
4550
An incremental (or 'marginal') curve, relating the production quantity to the derivative of
4651
cost: `y = f'(x)`. Can be used, for instance, in the representation of a [`CostCurve`](@ref)
@@ -62,7 +67,7 @@ IncrementalCurve(function_data, initial_input) =
6267
IncrementalCurve{T}(
6368
function_data,
6469
initial_input,
65-
) where {(T <: Union{QuadraticFunctionData, LinearFunctionData, PiecewiseLinearData})} =
70+
) where {(T <: Union{LinearFunctionData, PiecewiseStepData})} =
6671
IncrementalCurve{T}(function_data, initial_input, nothing)
6772

6873
"""
@@ -87,7 +92,7 @@ AverageRateCurve(function_data, initial_input) =
8792
AverageRateCurve{T}(
8893
function_data,
8994
initial_input,
90-
) where {(T <: Union{QuadraticFunctionData, LinearFunctionData, PiecewiseLinearData})} =
95+
) where {(T <: Union{LinearFunctionData, PiecewiseStepData})} =
9196
AverageRateCurve{T}(function_data, initial_input, nothing)
9297

9398
"Get the `initial_input` field of this `ValueCurve` (not defined for `InputOutputCurve`)"

test/test_cost_functions.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,32 @@ end
337337
) ==
338338
IS.LinearCurve(10.0, 7.0)
339339
end
340+
341+
@testset "Test prohibited FunctionData types" begin
342+
# Incremental and Average Rate curves only support
343+
# linear and piecewise step function data.
344+
q_fd = IS.QuadraticFunctionData(1, 2, 3)
345+
pwl_fd = IS.PiecewiseLinearData([(x = 0.0, y = 1.0), (x = 1.0, y = 2.0)])
346+
@test_throws MethodError IS.IncrementalCurve(q_fd, 0.0)
347+
@test_throws MethodError IS.AverageRateCurve(q_fd, 0.0)
348+
@test_throws MethodError IS.IncrementalCurve(pwl_fd, 0.0)
349+
@test_throws MethodError IS.AverageRateCurve(pwl_fd, 0.0)
350+
end
351+
352+
@testset "Test InputOutputCurve evaluation" begin
353+
io_quadratic = IS.InputOutputCurve(IS.QuadraticFunctionData(1, 2, 3))
354+
@test io_quadratic(0.0) == 3.0
355+
@test io_quadratic(1.0) == 6.0
356+
@test io_quadratic(2.0) == 11.0
357+
358+
io_linear = IS.InputOutputCurve(IS.LinearFunctionData(3, 2))
359+
@test io_linear(0.0) == 2.0
360+
@test io_linear(1.0) == 5.0
361+
@test io_linear(2.0) == 8.0
362+
363+
pwl = IS.PiecewiseLinearData([(x = 1, y = 3), (x = 3, y = 7), (x = 5, y = 11)])
364+
io_piecewise = IS.InputOutputCurve(pwl)
365+
@test io_piecewise(1.0) == 3.0
366+
@test io_piecewise(3.0) == 7.0
367+
@test io_piecewise(5.0) == 11.0
368+
end

test/test_function_data.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,3 +689,23 @@ end
689689
compact_plain_ans
690690
end
691691
end
692+
693+
@testset "Test piecewise domain checking" begin
694+
pwl = IS.PiecewiseStepData([1, 3, 5], [8, 10])
695+
696+
# floating point inputs
697+
@test_throws ArgumentError pwl(0.5)
698+
@test_throws ArgumentError pwl(5.5)
699+
pwl(2.5)
700+
701+
# non floating point inputs
702+
@test_throws ArgumentError pwl(1 // 2)
703+
@test_throws ArgumentError pwl(5 + 1 // 2)
704+
pwl(5 // 2)
705+
706+
# floating point precision edge cases (should not error)
707+
@assert isapprox(1 - eps() / 2, 1)
708+
@assert isapprox(5 + eps() / 2, 5)
709+
pwl(1 - eps() / 2)
710+
pwl(5 + eps() / 2)
711+
end

0 commit comments

Comments
 (0)