Skip to content
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
1 change: 1 addition & 0 deletions src/EpsilonOptimization/EpsilonOptimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export
AlphaBranchAndBound

include("types.jl")
include("utils.jl")
include("core.jl")

include("solvers/lipschitz_branch_and_bound.jl")
Expand Down
36 changes: 26 additions & 10 deletions src/EpsilonOptimization/solvers/alpha_branch_and_bound.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@
"""
struct AlphaBranchAndBound <: AbstractEpsilonSolver
epsilon::Real
threshold::Union{<:Real,Nothing}
target::Union{<:Real,Nothing}
max_iterations::Union{<:Integer,Nothing}
alpha::Real

function AlphaBranchAndBound(
epsilon::Real,
alpha::Real;
target::Union{<:Real,Nothing}=nothing,
max_iterations::Union{<:Integer,Nothing}=nothing,
)
solver = new(epsilon, target, max_iterations, alpha)
validate_solver_params(solver)

if alpha <= 0
throw(ArgumentError("Alpha parameter must be positive, got $alpha"))
end

return solver
end
end

"""
Expand All @@ -30,7 +46,7 @@ struct ABBHyperrectangle{Tx<:AbstractVector{<:Real},Tf<:Real}
function ABBHyperrectangle(
lower::Tx, upper::Tx, f::Function, alpha::Real
) where {Tx<:AbstractVector{<:Real}}
f_convex(x::Tx) = f(x) + alpha * sum((x .- lower) .* (upper .- x))
f_convex(x::Tx) = f(x) - alpha * sum((x .- lower) .* (upper .- x))
x0 = lower .+ (upper .- lower) ./ 2

