diff --git a/src/operators.jl b/src/operators.jl index 8150658fdb2..b1f8d1c1348 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -502,3 +502,32 @@ function _MA.operate!!( ) return _throw_operator_error(op, x) end + +_mult_upper(α, A) = parent(α * LinearAlgebra.UpperTriangular(parent(A))) +_mult_lower(α, A) = parent(α * LinearAlgebra.LowerTriangular(parent(A))) + +function Base.:*( + x::Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, + A::LinearAlgebra.Hermitian, +) + c = LinearAlgebra.sym_uplo(A.uplo) + B = c == :U ? _mult_upper(x, A) : _mult_lower(x, A) + # Intermediate conversion to `Matrix` is needed to work around + # https://github.com/JuliaLang/julia/issues/52895 + return LinearAlgebra.Hermitian(Matrix(LinearAlgebra.Hermitian(B, c)), c) +end + +function Base.:*( + A::LinearAlgebra.Hermitian, + x::Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +) + return x * A +end diff --git a/test/test_complex.jl b/test/test_complex.jl index db61feff447..7b64c027f20 100644 --- a/test/test_complex.jl +++ b/test/test_complex.jl @@ -323,4 +323,19 @@ function test_hermitian_jump_scalar() return end +function test_mul_real_hermitian() + A = [1+1im 1+1im; 1-2im 3+1im] + model = Model() + @variable(model, x) + for s in (:L, :U), f in (x, x + 1, x^2) + B = LinearAlgebra.Hermitian(A, s) + @test f * B isa LinearAlgebra.Hermitian + @test isequal_canonical(f * B, LinearAlgebra.Hermitian(f * A, s)) + @test B * f isa LinearAlgebra.Hermitian + @test isequal_canonical(B * f, LinearAlgebra.Hermitian(A * f, s)) + @test isequal_canonical(f * B + f * B, (2 * f) * B) + end + return +end + end # TestComplexNumberSupport