Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d9db492
First pass at a new PWL API
joehuchette Mar 4, 2020
a1b236b
Working port of incremental and log 1D formulations
joehuchette Mar 12, 2020
484ce20
Port over bivariate code
joehuchette Apr 6, 2020
05d0783
Add disaggregated logarithmic formulation
joehuchette Apr 6, 2020
b47bf93
Reorganize methods, adding stub for each method
joehuchette Apr 6, 2020
60dc6cd
More reorganization
joehuchette Apr 6, 2020
7e94d41
Plug in most existing formulations
joehuchette Apr 11, 2020
f6fefce
Support for optimal IB formulations
joehuchette Apr 11, 2020
ffdc911
More careful continuity checking
joehuchette May 18, 2020
e385b52
Get rewrite up and running (#59)
trulsf Jan 12, 2025
a545754
Merge remote-tracking branch 'remotes/origin/master' into rewrite
trulsf Jan 12, 2025
fdcf5ed
Formatting changes
trulsf Jan 12, 2025
cde3271
Add missing license headers
trulsf Jan 12, 2025
eb18432
Refactor to use explicit LinearAlgebra functions in calculations
trulsf Jan 12, 2025
76a4c42
Refactor piecewise linear function implementation and add univariate/…
trulsf Jan 14, 2025
c7d681c
Automatic code formatting
trulsf Jan 14, 2025
43f7dba
Update README to clarify piecewise linear function support and availa…
trulsf Jan 14, 2025
b4c3862
Correct piecewise linear function test
trulsf Jan 14, 2025
a36f2c4
Update CI configuration and project compatibility for Julia 1.10 as LTS
trulsf Jan 14, 2025
f85a8fc
adds docs and tests
ValentinKaisermayer Mar 31, 2026
0b750fc
Add _next_pwl_id! and _pwl_name helpers
ValentinKaisermayer Mar 31, 2026
4718d87
formatting and speed up tests
ValentinKaisermayer Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 53 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,73 @@
name: CI
on:
push:
branches: [master]
branches:
- master
tags: ["*"]
pull_request:
types: [opened, synchronize, reopened]
# needed to allow julia-actions/cache to delete old caches that it has created
permissions:
actions: write
contents: read
workflow_dispatch:
concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created
actions: write
contents: read
strategy:
fail-fast: false
matrix:
version: ['1.6', '1']
os: [ubuntu-latest, macOS-latest, windows-latest]
arch: [x64]
include:
# Also test against 32-bit Linux.
- version: '1'
os: ubuntu-latest
arch: x86
version:
- "1.10"
- "1.12"
- "pre"
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/cache@v2
- run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev xsettingsd x11-xserver-utils
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
file: lcov.info
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
docs:
name: Documentation
runs-on: ubuntu-latest
permissions:
actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created
contents: write
statuses: write
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v2
with:
version: "1"
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-docdeploy@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
- name: Run doctests
shell: julia --project=docs --color=yes {0}
run: |
using Documenter: DocMeta, doctest
using PiecewiseLinearOpt
DocMeta.setdocmeta!(PiecewiseLinearOpt, :DocTestSetup, :(using PiecewiseLinearOpt); recursive=true)
doctest(PiecewiseLinearOpt)
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
*.jl.mem
Manifest.toml
*.DS_Store
.DS_Store
.vscode/settings.json
coverage/
docs/build/
17 changes: 10 additions & 7 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ version = "0.4.2"
[deps]
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
Cbc = "1"
JuMP = "0.23, 1"
MathOptInterface = "1"
julia = "1.6"
HiGHS = "1"
JuMP = "1"
julia = "1.10"
LinearAlgebra = "1"
Random = "1"
Aqua = "0.8"
Test = "1"

[extras]
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Cbc", "Test"]
test = ["Aqua", "Test", "HiGHS"]
86 changes: 34 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![Build Status](https://github.com/jump-dev/PiecewiseLinearOpt.jl/workflows/CI/badge.svg)](https://github.com/jump-dev/PiecewiseLinearOpt.jl/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/jump-dev/PiecewiseLinearOpt.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/jump-dev/PiecewiseLinearOpt.jl)
[![Aqua QA](https://juliatesting.github.io/Aqua.jl/dev/assets/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

[PiecewiseLinearOpt.jl](https://github.com/jump-dev/PiecewiseLinearOpt.jl) is a
JuMP extension for modeling optimization problems containing piecewise linear
Expand Down Expand Up @@ -33,37 +34,21 @@ Pkg.add("PiecewiseLinearOpt")
## Use with JuMP

Current support is limited to modeling the graph of a continuous piecewise
linear function, either univariate or bivariate, with the goal of adding support
for the epigraphs of lower semicontinuous piecewise linear functions.
linear function, with a primary focus on univariate or bivariate functions.
There are also methods for more general multivariate problems.

### Univariate

Consider a piecewise linear function `f`. The function is described a domain `d`,
which is a set of breakpoints between pieces, and the function value `fd` at
those breakpoints:
Consider a piecewise linear function described by a domain `d`,
which is a set of breakpoints between pieces, and the function value at
those breakpoints given by the function `f` at those points:

```julia
julia> f(x) = sin(x)
f (generic function with 1 method)

julia> d = 0:0.5:2pi
0.0:0.5:6.0

julia> fd = f.(d)
13-element Vector{Float64}:
0.0
0.479425538604203
0.8414709848078965
0.9974949866040544
0.9092974268256817
0.5984721441039564
0.1411200080598672
-0.35078322768961984
-0.7568024953079282
-0.977530117665097
-0.9589242746631385
-0.7055403255703919
-0.27941549819892586
julia> f(x) = sin(x)
f (generic function with 1 method)
```

To represent this function in a JuMP model, do:
Expand All @@ -72,13 +57,14 @@ To represent this function in a JuMP model, do:
using JuMP, PiecewiseLinearOpt
model = Model()
@variable(model, x)
z = PiecewiseLinearOpt.piecewiselinear(model, x, d, fd; method = :CC)
z = PiecewiseLinearOpt.piecewiselinear(model, x, d, f; method = Logarithmic())
@objective(model, Min, z) # minimize f(x)
```

### Bivariate

Consider piecewise linear approximation for the function $f(x, y) = exp(x + y)$:
Consider a piecewise linear approximation for the function $f(x, y) = exp(x + y)$
on a triangular grid with a best fit pattern:

```julia
using JuMP, PiecewiseLinearOpt
Expand All @@ -92,38 +78,34 @@ z = PiecewiseLinearOpt.piecewiselinear(
0:0.1:1,
0:0.1:1,
(u, v) -> exp(u + v);
method = :DisaggLogarithmic,
method = SixStencil(),
pattern = :BestFit
)
@objective(model, Min, z)
```

## Methods

The following formualations are available in the package and is provided through the
`method` argument:

Supported multivariate formulations:
* `ConvexCombination()`
* `DisaggregatedLogarithmic()`
* `MultipleChoice()`: Limited support as it currently needs an explicit formulations with hyperplanes

Supported univariate formulations:
* `Incremental()`
* `Logarithmic()`
* `LogarithmicIndependentBranching()`
* `NativeSOS2()`
* `ZigZagBinary()`
* `ZigZagInteger()`

The following bivariate formulations are available and can be combined with most univariate
formulations to impose two axis-aligned SOS2 constraints. See the associated paper for more details.
* `K1(sos2_method)`: requires a K1 grid triangulation
* `UnionJack(sos2_method)`: requires a UnionJack grid triangulation
* `SixStencil(sos2_method)`: requires a grid triangulation
* `NineStencil(sos2_method)`: requires a grid triangulation

* Convex combination (`:CC`)
* Multiple choice (`:MC`)
* Native SOS2 branching (`:SOS2`)
* Incremental (`:Incremental`)
* Logarithmic (`:Logarithmic`; default)
* Disaggregated Logarithmic (`:DisaggLogarithmic`)
* Binary zig-zag (`:ZigZag`)
* General integer zig-zag (`:ZigZagInteger`)

Supported bivariate formulations for entire constraint:

* Convex combination (`:CC`)
* Multiple choice (`:MC`)
* Disaggregated Logarithmic (`:DisaggLogarithmic`)

Also, you can use any univariate formulation for bivariate functions as well.
They will be used to impose two axis-aligned SOS2 constraints, along with the
"6-stencil" formulation for the triangle selection portion of the constraint.
See the associated paper for more details. In particular, the following are also
acceptable bivariate formulation choices:

* Native SOS2 branching (`:SOS2`)
* Incremental (`:Incremental`)
* Logarithmic (`:Logarithmic`)
* Binary zig-zag (`:ZigZag`)
* General integer zig-zag (`:ZigZagInteger`)
5 changes: 5 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
PiecewiseLinearOpt = "0f51c51e-adfa-5141-8a04-d40246b8977c"
34 changes: 34 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

# julia --project=docs docs/make.jl
using PiecewiseLinearOpt
using Documenter

DocMeta.setdocmeta!(
PiecewiseLinearOpt,
:DocTestSetup,
:(using PiecewiseLinearOpt, JuMP);
recursive = true,
)

makedocs(;
modules = [PiecewiseLinearOpt],
sitename = "PiecewiseLinearOpt.jl",
authors = "Joey Huchette and contributors",
format = Documenter.HTML(;
canonical = "https://jump-dev.github.io/PiecewiseLinearOpt.jl",
edit_link = "master",
assets = String[],
),
pages = [
"Home" => "index.md",
"Tutorial" => "tutorial.md",
"Formulation Methods" => "methods.md",
"API Reference" => "api.md",
],
warnonly = true,
)

deploydocs(;
repo = "github.com/jump-dev/PiecewiseLinearOpt.jl",
devbranch = "master",
)
58 changes: 58 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# API Reference

## Main function

```@docs
piecewiselinear
```

## PWL function types

```@docs
PWLFunction
UnivariatePWLFunction
BivariatePWLFunction
```

## Direction enum

```@docs
PiecewiseLinearOpt.DIRECTION
```

## Univariate methods

```@docs
Logarithmic
LogarithmicEmbedding
LogarithmicIndependentBranching
Incremental
NativeSOS2
ZigZagBinary
ZigZagInteger
```

## Bivariate methods

```@docs
K1
SixStencil
NineStencil
UnionJack
```

## Multivariate methods

```@docs
ConvexCombination
DisaggregatedLogarithmic
MultipleChoice
```

## Internal types

```@docs
PiecewiseLinearOpt.SegmentPointRep
PiecewiseLinearOpt.SegmentHyperplaneRep
PiecewiseLinearOpt.AffineFunction
```
Loading