res = optimize(f_convex, lower, upper, x0, Fminbox(LBFGS()))
Expand All @@ -51,10 +67,10 @@ function _epsilon_minimize_impl(
epsilon = solver.epsilon
alpha = solver.alpha

if isnothing(solver.threshold)
threshold = -Inf
if isnothing(solver.target)
target = -Inf
else
threshold = solver.threshold
target = solver.target
end

if isnothing(solver.max_iterations)
Expand All @@ -76,9 +92,9 @@ function _epsilon_minimize_impl(
iterations < max_iterations &&
!isempty(rects_cand) &&
(
isinf(threshold) || (
min(lower_bound, first(rects_cand).lower_bound) <= threshold &&
minimum - threshold >= epsilon
isinf(target) || (
min(lower_bound, first(rects_cand).lower_bound) <= target &&
minimum - target >= epsilon
)
)
)
Expand Down Expand Up @@ -123,8 +139,8 @@ function _epsilon_minimize_impl(
minimum,
(
epsilon_optimal=minimum - lower_bound < epsilon,
threshold_reached=minimum - threshold < epsilon,
threshold_unreachable=-Inf < threshold < lower_bound,
target_reached=minimum - target < epsilon,
target_unreachable=-Inf < target < lower_bound,
iterations=iterations >= max_iterations,
),
)
Expand Down
38 changes: 29 additions & 9 deletions src/EpsilonOptimization/solvers/lipschitz_branch_and_bound.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,29 @@
"""
struct LipschitzBranchAndBound <: AbstractEpsilonSolver
epsilon::Real
threshold::Union{<:Real,Nothing}
target::Union{<:Real,Nothing}
max_iterations::Union{<:Integer,Nothing}
lipschitz_constant::Real

function LipschitzBranchAndBound(
epsilon::Real,
lipschitz_constant::Real;
target::Union{<:Real,Nothing}=nothing,
max_iterations::Union{<:Integer,Nothing}=nothing,
)
solver = new(epsilon, target, max_iterations, lipschitz_constant)
validate_solver_params(solver)

if lipschitz_constant <= 0
throw(
ArgumentError(
"Lipschitz constant must be positive, got $lipschitz_constant"
),
)
end

return solver
end
end

"""
Expand Down Expand Up @@ -49,10 +69,10 @@ function _epsilon_minimize_impl(
epsilon = solver.epsilon
lipschitz_constant = solver.lipschitz_constant

if isnothing(solver.threshold)
threshold = -Inf
if isnothing(solver.target)
target = -Inf
else
threshold = solver.threshold
target = solver.target
end

if isnothing(solver.max_iterations)
Expand All @@ -74,9 +94,9 @@ function _epsilon_minimize_impl(
iterations < max_iterations &&
!isempty(rects_cand) &&
(
isinf(threshold) || (
min(lower_bound, first(rects_cand).lower_bound) <= threshold &&
minimum - threshold >= epsilon
isinf(target) || (
min(lower_bound, first(rects_cand).lower_bound) <= target &&
minimum - target >= epsilon
)
)
)
Expand Down Expand Up @@ -118,8 +138,8 @@ function _epsilon_minimize_impl(
minimum,
(
epsilon_optimal=minimum - lower_bound < epsilon,
threshold_reached=minimum - threshold < epsilon,
threshold_unreachable=-Inf < threshold < lower_bound,
target_reached=minimum - target < epsilon,
target_unreachable=-Inf < target < lower_bound,
iterations=iterations >= max_iterations,
),
)
Expand Down
23 changes: 11 additions & 12 deletions src/EpsilonOptimization/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Concrete subtypes of `AbstractEpsilonSolver` must implement the following method

Concrete subtypes of `AbstractEpsilonSolver` must also have the following fields:
- `epsilon::Real`: The tolerance for ``ϵ``-convergence.
- `threshold::Union{<:Real,Nothing}`: An optional threshold value resulting in termination
once either (1) a function value less than `threshold + epsilon` is found or (2) it is
determined that no function value less than or equal to `threshold` exists.
- `target::Union{<:Real,Nothing}`: An optional threshold value resulting in termination once
either (1) a function value less than `target + epsilon` is found or (2) it is
determined that no function value less than or equal to `target` exists.
- `max_iterations::Union{<:Integer,Nothing}`: An optional maximum number of iterations after
which the algorithm will terminate.
"""
Expand All @@ -38,10 +38,10 @@ Output struct for ``ϵ``-optimization results.
- `minimum::Tf<:Real`: The estimated global minimum function value.
- `stopped_by::NamedTuple`: The reason(s) for termination, namely:
- `:epsilon_optimal::Bool`: Whether ``ϵ``-optimality was achieved.
- `:threshold_reached::Bool`: Whether ta function value less than `threshold + epsilon`
was found for some optionally provided `threshold`.
- `:threshold_unreachable::Bool`: Whether it was determined that no function value less
than or equal to `threshold` existed for some optionally provided `threshold`.
- `:target_reached::Bool`: Whether ta function value less than `target + epsilon` was
found for some optionally provided `target`.
- `:target_unreachable::Bool`: Whether it was determined that no function value less
than or equal to `target` existed for some optionally provided `target`.
- `:iterations::Bool`: Whether termination occurred due to reaching the maximum number
of iterations for some optionally provided `max_iterations`.
"""
Expand All @@ -53,17 +53,16 @@ struct EpsilonMinimizationResult{Tx<:AbstractVector{<:Real},Tf<:Real}
minimizer::Tx
minimum::Tf
stopped_by::NamedTuple{
(:epsilon_optimal, :threshold_reached, :threshold_unreachable, :iterations),
(:epsilon_optimal, :target_reached, :target_unreachable, :iterations),
Tuple{Bool,Bool,Bool,Bool},
}
end

# The `Base.show` override here takes heavy inspiration from the Optim.jl package
function Base.show(io::IO, res::EpsilonMinimizationResult)
println(io, "Results of Epsilon Minimization Algorithm")
print(io, " * Status: ")

if res.stopped_by.epsilon_optimal || res.stopped_by.threshold_reached
if res.stopped_by.epsilon_optimal || res.stopped_by.target_reached
println(io, "success")
else
println(io, "failure")
Expand All @@ -73,8 +72,8 @@ function Base.show(io::IO, res::EpsilonMinimizationResult)
println(io, " * Lower bounds: $(res.lower)")
println(io, " * Upper bounds: $(res.upper)")
println(io, " * Epsilon tolerance: $(res.epsilon)")
println(io, " * Estimated minimizer: $(res.minimizer)")
println(io, " * Estimated minimum: $(res.minimum)")
println(io, " * Minimizer: $(res.minimizer)")
println(io, " * Minimum: $(res.minimum)")

return nothing
end
37 changes: 37 additions & 0 deletions src/EpsilonOptimization/utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2025 Luis M. B. Varona and Nathaniel Johnston
#
# Licensed under the MIT license <LICENSE or
# http://opensource.org/licenses/MIT>. This file may not be copied, modified, or
# distributed except according to those terms.

"""
validate_solver_params(solver)

[TODO: Write here]

# Arguments
[TODO: Write here]

# Raises
[TODO: Write here]

# Returns
[TODO: Write here]
"""
function validate_solver_params(solver::AbstractEpsilonSolver)
epsilon = solver.epsilon

if epsilon <= 0
throw(ArgumentError("Epsilon parameter must be positive, got $epsilon"))
end

max_iterations = solver.max_iterations

if !isnothing(max_iterations) && max_iterations <= 0
throw(
ArgumentError(
"Max iterations must be positive or nothing, got $(max_iterations)"
),
)
end
end
7 changes: 5 additions & 2 deletions src/QuantumStateTransfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ include("types.jl")
include("EpsilonOptimization/EpsilonOptimization.jl")
using .EpsilonOptimization

include("core.jl")
include("state_transfer.jl")
include("uniform_mixing.jl")
include("fractional_revival.jl")

# TODO: Exports
# TODO: Exports (add more later)
export max_state_transfer, check_state_transfer

include("startup.jl")

Expand Down
File renamed without changes.
Loading
Loading