Skip to content

Interior-point Newton seems to be non-deterministic #971

@ForceBru

Description

@ForceBru

Code

import Pkg
Pkg.activate(temp=true, io=devnull)
Pkg.add(name="Optim", version="1.6.0", io=devnull)
Pkg.status()

const AV = AbstractVector{T} where T
using DelimitedFiles, Optim

normal_pdf(x::Real, μ::Real, var::Real) =
    exp(-(x-μ)^2 / (2var)) / sqrt(2π * var)

function D::AV{<:Real}, b::Real, r::AV{<:Real})
	@assert length(θ) % 3 == 0

	N = length(r)
	K = length(θ) ÷ 3
	α, μ, σ = θ[1:K], θ[K+1:2K], θ[2K+1:end]
	var = σ .^2

	sum(
		α[k] * (
			sum(
				α[h] * normal_pdf(μ[k], μ[h], 2b + var[k] + var[h])
				for h  1:K
			)
			- 2*sum(
				1/N * normal_pdf(μ[k], r[j], 2b + var[k])
				for j  eachindex(r)
			)
		)
		for k  1:K
	)
end

data = open("sample_data.csv", "r") do io
	DelimitedFiles.readdlm(io, ',')
end
data = data[:, 1]

result = let
	θ0 = Float64[1/3,1/3,1/3, 0,0,0, 1,1,1]
	obj::AV{<:Real}) = D(θ, 1.0, data)

	con_c!(c, θ::AV{<:Real}) = (c[1] = sum(θ[1:(length(θ)÷3)]) - 1; c)
	con_jac!(J, θ::AV{<:Real}) = (J[1, 1:(length(θ)÷3)] .= 1; J)
	con_h!(H, θ, λ) = H

	lx = Float64[0,0,0, -Inf,-Inf,-Inf, 0,  0,  0]
	ux = Float64[1,1,1,  Inf, Inf, Inf, Inf,Inf,Inf]
	lc = [0.0]; uc = [0.0]
	dfc = TwiceDifferentiableConstraints(
		con_c!, con_jac!, con_h!,
		lx, ux, lc, uc
	)
	optimize(
		obj, dfc, θ0, IPNewton(),
		Optim.Options(store_trace=true, show_trace=true, show_every=1, iterations=10),
		autodiff=:forward
	)
end

@show Optim.minimizer(result)
  • The objective function is obj(θ::AV{<:Real}) = D(θ, 1.0, data)
  • One affine constraint: θ[1] + θ[2] + θ[3] - 1 == 0
  • Inequality constraints:
    • θ[1]>=0 && θ[2]>=0 && θ[3]>=0
    • θ[1]<=1 && θ[2]<=1 && θ[3]<=1
    • θ[7]>=0 && θ[8]>=0 && θ[9]>=0
  • Starting point: θ0 = Float64[1/3,1/3,1/3, 0,0,0, 1,1,1]

100 data points: sample_data.csv

I'm not particularly sure about the specification of the Hessian in con_h!(H, θ, λ) = H. AFAIK, this function should add the Hessian of the constraints to H. Since the constraints are linear, I should be adding zero, which is equivalent to doing nothing and simply returning the original Hessian, right?

Problem

If I run this code multiple times (with the same starting point and the same settings!), I randomly get NaNs everywhere:

~/t/Optim $ julia-1.7 report.jl                                                                                                               (base) 
      Status `/private/var/folders/ys/3h0gnqns4b98zb66_vl_m35m0000gn/T/jl_uNRF2Q/Project.toml`
  [429524aa] Optim v1.6.0
Iter     Lagrangian value Function value   Gradient norm    |==constr.|      μ
     0   -1.966311e-01    -1.966871e-01    1.096659e-03     0.000000e+00     1.24e-05
 * time: 1.642772858940843e9
     1   -1.967483e-01    -1.967538e-01    5.965825e-04     4.191989e-21     1.24e-06
 * time: 1.642772860832457e9
     2   -1.967599e-01    -1.967656e-01    1.449232e-03     0.000000e+00     1.24e-06
 * time: 1.642772860833223e9
     3   -1.967671e-01    -1.967714e-01    8.505952e-05     3.350116e-23     9.33e-07
 * time: 1.642772860833715e9
     4   -1.967796e-01    -1.967802e-01    4.338795e-04     1.761310e-22     1.30e-07
 * time: 1.642772860834255e9
     5   -1.967911e-01    -1.967917e-01    4.314652e-04     1.548437e-22     1.25e-07
 * time: 1.642772860834772e9
     6   -1.967975e-01    -1.967984e-01    6.682571e-05     2.934913e-22     1.63e-07
 * time: 1.642772860835274e9
     7   -1.968029e-01    -1.968034e-01    1.454685e-04     6.996569e-23     9.13e-08
 * time: 1.64277286083576e9
     8   -1.968043e-01    -1.968048e-01    1.681820e-04     3.166961e-22     8.18e-08
 * time: 1.642772860836246e9
     9   -1.968060e-01    -1.968065e-01    4.130831e-04     2.883045e-22     7.89e-08
 * time: 1.642772860836716e9
    10   -1.968087e-01    -1.968091e-01    6.875015e-04     1.149389e-22     7.09e-08
 * time: 1.642772860837178e9
Optim.minimizer(result) = [0.12046220033401919, 0.21628965837338163, 0.6632481412925991, 0.3006553011305413, 0.36322704596103234, -0.15810174712000133, 1.3014704098568006, 0.561227621176915, 1.0813300576532012]
~/t/Optim $ julia-1.7 report.jl                                                                                                               (base) 
      Status `/private/var/folders/ys/3h0gnqns4b98zb66_vl_m35m0000gn/T/jl_JcPxe4/Project.toml`
  [429524aa] Optim v1.6.0
Iter     Lagrangian value Function value   Gradient norm    |==constr.|      μ
     0   NaN              -1.966871e-01    NaN              NaN              1.00e+00
 * time: 1.642772890437607e9
     1   NaN              NaN              NaN              NaN              1.00e-01
 * time: 1.642772892260335e9
     2   NaN              NaN              NaN              NaN              NaN   
 * time: 1.64277289226151e9
     3   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892262521e9
     4   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892263575e9
     5   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892264575e9
     6   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892265608e9
     7   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892266602e9
     8   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892267635e9
     9   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892268625e9
    10   NaN              NaN              NaN              NaN              NaN   
 * time: 1.642772892269617e9
Optim.minimizer(result) = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]
~/t/Optim $

Looks like NaN first appears in Lagrangian value, and then everything becomes NaN.

Is this expected behavior? Am I maybe specifying constraints incorrectly?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions