Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[breaking] Rewrite the C API using Clang.jl #128

Merged
merged 7 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- 'src/gen'
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ['1.0', '1'] # Test against LTS and current minor release
version: ['1']
os: [ubuntu-latest, macOS-latest, windows-latest]
arch: [x64]
include:
# Also test against 32-bit Linux on LTS.
- version: '1.0'
- version: '1'
os: ubuntu-latest
arch: x86
steps:
Expand Down
17 changes: 7 additions & 10 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
name = "ECOS"
uuid = "e2685f51-7e38-5353-a97d-a921fd2c8199"
repo = "https://github.com/jump-dev/ECOS.jl.git"
version = "0.13.0"
version = "0.14.0"

[deps]
BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
ECOS_jll = "c2c64177-6a8e-5dca-99a7-64895ad7445f"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
BinaryProvider = "0.5"
ECOS_jll = "=2.0.5"
CEnum = "0.3, 0.4"
ECOS_jll = "=2.0.8"
MathOptInterface = "0.10.3"
julia = "1"
julia = "1.6"

[extras]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Compat", "Test"]
test = ["SparseArrays", "Test"]
85 changes: 31 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
# ECOS.jl

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

Julia wrapper for the [ECOS](https://github.com/embotech/ecos) embeddable conic
optimization interior point solver.

[![Build Status](https://github.com/jump-dev/ECOS.jl/workflows/CI/badge.svg?branch=master)](https://github.com/jump-dev/ECOS.jl/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/jump-dev/ECOS.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/jump-dev/ECOS.jl)
The wrapper has two components:
* a thin wrapper around the complete C API
* an iterface to [MathOptInterface](https://github.com/jump-dev/MathOptInterface.jl)

## Installation

You can install ECOS.jl through the Julia package manager:
Install ECOS.jl using `Pkg.add`:
```julia
julia> Pkg.add("ECOS")
import Pkg; Pkg.add("ECOS")
```

ECOS.jl will automatically install and setup the ECOS solver itself using [BinaryProvider.jl](https://github.com/JuliaPackaging/BinaryProvider.jl).
In addition to installing the ECOS.jl package, this will also download and
install the ECOS binaries. (You do not need to install ECOS separately.) If you
require a custom build of ECOS, see the **Custom Installation** instructions
below.

## Custom Installation
### License

After ECOS.jl is installed and built, you can replace the installed `libecos` dependency with a custom installation by following the [Pkg documentation for overriding artifacts](https://julialang.github.io/Pkg.jl/v1/artifacts/#Overriding-artifact-locations-1). Note that your custom `libecos` is required to be at least version 2.0.5.
`ECOS.jl` is licensed under the MIT License (see LICENSE.md), but note that ECOS
itself is GPL v3.

## Usage
## Use with JuMP

The ECOS interface is completely wrapped. ECOS functions corresponding to the C API are available as `ECOS.setup`, `ECOS.solve`, `ECOS.cleanup`, and `ECOS.ver` (these are not exported from the module). Function arguments are extensively documented in the source, and an example of usage can be found in `test/direct.jl`.
TO use ECOS with [JuMP](https://github.com/jump-dev/JuMP.jl), use
`ECOS.Optimizer`:
```julia
using JuMP, ECOS
model = Model(ECOS.Optimizer)
set_optimizer_attribute(model, "maxit", 100)
```

ECOS.jl also supports the **[MathOptInterface](https://github.com/jump-dev/MathOptInterface.jl)** standard solver interface.
Thanks to this support ECOS can be used as a solver with both the **[JuMP]** and **[Convex.jl]** modeling languages.
## Options

All ECOS solver options can be set through the direct interface and through MathOptInterface.
The list of options is defined the [`ecos.h` header](https://github.com/embotech/ecos/blob/master/include/ecos.h), which we reproduce here:
The list of options is defined the [`ecos.h` header](https://github.com/embotech/ecos/blob/master/include/ecos.h),
which we reproduce here:
```julia
gamma # scaling the final step length
delta # regularization parameter
Expand All @@ -42,46 +55,10 @@ nitref # number of iterative refinement steps
maxit # maximum number of iterations
verbose # verbosity bool for PRINTLEVEL < 3
```
To use these settings you can either pass them as keyword arguments to `setup`
(direct interface) or as arguments to the `ECOS.Optimizer` constructor
(MathOptInterface interface), e.g.
```julia
# Direct
my_prob = ECOS.setup(n, m, ..., c, h, b; maxit=10, feastol=1e-5)
# MathOptInterface (with JuMP)
model = Model(with_optimizer(ECOS.Optimizer, maxit=10, feastol=1e-5))
```

### JuMP example

This example shows how we can model a simple knapsack problem with JuMP and use ECOS to solve it.

```julia
using JuMP
using ECOS

items = [:Gold, :Silver, :Bronze]
values = Dict(:Gold => 5.0, :Silver => 3.0, :Bronze => 1.0)
weight = Dict(:Gold => 2.0, :Silver => 1.5, :Bronze => 0.3)

model = Model(with_optimizer(ECOS.Optimizer))
@variable(model, 0 <= take[items] <= 1) # Define a variable for each item
@objective(model, Max, sum(values[item] * take[item] for item in items))
@constraint(model, sum(weight[item] * take[item] for item in items) <= 3)
optimize!(model)

println(value.(take))
# take
# [ Gold] = 0.9999999680446406
# [Silver] = 0.46666670881026834
# [Bronze] = 0.9999999633898735
```

---

`ECOS.jl` is licensed under the MIT License (see LICENSE.md), but note that ECOS itself is GPL v3.
## Custom Installation

[MathProgBase]: https://github.com/JuliaOpt/MathProgBase.jl
[JuMP]: https://github.com/jump-dev/JuMP.jl
[Convex.jl]: https://github.com/JuliaOpt/Convex.jl
[Homebrew.jl]: https://github.com/JuliaLang/Homebrew.jl
After ECOS.jl is installed and built, you can replace the installed `libecos`
dependency with a custom installation by following the
[Pkg documentation for overriding artifacts](https://julialang.github.io/Pkg.jl/v1/artifacts/#Overriding-artifact-locations-1).
Note that your custom `libecos` is required to be version 2.0.8.
5 changes: 0 additions & 5 deletions deps/.gitignore

This file was deleted.

99 changes: 0 additions & 99 deletions deps/build.jl

This file was deleted.

6 changes: 6 additions & 0 deletions gen/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[deps]
Clang = "40e3b903-d033-50b4-a0cc-940c62c95e31"
ECOS_jll = "c2c64177-6a8e-5dca-99a7-64895ad7445f"

[compat]
Clang = "0.13"
44 changes: 44 additions & 0 deletions gen/gen.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Clang, ECOS_jll

_GEN_DIR = joinpath(dirname(@__DIR__), "src", "gen")
_COMMON = joinpath(_GEN_DIR, "libecos_common.jl")

function _get_headers(include_path)
return map(
f -> joinpath(include_path, f),
filter(f -> endswith(f, ".h") && f != "timer.h", readdir(include_path)),
)
end

wc = Clang.init(
headers = _get_headers(joinpath(ECOS_jll.artifact_dir, "include")),
output_file = joinpath(_GEN_DIR, "libecos_api.jl"),
common_file = _COMMON,
header_wrapped = (root, current) -> root == current,
header_library = x -> "ecos",
clang_args = String["-I" * header for header in Clang.find_std_headers()],
clang_diagnostics = true,
)

run(wc)

function manual_replacements()
s = read(_COMMON, String)
for (old, new) in [
"DBL_MAX + DBL_MAX" => "Inf",
"ECOS_INFINITY - ECOS_INFINITY" => "NaN",
"const PRINTTEXT = printf" => "# const PRINTTEXT = printf",
"const MALLOC = malloc" => "# const MALLOC = malloc",
"const FREE = free" => "# const FREE = free",
"const idxint = Cint" => "const idxint = Int64",
]
s = replace(s, old => new)
end
write(_COMMON, s)
return
end

manual_replacements()

rm(joinpath(_GEN_DIR, "LibTemplate.jl"))
rm(joinpath(_GEN_DIR, "ctypes.jl"))
Loading