using Random using LinearAlgebra function genQf(n::Integer; fseed::Integer=0, rank::Real=1.1, conv::Real=1, ecc::Real=0.99, dom::Real=1, box::Real=1, q::Union{Vector, Nothing}=nothing, v::Union{Real, Nothing}=nothing)::Tuple{Matrix, Union{Vector, Nothing}, Union{Real, Nothing}} if n <= 0 throw(ArgumentError(n, "n must be > 0")) end if rank <= 0 throw(ArgumentError(rank, "rank must be > 0")) end if !(0 <= conv <= 1) throw(ArgumentError(conv, "conv must be in [0, 1]")) end if !(0 <= ecc < 1) throw(ArgumentError(ecc, "ecc must be in [0, 1)")) end if dom < 0 throw(ArgumentError(dom, "dom must be >= 0")) end if box == 0 throw(ArgumentError(box, "box must not be 0")) end Random.seed!(fseed) # generate Q- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # first step: generate the appropriate rank and positive / negative # definiteness r = round(Int, rank * n) p = round(Int, r * conv) if p > 0 G = rand(p, n) Q = G' * G else Q = zeros(n, n) end if r > p G = rand(r - p, n) Q = Q - (G' * G) end # second step: if dom ~= 1, modify the diagonal # increase or decrease randomly each element by [ - 1/3 , 1/3 ] of its # initial value, then multiply it by dom if dom != 1 D = diag(Q) D = D .* (1 .+ (2 .* rand(n, 1) .- 1) ./ 3) D = dom * D @view(Q[diagind(Q)]) .= D end # compute eigenvalue decomposition F = eigen(Q); # V * D * V' = Q , D( 1 , 1 ) = \lambda_n V = F.vectors D = F.values if D[1] > 1e-14 # modify eccentricity only if \lambda_n > 0, for when \lambda_n = 0 the # eccentricity is 1 by default # # the formula is: # # \lambda_i - \lambda_n 2 * ecc # \lambda_i = \lambda_n + --------------------- * \lambda_n ------- # \lambda_1 - \lambda_n 1 - ecc # # This leaves \lambda_n unchanged, and modifies all the other ones # proportionally so that # # \lambda_1 - \lambda_n # --------------------- = ecc (exercise: check) # \lambda_1 - \lambda_n l = D[1] .* ones(n) + ((D[1] / (D[n] - D[1])) * (2 * ecc / (1 - ecc))) .* (D - D[1] .* ones(n)) Q = V * diagm(l) * V'; end if q != nothing || v != nothing # if so required generate q- - - - - - - - - - - - - - - - - - - - - - - # # we first generate the unconstrained minimum x_* of the problem in the # box [ - abs( box ) , abs( box ) ] and then we set q = - Q * x_* x = 2 * abs(box) .* rand(n) .- abs(box) q = -Q * x # if so required, we now randomly destroy the alignment between q and # the image of Q so as to make it hard to solve Q x = q if ( box < 0 ) && (D[1] <= 1e-14 ) q = q .* ((4/3) .* rand(n) .- (2/3)) end end if v != nothing # if so required compute v. - - - - - - - - - - - - - - - - - - - - - # v is finite-valued only if either Q is strictly positive definite # or it is positive semidefinite but q has been constructed in such # a was that Q * x + q = 0 has a solution (that is the x we have) if (D[1] > 1e-14) || ((D[1] > -1e-14) && (box > 0)) v = dot(q', x) / 2 else v = -Inf end end return (Q, q, v) end