Convex.jl norm of multiple variables

I would like to use Convex.jl to solve a problem along the lines of

using Convex
x = Variable(1)
y = Variable(1)
z = Variable(1)
problem = minimize(x+y+z,norm([x,y,z])<=1)

However, when I run this code, I get the error

MethodError: no method matching AbstractFloat(::Convex.MaxAtom)

Closest candidates are:
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number}
   @ Base char.jl:50
  (::Type{T})(::Base.TwicePrecision) where T<:Number
   @ Base twiceprecision.jl:266
  (::Type{T})(::Complex) where T<:Real
   @ Base complex.jl:44
  ...


Stacktrace:
 [1] float(x::Convex.MaxAtom)
   @ Base ./float.jl:294
 [2] generic_normInf(x::Vector{Variable})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:453
 [3] normInf
   @ ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:527 [inlined]
 [4] generic_norm2(x::Vector{Variable})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:463
 [5] norm2
   @ ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:529 [inlined]
 [6] norm(itr::Vector{Variable}, p::Int64)
   @ LinearAlgebra ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:598
 [7] norm(itr::Vector{Variable})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.9.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.9/LinearAlgebra/src/generic.jl:596
 [8] top-level scope
   @ In[9]:5

From this I understand that the norm function can only be used on multi-dimensional variables, not multiple variables. Is there a way to combine multiple one-dimensional variables into a multi-dimensional variable so that this works?

Can you just formulate your problem with a single vector variable from the start?

I suppose in principle this is always possible, but it would improve readability etc to be able to use multiple names for conceptually different variables.

I think you might have better luck by defining the vector variable first and then giving individual names to new variables which you constrain to be equal to the vector components?

You can combine scalar variables into a vector-valued variable using hcat or vcat.

julia> using Convex

julia> import SCS

julia> x = Variable(1)
Variable
size: (1, 1)
sign: real
vexity: affine
id: 351…156

julia> y = Variable(1)
Variable
size: (1, 1)
sign: real
vexity: affine
id: 464…161

julia> z = Variable(1)
Variable
size: (1, 1)
sign: real
vexity: affine
id: 749…425

julia> problem = minimize(x + y + z, norm(hcat(x, y, z)) <= 1)
minimize
└─ + (affine; real)
   ├─ real variable (id: 351…156)
   ├─ real variable (id: 464…161)
   └─ real variable (id: 749…425)
subject to
└─ <= constraint (convex)
   ├─ norm2 (convex; positive)
   │  └─ hcat (affine; real)
   │     ├─ …
   │     ├─ …
   │     └─ …
   └─ 1

status: `solve!` not called yet

julia> solve!(problem, SCS.Optimizer)
------------------------------------------------------------------
	       SCS v3.2.3 - Splitting Conic Solver
	(c) Brendan O'Donoghue, Stanford University, 2012
------------------------------------------------------------------
problem:  variables n: 5, constraints m: 6
cones: 	  z: primal zero / dual free vars: 1
	  l: linear vars: 1
	  q: soc vars: 4, qsize: 1
settings: eps_abs: 1.0e-04, eps_rel: 1.0e-04, eps_infeas: 1.0e-07
	  alpha: 1.50, scale: 1.00e-01, adaptive_scale: 1
	  max_iters: 100000, normalize: 1, rho_x: 1.00e-06
	  acceleration_lookback: 10, acceleration_interval: 10
lin-sys:  sparse-direct-amd-qdldl
	  nnz(A): 9, nnz(P): 0
------------------------------------------------------------------
 iter | pri res | dua res |   gap   |   obj   |  scale  | time (s)
------------------------------------------------------------------
     0| 1.68e+01  1.68e+00  6.00e+01 -3.00e+01  1.00e-01  8.72e-05 
    50| 3.30e-06  1.39e-09  2.86e-06 -1.73e+00  1.00e-01  1.17e-04 
------------------------------------------------------------------
status:  solved
timings: total: 1.19e-04s = setup: 6.81e-05s + solve: 5.12e-05s
	 lin-sys: 9.65e-06s, cones: 5.73e-06s, accel: 2.43e-06s
------------------------------------------------------------------
objective = -1.732052
------------------------------------------------------------------

julia> evaluate(x), evaluate(y), evaluate(z)
(-0.5773512251810479, -0.5773512251810479, -0.5773512251810479)
3 Likes

Thank you! Of course this perfectly answers my question. Thanks also @gdalle ; what you suggest would work as well I think but it is very nice to be able to write things using multiple variables when this is convenient.