diff --git a/.travis.yml b/.travis.yml index 14cdf55..b254b7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,27 @@ -# Documentation: http://docs.travis-ci.com/user/languages/julia/ language: julia +sudo: required + +services: + - docker + os: - linux - osx julia: - - 0.5 - - 0.6 + - 0.7 + - 1.0 - nightly -matrix: - allow_failures: - - julia: 0.5 - +# matrix: +# allow_failures: +# - julia: nightly notifications: email: false -# uncomment the following lines to override the default test script -#script: -# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi -# - julia -e 'Pkg.clone(pwd()); Pkg.build("ConstraintPropagation"); Pkg.test("ConstraintPropagation"; coverage=true)' - after_success: - - julia -e 'Pkg.add("Documenter")' + - julia -e 'using Pkg; Pkg.add("Documenter")' - julia -e 'cd(Pkg.dir("IntervalConstraintProgramming")); include(joinpath("docs", "make.jl"))' + - julia -e 'cd(Pkg.dir("IntervalConstraintProgramming")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder()); Codecov.submit(process_folder())' diff --git a/REQUIRE b/REQUIRE index e2862c3..5342829 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,6 +1,5 @@ -julia 0.6 0.7 -IntervalArithmetic 0.9 0.14 -IntervalRootFinding 0.2 0.3 -IntervalContractors 0.2 0.3 -MacroTools 0.3 - +julia 0.7 +IntervalArithmetic 0.15 +IntervalRootFinding 0.4 +IntervalContractors 0.3 +MacroTools 0.4 diff --git a/appveyor.yml b/appveyor.yml index 92e8dd5..c2588f1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,18 @@ environment: matrix: - - JULIAVERSION: "julialang/bin/winnt/x86/0.4/julia-0.4-latest-win32.exe" - - JULIAVERSION: "julialang/bin/winnt/x64/0.4/julia-0.4-latest-win64.exe" - - JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe" - - JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe" + - julia_version: 0.7 + - julia_version: 1 + - julia_version: nightly + +platform: + - x86 # 32-bit + - x64 # 64-bit + +# # Uncomment the following lines to allow failures on nightly julia +# # (tests will run but not make your overall status red) +# matrix: +# allow_failures: +# - julia_version: nightly branches: only: @@ -17,18 +26,18 @@ notifications: on_build_status_changed: false install: -# Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $("http://s3.amazonaws.com/"+$env:JULIAVERSION), - "C:\projects\julia-binary.exe") -# Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia + - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) build_script: -# Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"ConstraintPropagation\"); Pkg.build(\"ConstraintPropagation\")" + - echo "%JL_BUILD_SCRIPT%" + - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" test_script: - - C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"ConstraintPropagation\")" + - echo "%JL_TEST_SCRIPT%" + - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" + +# # Uncomment to support code coverage upload. Should only be enabled for packages +# # which would have coverage gaps without running on Windows +# on_success: +# - echo "%JL_CODECOV_SCRIPT%" +# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" diff --git a/src/ast.jl b/src/ast.jl index 1c842a3..38114fa 100644 --- a/src/ast.jl +++ b/src/ast.jl @@ -1,7 +1,6 @@ const symbol_numbers = Dict{Symbol, Int}() - -doc"""Return a new, unique symbol like _z3_""" +"""Return a new, unique symbol like _z3_""" function make_symbol(s::Symbol) # default is :z i = get(symbol_numbers, s, 0) @@ -17,7 +16,7 @@ end make_symbol(c::Char) = make_symbol(Symbol(c)) let current_symbol = 'a' - function make_symbol() + global function make_symbol() current_sym = current_symbol if current_sym < 'z' @@ -36,7 +35,7 @@ function make_symbols(n::Integer) end # The following function is not used -doc"""Check if a symbol like `:a` has been uniqued to `:_a_1_`""" +"""Check if a symbol like `:a` has been uniqued to `:_a_1_`""" function isuniqued(s::Symbol) ss = string(s) contains(ss, "_") && isdigit(ss[end-1]) @@ -46,13 +45,13 @@ end # Combine Assignment and FunctionAssignment ? -immutable Assignment +struct Assignment lhs op args end -immutable FunctionAssignment +struct FunctionAssignment f # function name args # input arguments return_arguments @@ -60,7 +59,7 @@ immutable FunctionAssignment end # Close to single assignment form -type FlatAST +mutable struct FlatAST top # topmost variable(s) input_variables::Set{Symbol} intermediate::Vector{Symbol} # generated vars @@ -103,7 +102,7 @@ function flatten(ex) end -doc"""`flatten!` recursively converts a Julia expression into a "flat" (one-dimensional) +"""`flatten!` recursively converts a Julia expression into a "flat" (one-dimensional) structure, stored in a FlatAST object. This is close to SSA (single-assignment form, https://en.wikipedia.org/wiki/Static_single_assignment_form). diff --git a/src/code_generation.jl b/src/code_generation.jl index e4a9474..ffeac04 100644 --- a/src/code_generation.jl +++ b/src/code_generation.jl @@ -1,7 +1,7 @@ -doc""" +""" A generated function, with the code that generated it """ -immutable GeneratedFunction{F} +struct GeneratedFunction{F} f::F code::Expr end @@ -10,7 +10,7 @@ end # GeneratedFunction(code::Expr) = GeneratedFunction(eval(code), code) -(f::GeneratedFunction{F}){F}(x...) = f.f(x...) +(f::GeneratedFunction{F})(x...) where {F} = f.f(x...) function make_tuple(args) @@ -179,7 +179,7 @@ function forward_backward(flatAST::FlatAST) end -doc""" +""" Generate code for an anonymous function with given input arguments, output arguments, and code block. """ diff --git a/src/contractor.jl b/src/contractor.jl index 0dd443a..75e9306 100644 --- a/src/contractor.jl +++ b/src/contractor.jl @@ -1,9 +1,9 @@ -doc""" -`Contractor` represents a `Contractor` from $\mathbb{R}^N$ to $\mathbb{R}^N$. +""" +`Contractor` represents a `Contractor` from ``\\mathbb{R}^N`` to ``\\mathbb{R}^N``. Nout is the output dimension of the forward part. """ -immutable Contractor{N, Nout, F1<:Function, F2<:Function} +struct Contractor{N, Nout, F1<:Function, F2<:Function} variables::Vector{Symbol} # input variables forward::GeneratedFunction{F1} backward::GeneratedFunction{F2} @@ -32,7 +32,7 @@ function Contractor(variables::Vector{Symbol}, top, forward, backward, expressio Contractor{N, Nout, typeof(forward.f), typeof(backward.f)}(variables, forward, backward, expression) end -function Base.show{N,Nout,F1,F2}(io::IO, C::Contractor{N,Nout,F1,F2}) +function Base.show(io::IO, C::Contractor{N,Nout,F1,F2}) where {N,Nout,F1,F2} println(io, "Contractor in $(N) dimensions:") println(io, " - forward pass contracts to $(Nout) dimensions") println(io, " - variables: $(C.variables)") @@ -41,8 +41,8 @@ end -function (C::Contractor{N,Nout,F1,F2}){N,Nout,F1,F2,T}( - A::IntervalBox{Nout,T}, X::IntervalBox{N,T}) +function (C::Contractor{N,Nout,F1,F2})( + A::IntervalBox{Nout,T}, X::IntervalBox{N,T}) where {N,Nout,F1,F2,T} output, intermediate = C.forward(X) @@ -67,7 +67,8 @@ function (C::Contractor{N,Nout,F1,F2}){N,Nout,F1,F2,T}( end # allow 1D contractors to take Interval instead of IntervalBox for simplicty: -(C::Contractor{N,1,F1,F2}){N,F1,F2,T}(A::Interval{T}, X::IntervalBox{N,T}) = C(IntervalBox(A), X) + +(C::Contractor{N,1,F1,F2})(A::Interval{T}, X::IntervalBox{N,T}) where {N,F1,F2,T} = C(IntervalBox(A), X) function make_contractor(expr::Expr) # println("Entering Contractor(ex) with ex=$ex") @@ -109,7 +110,7 @@ function make_contractor(expr::Expr) end -doc"""Usage: +"""Usage: ``` C = @contractor(x^2 + y^2) A = -∞..1 # the constraint interval diff --git a/src/functions.jl b/src/functions.jl index b034dac..9b88658 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -1,10 +1,10 @@ -doc""" +""" A `ConstraintFunction` contains the created forward and backward code """ -type ConstraintFunction{F <: Function, G <: Function} +mutable struct ConstraintFunction{F <: Function, G <: Function} input::Vector{Symbol} # input arguments for forward function output::Vector{Symbol} # output arguments for forward function forward::F @@ -14,7 +14,7 @@ type ConstraintFunction{F <: Function, G <: Function} expression::Expr end -function Base.show{F,G}(io::IO, f::ConstraintFunction{F,G}) +function Base.show(io::IO, f::ConstraintFunction{F,G}) where {F,G} println(io, "ConstraintFunction:") println(io, " - input arguments: $(f.input)") println(io, " - output arguments: $(f.output)") @@ -22,7 +22,7 @@ function Base.show{F,G}(io::IO, f::ConstraintFunction{F,G}) end -immutable FunctionArguments +struct FunctionArguments input return_arguments intermediate @@ -33,12 +33,11 @@ end const registered_functions = Dict{Symbol, FunctionArguments}() -@doc """ -`@function` registers a function to be used in forwards and backwards mode. - -Example: `@function f(x, y) = x^2 + y^2` -""" # this docstring does not work! - +# """ +# `@function` registers a function to be used in forwards and backwards mode. +# +# Example: `@function f(x, y) = x^2 + y^2` +# """ # this docstring does not work! @eval macro ($(:function))(ex) # workaround to define macro @function (f, args, code) = match_function(ex) diff --git a/src/paving.jl b/src/paving.jl index 34970c3..f70ef5a 100644 --- a/src/paving.jl +++ b/src/paving.jl @@ -1,6 +1,6 @@ -typealias SubPaving{N,T} Vector{IntervalBox{N,T}} +const SubPaving{N,T} = Vector{IntervalBox{N,T}} -type Paving{N,T} +struct Paving{N,T} separator::Separator # parametrize! inner::SubPaving{N,T} boundary::SubPaving{N,T} @@ -8,7 +8,7 @@ type Paving{N,T} end -function setdiff{N,T}(x::IntervalBox{N,T}, subpaving::SubPaving{N,T}) +function setdiff(x::IntervalBox{N,T}, subpaving::SubPaving{N,T}) where {N,T} working = [x] new_working = IntervalBox{N,T}[] @@ -37,16 +37,16 @@ function setdiff{N,T}(x::IntervalBox{N,T}, subpaving::SubPaving{N,T}) end -setdiff{N,T}(X::SubPaving{N,T}, Y::SubPaving{N,T}) = vcat([setdiff(x, Y) for x in X]...) +setdiff(X::SubPaving{N,T}, Y::SubPaving{N,T}) where {N,T} = vcat([setdiff(x, Y) for x in X]...) -function setdiff{N,T}(x::IntervalBox{N,T}, paving::Paving{N,T}) +function setdiff(x::IntervalBox{N,T}, paving::Paving{N,T}) where {N,T} Y = setdiff(x, paving.inner) Z = setdiff(Y, paving.boundary) return Z end -function show{N,T}(io::IO, p::Paving{N,T}) +function show(io::IO, p::Paving{N,T}) where {N,T} print(io, """Paving: - tolerance ϵ = $(p.ϵ) - inner approx. of length $(length(p.inner)) diff --git a/src/separator.jl b/src/separator.jl index ffedba5..08a3b27 100644 --- a/src/separator.jl +++ b/src/separator.jl @@ -1,11 +1,11 @@ -abstract Separator +abstract type Separator end -doc""" +""" ConstraintSeparator is a separator that represents a constraint defined directly using `@constraint`. """ -immutable ConstraintSeparator{C, II} <: Separator +struct ConstraintSeparator{C, II} <: Separator variables::Vector{Symbol} constraint::II # Interval or IntervalBox contractor::C @@ -14,10 +14,10 @@ end ConstraintSeparator(constraint, contractor, expression) = ConstraintSeparator(contractor.variables, constraint, contractor, expression) -doc"""CombinationSeparator is a separator that is a combination (union, intersection, +"""CombinationSeparator is a separator that is a combination (union, intersection, or complement) of other separators. """ -immutable CombinationSeparator{F} <: Separator +struct CombinationSeparator{F} <: Separator variables::Vector{Symbol} separator::F expression::Expr @@ -50,7 +50,7 @@ end -doc"""`parse_comparison` parses comparisons like `x >= 10` +"""`parse_comparison` parses comparisons like `x >= 10` into the corresponding interval, expressed as `x ∈ [10,∞]` Returns the expression and the constraint interval @@ -122,28 +122,27 @@ function make_constraint(expr, constraint) full_expr = Meta.quot(:($expr ∈ $constraint)) + contractor_code = make_contractor(expr) + code = quote end - push!(code.args, :($(esc(contractor_name)) = @contractor($(esc(expr))))) - # push!(code.args, :(ConstraintSeparator($(esc(contractor_name)).variables[2:end], $constraint, $(esc(contractor_name)), $full_expr))) + push!(code.args, :($(esc(contractor_name)) = $(contractor_code))) push!(code.args, :(ConstraintSeparator($constraint, $(esc(contractor_name)), $full_expr))) - # @show code - code end -doc"""Create a separator from a given constraint expression, written as +"""Create a separator from a given constraint expression, written as standard Julia code. e.g. `C = @constraint x^2 + y^2 <= 1` The variables (`x` and `y`, in this case) are automatically inferred. -External constants can be used as e.g. `$a`: +External constants can be used as e.g. `\$a`: ``` a = 3 -C = @constraint x^2 + y^2 <= $a +C = @constraint x^2 + y^2 <= \$a ``` """ macro constraint(ex::Expr) @@ -165,7 +164,7 @@ end (S::CombinationSeparator)(X) = S.separator(X) -doc"Unify the variables of two separators" +"Unify the variables of two separators" function unify_variables(vars1, vars2) variables = unique(sort(vcat(vars1, vars2))) @@ -192,7 +191,7 @@ end # TODO: when S1 and S2 have different variables -- amalgamate! -doc""" +""" ∩(S1::Separator, S2::Separator) Separator for the intersection of two sets given by the separators `S1` and `S2`. diff --git a/src/setinversion.jl b/src/setinversion.jl index 9de3a4e..6392cec 100644 --- a/src/setinversion.jl +++ b/src/setinversion.jl @@ -1,9 +1,9 @@ -doc""" +""" `pave` takes the given working list of boxes and splits them into inner and boundary lists with the given separator """ -function pave{N,T}(S::Separator, working::Vector{IntervalBox{N,T}}, ϵ) +function pave(S::Separator, working::Vector{IntervalBox{N,T}}, ϵ, bisection_point=nothing) where {N,T} inner_list = SubPaving{N,T}() boundary_list = SubPaving{N,T}() @@ -33,7 +33,12 @@ function pave{N,T}(S::Separator, working::Vector{IntervalBox{N,T}}, ϵ) push!(boundary_list, boundary) else - push!(working, bisect(boundary)...) + if bisection_point == nothing + push!(working, bisect(boundary)...) + else + push!(working, bisect(boundary, bisection_point)...) + end + end end @@ -43,30 +48,30 @@ function pave{N,T}(S::Separator, working::Vector{IntervalBox{N,T}}, ϵ) end -doc""" +""" pave(S::Separator, domain::IntervalBox, eps)` Find the subset of `domain` defined by the constraints specified by the separator `S`. Returns (sub)pavings `inner` and `boundary`, i.e. lists of `IntervalBox`. """ -function pave{N,T}(S::Separator, X::IntervalBox{N,T}, ϵ = 1e-2) +function pave(S::Separator, X::IntervalBox{N,T}, ϵ = 1e-2, bisection_point=nothing) where {N,T} - inner_list, boundary_list = pave(S, [X], ϵ) + inner_list, boundary_list = pave(S, [X], ϵ, bisection_point) return Paving(S, inner_list, boundary_list, ϵ) end - -doc"""Refine a paving to tolerance ϵ""" -function refine!(P::Paving, ϵ = 1e-2) - if P.ϵ <= ϵ # already refined - return - end - - new_inner, new_boundary = pave(P.separator, P.boundary, ϵ) - - append!(P.inner, new_inner) - P.boundary = new_boundary - P.ϵ = ϵ -end +# +# """Refine a paving to tolerance ϵ""" +# function refine!(P::Paving, ϵ = 1e-2) +# if P.ϵ <= ϵ # already refined +# return +# end +# +# new_inner, new_boundary = pave(P.separator, P.boundary, ϵ) +# +# append!(P.inner, new_inner) +# P.boundary = new_boundary +# P.ϵ = ϵ +# end diff --git a/src/volume.jl b/src/volume.jl index 9f0ca1c..6d45ea3 100644 --- a/src/volume.jl +++ b/src/volume.jl @@ -1,30 +1,30 @@ import Base: +, show, * -doc"""N-dimensional Volume with lower and upper bounds""" -immutable Vol{N,T} +"""N-dimensional Volume with lower and upper bounds""" +struct Vol{N,T} bounds::Interval{T} end Vol_name(i::Integer) = i == 1 ? "length" : i == 2 ? "area" : i == 3 ? "Volume" : "hyper-Volume" -show{N,T}(io::IO, v::Vol{N,T}) = print(io, "$N-dimensional $(Vol_name(N)): $(v.bounds)") +show(io::IO, v::Vol{N,T}) where {N,T} = print(io, "$N-dimensional $(Vol_name(N)): $(v.bounds)") -Vol{N,T}(X::IntervalBox{N,T}) = Vol{N,T}(prod([Interval(x.hi) - Interval(x.lo) for x in X])) +Vol(X::IntervalBox{N,T}) where {N,T} = Vol{N,T}(prod([Interval(x.hi) - Interval(x.lo) for x in X])) -Vol{T}(x::Interval{T}) = Vol(IntervalBox(x)) +Vol(x::Interval{T}) where {T} = Vol(IntervalBox(x)) -+{N,T}(v::Vol{N,T}, w::Vol{N,T}) = Vol{N,T}(v.bounds + w.bounds) ++(v::Vol{N,T}, w::Vol{N,T}) where {N,T} = Vol{N,T}(v.bounds + w.bounds) -*{N1,N2,T}(v::Vol{N1,T}, w::Vol{N2,T}) = Vol{N1+N2,T}(v.bounds * w.bounds) +*(v::Vol{N1,T}, w::Vol{N2,T}) where {N1,N2,T} = Vol{N1+N2,T}(v.bounds * w.bounds) -Vol{N,T}(XX::SubPaving{N,T}) = sum([Vol(X) for X in XX]) +Vol(XX::SubPaving{N,T}) where {N,T} = sum([Vol(X) for X in XX]) -function Vol{N,T}(P::Paving{N,T}) +function Vol(P::Paving{N,T}) where {N,T} Vin = Vol(P.inner) Vout = Vin + Vol(P.boundary) diff --git a/test/runtests.jl b/test/runtests.jl index 4a9208d..e1f979a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,11 +1,10 @@ -using Base.Test -using IntervalConstraintProgramming -using IntervalArithmetic #, IntervalArithmetic.RootFinding -#using Base.Test +using IntervalArithmetic +using IntervalConstraintProgramming +using Test @testset "Utilities" begin @test IntervalConstraintProgramming.unify_variables([:a, :c], [:c, :b]) == ([:a,:b,:c], [1,3], [3,2], [1,0,2], [0,2,1]) @@ -42,20 +41,20 @@ end S1b = @constraint y > 0 S1 = S1a ∩ S1b - paving = pave(S1, IntervalBox(-3..3, -3..3), 0.1) + paving = pave(S1, IntervalBox(-3..3, -3..3), 2.0, 0.5) - @test paving.inner == [IntervalBox(1.5..3, 0..3), IntervalBox(0..1.5, 0..3)] - @test isempty(paving.boundary) == true + @test paving.inner == [IntervalBox(1.5..3, 0..3), IntervalBox(0..1.5, 1.5..3)] + @test isempty(paving.boundary) == false S2 = S1a ∪ S1b paving = pave(S2, IntervalBox(-3..3, -3..3), 0.1) @test paving.inner == [IntervalBox(-3..0, 0..3), IntervalBox(0..3, -3..3)] - @test isempty(paving.boundary) == true + @test isempty(paving.boundary) == false S3 = @constraint x^2 + y^2 <= 1 X = IntervalBox(-∞..∞, -∞..∞) - paving = pave(S3, X, 1.0) + paving = pave(S3, X, 1.0, 0.5) @test paving.inner == [IntervalBox(Interval(0.0, 0.5), Interval(0.0, 0.8660254037844386)), IntervalBox(Interval(0.0, 0.5), Interval(-0.8660254037844386, 0.0)), @@ -136,18 +135,16 @@ end @testset "Nested functions" begin @function f(x) = 2x @function g(x) = ( a = f(x); a^2 ) + @function g2(x) = ( a = f(f(x)); a^2 ) C = @contractor g(x) + C2 = @contractor g2(x) A = IntervalBox(0.5..1) x = IntervalBox(0..1) - @test C(A, x) == IntervalBox(sqrt(A / 4)) - - @function g2(x) = ( a = f(f(x)); a^2 ) - C2 = @contractor g2(x) - - @test C2(A, x) == IntervalBox(sqrt(A / 16)) + @test C(A, x) == IntervalBox(sqrt(A[1] / 4)) + @test C2(A, x) == IntervalBox(sqrt(A[1] / 16)) end @@ -161,5 +158,5 @@ end @function g3(x) = ( a = (f↑2)(x); a^2 ) C3 = @contractor g3(x) - @test C3(A, x) == IntervalBox(sqrt(A / 16)) + @test C3(A, x) == IntervalBox(sqrt(A[1] / 16)) end