Find closest convex function for piecewise linear data#513
Merged
jd-lara merged 5 commits intojd/convex_checksfrom Jan 15, 2026
Merged
Conversation
Implement make_convex and make_concave functions using the Pool Adjacent Violators Algorithm (PAVA) for isotonic regression. This provides an optimal O(n) solution for finding the closest convex/concave approximation to non-convex PiecewiseLinearData and PiecewiseStepData. New functions: - isotonic_regression / antitonic_regression: Core PAVA implementation - make_convex / make_concave: Convert piecewise data to convex/concave form - convexity_violations: Find indices where convexity is violated - convexity_gap: Measure the maximum convexity violation - approximation_error: Compute error between original and approximated data Features: - Multiple weighting schemes (:uniform, :length, custom) - Multiple anchor options for PiecewiseLinearData (:first, :last, :centroid) - Support for L1, L2, and Linf error metrics
Remove antitonic_regression, make_concave for PiecewiseStepData and PiecewiseLinearData, and related tests. The library only needs conversion from concave to convex (make_convex), not the reverse.
| return _get_x_lengths(x_coords) | ||
| elseif weights isa Vector{Float64} | ||
| length(weights) == n_segments || | ||
| throw(ArgumentError("Custom weights must have length $n_segments, got $(length(weights))")) |
Contributor
There was a problem hiding this comment.
[JuliaFormatter] reported by reviewdog 🐶
Suggested change
| throw(ArgumentError("Custom weights must have length $n_segments, got $(length(weights))")) | |
| throw( | |
| ArgumentError( | |
| "Custom weights must have length $n_segments, got $(length(weights))", | |
| ), | |
| ) |
| approximated = IS.PiecewiseStepData([0.0, 1.0, 2.0, 3.0], [7.5, 7.5, 15.0]) | ||
|
|
||
| # L2 error with uniform weights | ||
| err_l2 = IS.approximation_error(original, approximated; metric = :L2, weights = :uniform) |
Contributor
There was a problem hiding this comment.
[JuliaFormatter] reported by reviewdog 🐶
Suggested change
| err_l2 = IS.approximation_error(original, approximated; metric = :L2, weights = :uniform) | |
| err_l2 = | |
| IS.approximation_error(original, approximated; metric = :L2, weights = :uniform) |
| @test err_l2 ≈ expected_l2 | ||
|
|
||
| # L1 error | ||
| err_l1 = IS.approximation_error(original, approximated; metric = :L1, weights = :uniform) |
Contributor
There was a problem hiding this comment.
[JuliaFormatter] reported by reviewdog 🐶
Suggested change
| err_l1 = IS.approximation_error(original, approximated; metric = :L1, weights = :uniform) | |
| err_l1 = | |
| IS.approximation_error(original, approximated; metric = :L1, weights = :uniform) |
| @test err_l1 ≈ expected_l1 | ||
|
|
||
| # Linf error | ||
| err_linf = IS.approximation_error(original, approximated; metric = :Linf, weights = :uniform) |
Contributor
There was a problem hiding this comment.
[JuliaFormatter] reported by reviewdog 🐶
Suggested change
| err_linf = IS.approximation_error(original, approximated; metric = :Linf, weights = :uniform) | |
| err_linf = | |
| IS.approximation_error(original, approximated; metric = :Linf, weights = :uniform) |
| @test err ≈ expected | ||
|
|
||
| # Test invalid metric | ||
| @test_throws ArgumentError IS.approximation_error(original, approximated; metric = :invalid) |
Contributor
There was a problem hiding this comment.
[JuliaFormatter] reported by reviewdog 🐶
Suggested change
| @test_throws ArgumentError IS.approximation_error(original, approximated; metric = :invalid) | |
| @test_throws ArgumentError IS.approximation_error( | |
| original, | |
| approximated; | |
| metric = :invalid, | |
| ) |
Refactor approximation_error functions to use Julia's standard LinearAlgebra functions instead of manually implementing norms: - L2 norm: Use norm() with weighted scaling - L1 norm: Use dot() for weighted sum - L∞ norm: Use norm(diff, Inf) This makes the code more idiomatic and leverages optimized implementations from Julia's standard library.
Contributor
|
Using these implementations on EasterInterConnection.jl. Once proved to work properly, I will include review the functions and contribute to this PR. |
…#526) * Modified slope convexity and concavity checks to return False when curve is not convex/concave. * Uploading script that uses newly defined methods in function_data.jl for convexifying non-convex functions * Added method check_nonconvex, which will be very useful. Recently added scripts are examples of how to use them. * Added scripts that serve as example of how to use the is_nonconvex, PAVA, approximation_error and other methods. These scripts worked succesfully wit pwl curves. They are designed to work with all type of curves but have not been tested for other curves atm. --------- Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov>
…od-01NXcPZ4Xpj2YsW4FUurN8X9
jd-lara
added a commit
that referenced
this pull request
Jan 21, 2026
* Add convex approximation methods for piecewise data Implement make_convex and make_concave functions using the Pool Adjacent Violators Algorithm (PAVA) for isotonic regression. This provides an optimal O(n) solution for finding the closest convex/concave approximation to non-convex PiecewiseLinearData and PiecewiseStepData. New functions: - isotonic_regression / antitonic_regression: Core PAVA implementation - make_convex / make_concave: Convert piecewise data to convex/concave form - convexity_violations: Find indices where convexity is violated - convexity_gap: Measure the maximum convexity violation - approximation_error: Compute error between original and approximated data Features: - Multiple weighting schemes (:uniform, :length, custom) - Multiple anchor options for PiecewiseLinearData (:first, :last, :centroid) - Support for L1, L2, and Linf error metrics * Remove make_concave functions - only convex conversion needed Remove antitonic_regression, make_concave for PiecewiseStepData and PiecewiseLinearData, and related tests. The library only needs conversion from concave to convex (make_convex), not the reverse. * Use LinearAlgebra library for norm calculations Refactor approximation_error functions to use Julia's standard LinearAlgebra functions instead of manually implementing norms: - L2 norm: Use norm() with weighted scaling - L1 norm: Use dot() for weighted sum - L∞ norm: Use norm(diff, Inf) This makes the code more idiomatic and leverages optimized implementations from Julia's standard library. * Claude/convex approximation method 01 n xc pz4 xpj2 ys w4 f uur n8 x9 (#526) * Modified slope convexity and concavity checks to return False when curve is not convex/concave. * Uploading script that uses newly defined methods in function_data.jl for convexifying non-convex functions * Added method check_nonconvex, which will be very useful. Recently added scripts are examples of how to use them. * Added scripts that serve as example of how to use the is_nonconvex, PAVA, approximation_error and other methods. These scripts worked succesfully wit pwl curves. They are designed to work with all type of curves but have not been tested for other curves atm. --------- Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: PBGP <pbotin@nrel.gov> Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov>
jd-lara
added a commit
that referenced
this pull request
Feb 9, 2026
* Find closest convex function for piecewise linear data (#513) * Add convex approximation methods for piecewise data Implement make_convex and make_concave functions using the Pool Adjacent Violators Algorithm (PAVA) for isotonic regression. This provides an optimal O(n) solution for finding the closest convex/concave approximation to non-convex PiecewiseLinearData and PiecewiseStepData. New functions: - isotonic_regression / antitonic_regression: Core PAVA implementation - make_convex / make_concave: Convert piecewise data to convex/concave form - convexity_violations: Find indices where convexity is violated - convexity_gap: Measure the maximum convexity violation - approximation_error: Compute error between original and approximated data Features: - Multiple weighting schemes (:uniform, :length, custom) - Multiple anchor options for PiecewiseLinearData (:first, :last, :centroid) - Support for L1, L2, and Linf error metrics * Remove make_concave functions - only convex conversion needed Remove antitonic_regression, make_concave for PiecewiseStepData and PiecewiseLinearData, and related tests. The library only needs conversion from concave to convex (make_convex), not the reverse. * Use LinearAlgebra library for norm calculations Refactor approximation_error functions to use Julia's standard LinearAlgebra functions instead of manually implementing norms: - L2 norm: Use norm() with weighted scaling - L1 norm: Use dot() for weighted sum - L∞ norm: Use norm(diff, Inf) This makes the code more idiomatic and leverages optimized implementations from Julia's standard library. * Claude/convex approximation method 01 n xc pz4 xpj2 ys w4 f uur n8 x9 (#526) * Modified slope convexity and concavity checks to return False when curve is not convex/concave. * Uploading script that uses newly defined methods in function_data.jl for convexifying non-convex functions * Added method check_nonconvex, which will be very useful. Recently added scripts are examples of how to use them. * Added scripts that serve as example of how to use the is_nonconvex, PAVA, approximation_error and other methods. These scripts worked succesfully wit pwl curves. They are designed to work with all type of curves but have not been tested for other curves atm. --------- Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: PBGP <pbotin@nrel.gov> Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> * update file structure * Find closest convex function for piecewise linear data (#513) * Add convex approximation methods for piecewise data Implement make_convex and make_concave functions using the Pool Adjacent Violators Algorithm (PAVA) for isotonic regression. This provides an optimal O(n) solution for finding the closest convex/concave approximation to non-convex PiecewiseLinearData and PiecewiseStepData. New functions: - isotonic_regression / antitonic_regression: Core PAVA implementation - make_convex / make_concave: Convert piecewise data to convex/concave form - convexity_violations: Find indices where convexity is violated - convexity_gap: Measure the maximum convexity violation - approximation_error: Compute error between original and approximated data Features: - Multiple weighting schemes (:uniform, :length, custom) - Multiple anchor options for PiecewiseLinearData (:first, :last, :centroid) - Support for L1, L2, and Linf error metrics * Remove make_concave functions - only convex conversion needed Remove antitonic_regression, make_concave for PiecewiseStepData and PiecewiseLinearData, and related tests. The library only needs conversion from concave to convex (make_convex), not the reverse. * Use LinearAlgebra library for norm calculations Refactor approximation_error functions to use Julia's standard LinearAlgebra functions instead of manually implementing norms: - L2 norm: Use norm() with weighted scaling - L1 norm: Use dot() for weighted sum - L∞ norm: Use norm(diff, Inf) This makes the code more idiomatic and leverages optimized implementations from Julia's standard library. * Claude/convex approximation method 01 n xc pz4 xpj2 ys w4 f uur n8 x9 (#526) * Modified slope convexity and concavity checks to return False when curve is not convex/concave. * Uploading script that uses newly defined methods in function_data.jl for convexifying non-convex functions * Added method check_nonconvex, which will be very useful. Recently added scripts are examples of how to use them. * Added scripts that serve as example of how to use the is_nonconvex, PAVA, approximation_error and other methods. These scripts worked succesfully wit pwl curves. They are designed to work with all type of curves but have not been tested for other curves atm. --------- Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: PBGP <pbotin@nrel.gov> Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> * update file structure * remove scripts * add some additional testing * Convexification of cost curves (#545) * Moved convexity checks from function_data to convexity_checks (already existing) and reviewed their logic, getting rid of redundant is_nonconvex() and minor changes on the others to ensure linear curves are considered convex * Modified function_data test according to latest changes * Created test for convexity checks, passed * Aliased is_concave to not is_convex and deprecated the function * Restructuration, added new dispatch type for is_convex and defined multiple d ispatch for make_convex, making sure heat rate curves (incremental curves) are treated as they need to, performing an integration and der ivating back * Merged convexity_checks into make_convex * merge continutation * restructre make_convex sctiope * removed deprecations because is_concave definitions were previously defined and I dont wand to mess with them * Removed make_convex methods for IncrementalCurve{LinearFunctionData} and AverageRateCurve{LinearFunctionData} because it did not make sense given how quadratic curves are being convexified * reorganized code into convexity checks and make convex * Check ValueCurve convexity directly via slopes * Added make_convex methods for fuel and cost curves * Modified is_convex to delegate to function data * Created multiple dispatch for ValueCurves, corresponding tests and modified IS.jl order to include cost alias correctly * Added colinear segment merging to make_function, which required creating a new method, added/modified tests accordingly * Modified convexification logic in InputOutputCurve{QuadraticFunctionData} curves * Removed quadratic from multiple dispatch. Added comments with reasons. * Removed make_convex dispatch for nputOutputCurve{LinearFunctionData} * Added NotImplementedError for make_convex, is_convex and approximation_error() * Added info statements to make_convex() * Removed unnecessary merge_colinear_segments() dispatch for linear and quadratic curves * Removed deleted method cyrve type definitions tests --------- Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> Co-authored-by: Abril-Guevara, Sara <Sara.AbrilGuevara@nrel.gov> * fix file * update the md file for claude to stop looking for master * fix testing * simplify the implementation for internally defined weights * remove custom weights * some improvements to function data * Removed is_concave() deprecated method definitions * Define is_concave() and slope_copncavity_checks() back because it is used for a different purpose by PSY * Rename make_convex() into make_convex_approximation() * format update Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Added is_concave(::CostCurve{PiecewiseIncrementalCurve}) dispatch * Simplified curve multiple dispatch definition * Defined is_valid_data() and integrated into make_convex_approximation() * Reviewed warn and error logs * Update src/function_data/convexity_checks.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_convexity_checks.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Removed NotImplementedError() calls * Added is_strictly_decreasing and is_strictly_increasing() methods and modified is_valid_data() to be generic. Also, added generator_name argument to make_convex() in order to identify the generator in the log * style review * Modified make_convex_approximation(): 1) Added is_strictly_increasing() check, 2) Got rid of internal make_convex_approximation(), 3) Modified loggin to only print the error and the name of the generator. * Remove unused convexity_violation and convexity_gap() methods * Created dedicated increasing_curve_convex_approximation() that applies sppecifically to increasing curves. * Added info log message to merge_colinear_segments * Update src/function_data/convexity_checks.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_merge_colinear.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * add missing docstring * remove unsued piece of code * throw errors instead of log erros * minor update to claude.md * update to convexity check tolerance * update make_convext to throw erros and use tolerances * update testing * docstring fix * Modified the logic of the increasing_curve_convex_approximation() AverageRate and Increment al curve definitions for simplicity, removing the skip_validation parameters. Reviewed docstrings * Added an additional colinear segments merge after the convexification is applied * Update src/function_data/make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_function_data.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_make_convex.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: PBGP <pbotin@nrel.gov> Co-authored-by: PabloBotinGP <Pablo.Botin@nrel.gov> Co-authored-by: Abril-Guevara, Sara <Sara.AbrilGuevara@nrel.gov> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement make_convex and make_concave functions using the Pool Adjacent Violators Algorithm (PAVA) for isotonic regression. This provides an optimal O(n) solution for finding the closest convex/concave approximation to non-convex PiecewiseLinearData and PiecewiseStepData.
New functions:
Features: