Skip to content

Update Dependencies, fix tests and add Aqua.jl tests #44

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
matrix:
version:
- '1'
- '1.6'
- '1.10'
os:
- ubuntu-latest
- macOS-latest
Expand Down
23 changes: 15 additions & 8 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
name = "StructuredOptimization"
uuid = "46cd3e9d-64ff-517d-a929-236bc1a1fc9d"
version = "0.4.0"
version = "0.5.0"

[deps]
AbstractOperators = "d9c5613a-d543-52d8-9afd-8f241a8c3f1c"
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
ProximalAlgorithms = "140ffc9f-1907-541a-a177-7475e0a401e9"
ProximalOperators = "a725b495-10eb-56fe-b38b-717eba820537"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"

[compat]
AbstractOperators = "0.3"
DSP = "0.5.1 - 0.7"
AbstractOperators = "0.4"
Aqua = "0.8"
DSP = "0.5.1 - 0.8"
DifferentiationInterface = "0.6"
FFTW = "1"
ProximalAlgorithms = "0.5"
ProximalOperators = "0.15"
RecursiveArrayTools = "1 - 2"
julia = "1.4"
LinearAlgebra = "1"
ProximalAlgorithms = "0.7"
ProximalOperators = "0.16"
Random = "1"
RecursiveArrayTools = "1 - 3"
Test = "1"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["LinearAlgebra", "Test", "Random"]
test = ["LinearAlgebra", "Test", "Random", "Aqua"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![Build status](https://github.com/JuliaFirstOrder/StructuredOptimization.jl/workflows/CI/badge.svg)](https://github.com/JuliaFirstOrder/StructuredOptimization.jl/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/JuliaFirstOrder/StructuredOptimization.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaFirstOrder/StructuredOptimization.jl)
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliafirstorder.github.io/StructuredOptimization.jl/stable)
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://juliafirstorder.github.io/StructuredOptimization.jl/latest)
Expand Down
5 changes: 5 additions & 0 deletions src/StructuredOptimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ using ProximalAlgorithms
import ProximalAlgorithms: ZeroFPR, PANOC, PANOCplus
export ZeroFPR, PANOC, PANOCplus

ProximalAlgorithms.value_and_gradient(f, x) = begin
y, fy = gradient(f, x)
return fy, y
end

include("syntax/syntax.jl")
include("calculus/precomposeNonlinear.jl") # TODO move to ProximalOperators?
include("arraypartition.jl") # TODO move to ProximalOperators?
Expand Down
50 changes: 24 additions & 26 deletions src/solvers/build_solve.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export build

"""
parse_problem(terms::Tuple, solver::ForwardBackwardSolver)
parse_problem(terms::Tuple, solver::ForwardBackwardSolver)

Takes as input a tuple containing the terms defining the problem and the solver.

Expand All @@ -22,34 +20,34 @@ julia> StructuredOptimization.parse_problem(p, PANOCplus());
```
"""
function parse_problem(terms::Tuple, solver::T) where T <: ForwardBackwardSolver
x = extract_variables(terms)
# Separate smooth and nonsmooth
smooth, nonsmooth = split_smooth(terms)
if is_proximable(nonsmooth)
g = extract_proximable(x, nonsmooth)
x = extract_variables(terms)
# Separate smooth and nonsmooth
smooth, nonsmooth = split_smooth(terms)
if is_proximable(nonsmooth)
g = extract_proximable(x, nonsmooth)
kwargs = Dict{Symbol, Any}(:g => g)
if !isempty(smooth)
if is_linear(smooth)
f = extract_functions(smooth)
A = extract_operators(x, smooth)
kwargs[:A] = A
else # ??
f = extract_functions_nodisp(smooth)
A = extract_affines(x, smooth)
f = PrecomposeNonlinear(f, A)
end
kwargs[:f] = f
end
return (x, kwargs)
end
if !isempty(smooth)
if is_linear(smooth)
f = extract_functions(smooth)
A = extract_operators(x, smooth)
kwargs[:A] = A
else # ??
f = extract_functions_nodisp(smooth)
A = extract_affines(x, smooth)
f = PrecomposeNonlinear(f, A)
end
kwargs[:f] = f
end
return (x, kwargs)
end
error("Sorry, I cannot parse this problem for solver of type $(T)")
end


export solve

"""
solve(terms::Tuple, solver::ForwardBackwardSolver)
solve(terms::Tuple, solver::ForwardBackwardSolver)

Takes as input a tuple containing the terms defining the problem and the solver options.

Expand All @@ -71,8 +69,8 @@ julia> ~x
```
"""
function solve(terms::Tuple, solver::ForwardBackwardSolver)
x, kwargs = parse_problem(terms, solver)
x, kwargs = parse_problem(terms, solver)
x_star, it = solver(; x0 = ~x, kwargs...)
~x .= x_star
return x, it
~x .= x_star
return x, it
end
113 changes: 49 additions & 64 deletions src/syntax/expressions/addition.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Base: +, -

"""
+(ex1::AbstractExpression, ex2::AbstractExpression)
+(ex1::AbstractExpression, ex2::AbstractExpression)

Add two expressions.

Expand Down Expand Up @@ -47,112 +47,97 @@ julia> ex3.+z
function (+)(a::AbstractExpression, b::AbstractExpression)
A = convert(Expression,a)
B = convert(Expression,b)
if variables(A) == variables(B)
if variables(A) == variables(B)
return Expression{length(A.x)}(A.x,affine(A)+affine(B))
else
opA = affine(A)
xA = variables(A)
opB = affine(B)
xB = variables(B)
else
opA = affine(A)
xA = variables(A)
opB = affine(B)
xB = variables(B)
xNew, opNew = Usum_op(xA,xB,opA,opB,true)
return Expression{length(xNew)}(xNew,opNew)
end
end
end
# sum expressions

function (-)(a::AbstractExpression, b::AbstractExpression)
A = convert(Expression,a)
B = convert(Expression,b)
if variables(A) == variables(B)
if variables(A) == variables(B)
return Expression{length(A.x)}(A.x,affine(A)-affine(B))
else
opA = affine(A)
xA = variables(A)
opB = affine(B)
xB = variables(B)
else
opA = affine(A)
xA = variables(A)
opB = affine(B)
xB = variables(B)
xNew, opNew = Usum_op(xA,xB,opA,opB,false)
return Expression{length(xNew)}(xNew,opNew)
end
end
end

#unsigned sum affines with single variables
function Usum_op(xA::Tuple{Variable},
xB::Tuple{Variable},
A::AbstractOperator,
B::AbstractOperator,sign::Bool)
function Usum_op(xA::Tuple{Variable}, xB::Tuple{Variable}, A::AbstractOperator, B::AbstractOperator, sign::Bool)
xNew = (xA...,xB...)
opNew = sign ? hcat(A,B) : hcat(A,-B)
return xNew, opNew
return xNew, opNew
end

#unsigned sum: HCAT + AbstractOperator
function Usum_op(xA::NTuple{N,Variable},
xB::Tuple{Variable},
A::L1,
B::AbstractOperator,sign::Bool) where {N, M, L1<:HCAT{N}}
if xB[1] in xA
function Usum_op(xA::NTuple{N,Variable}, xB::Tuple{Variable}, A::HCAT{N}, B::AbstractOperator, sign::Bool) where {N}
if xB[1] in xA
idx = findfirst(xA.==Ref(xB[1]))
S = sign ? A[idx]+B : A[idx]-B
xNew = xA
xNew = xA
opNew = hcat(A[1:idx-1],S,A[idx+1:N] )
else
else
xNew = (xA...,xB...)
opNew = sign ? hcat(A,B) : hcat(A,-B)
end
return xNew, opNew
end
return xNew, opNew
end

#unsigned sum: AbstractOperator+HCAT
function Usum_op(xA::Tuple{Variable},
xB::NTuple{N,Variable},
A::AbstractOperator,
B::L2,sign::Bool) where {N, M, L2<:HCAT{N}}
if xA[1] in xB
function Usum_op(xA::Tuple{Variable}, xB::NTuple{N,Variable}, A::AbstractOperator, B::HCAT{N}, sign::Bool) where {N}
if xA[1] in xB
idx = findfirst(xA.==Ref(xB[1]))
S = sign ? A+B[idx] : B[idx]-A
xNew = xB
xNew = xB
opNew = sign ? hcat(B[1:idx-1],S,B[idx+1:N] ) : -hcat(B[1:idx-1],S,B[idx+1:N] )
else
else
xNew = (xA...,xB...)
opNew = sign ? hcat(A,B) : hcat(A,-B)
end
end

return xNew, opNew
return xNew, opNew
end

#unsigned sum: HCAT+HCAT
function Usum_op(xA::NTuple{NA,Variable},
xB::NTuple{NB,Variable},
A::L1,
B::L2,sign::Bool) where {NA,NB,M,
L1<:HCAT{NB},
L2<:HCAT{NB} }
xNew = xA
opNew = A
for i in eachindex(xB)
xNew, opNew = Usum_op(xNew, (xB[i],), opNew, B[i], sign)
end
function Usum_op(xA::NTuple{NA,Variable}, xB::NTuple{NB,Variable}, A::HCAT{NB}, B::HCAT{NB}, sign::Bool) where {NA,NB}
xNew = xA
opNew = A
for i in eachindex(xB)
xNew, opNew = Usum_op(xNew, (xB[i],), opNew, B[i], sign)
end
return xNew,opNew
end

#unsigned sum: multivar AbstractOperator + AbstractOperator
function Usum_op(xA::NTuple{N,Variable},
xB::Tuple{Variable},
A::AbstractOperator,
B::AbstractOperator,sign::Bool) where {N}
if xB[1] in xA
Z = Zeros(A) #this will be an HCAT
function Usum_op(
xA::NTuple{N,Variable}, xB::Tuple{Variable}, A::AbstractOperator, B::AbstractOperator, sign::Bool
) where {N}
if xB[1] in xA
Z = Zeros(A) #this will be an HCAT
xNew, opNew = Usum_op(xA,xB,Z,B,sign)
opNew += A
else
opNew += A
else
xNew = (xA...,xB...)
opNew = sign ? hcat(A,B) : hcat(A,-B)
end
return xNew, opNew
end
return xNew, opNew
end

"""
+(ex::AbstractExpression, b::Union{AbstractArray,Number})
+(ex::AbstractExpression, b::Union{AbstractArray,Number})

Add a scalar or an `Array` to an expression:

Expand Down Expand Up @@ -213,9 +198,9 @@ function Broadcast.broadcasted(::typeof(+),a::AbstractExpression, b::AbstractExp
elseif prod(size(affine(B),1)) > prod(size(affine(A),1))
A = Expression{length(A.x)}(variables(A),
BroadCast(affine(A),size(affine(B),1)))
end
end
return A+B
end
end
return A+B
end

Expand All @@ -229,8 +214,8 @@ function Broadcast.broadcasted(::typeof(-),a::AbstractExpression, b::AbstractExp
elseif prod(size(affine(B),1)) > prod(size(affine(A),1))
A = Expression{length(A.x)}(variables(A),
BroadCast(affine(A),size(affine(B),1)))
end
end
return A-B
end
end
return A-B
end
10 changes: 5 additions & 5 deletions src/syntax/variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ Returns a `Variable` of dimension `dims` initialized with an array of all zeros.
Returns a `Variable` of dimension `size(x)` initialized with `x`

"""
function Variable(T::Type, args::Vararg{I,N}) where {I <: Integer,N}
Variable{T,N,Array{T,N}}(zeros(T, args...))
function Variable(T::Type, args::Int...)
N = length(args)
Variable{T,N,Array{T,N}}(zeros(T, args...))
end

function Variable(args::Vararg{I}) where {I <: Integer}
function Variable(args::Int...)
Variable(zeros(args...))
end

Expand All @@ -30,7 +31,6 @@ function Base.show(io::IO, x::Variable)
print(io, "Variable($(eltype(x.x)), $(size(x.x)))")
end


"""
~(x::Variable)

Expand All @@ -46,7 +46,7 @@ size(x::Variable, [dim...])
Like `size(A::AbstractArray, [dims...])` returns the tuple containing the dimensions of the variable `x`.
"""
size(x::Variable) = size(x.x)
size(x::Variable, dim::I) where { I <: Integer} = size(x.x, dim)
size(x::Variable, dim::Integer) = size(x.x, dim)

"""
eltype(x::Variable)
Expand Down
Loading