Overloading struct and related methods


#1

Hi there,

I want to use a struct and related methods from the package ProximalOperators.jl. Specifically, I want to change the field lambda of an immutable struct.

MWE:

using ProximalOperators

function foo(g::T) where T <: ProximableFunction
    g.lambda = 1.0
end

foo(NormL1())

which gives an ERROR: type is immutable. Makes sense, since NormL1 is immutable…

How do I proceed from here?

I tried changing the struct NormL1 from immutable to mutable inside my own module:

module mymodule
import ProximalOperators: NormL1, ProximableFunction
export NormL1

mutable struct NormL1{T <: Union{Real, AbstractArray}} <: ProximableFunction
  lambda::T
  function NormL1{T}(lambda::T) where {T <: Union{Real, AbstractArray}}
    if !(eltype(lambda) <: Real)
      error("λ must be real")
    end
    if any(lambda .< 0)
      error("λ must be nonnegative")
    else
      new(lambda)
    end
  end
end

NormL1{R <: Real}(lambda::R=1.0) = NormL1{R}(lambda)

end

Now I can do foo(mymodule.NormL1()) without errors. However, I cannot see how to overload all existing methods in ProximalOperators that make use of NormL1, see e.g., prox!, without copy-pasting all of them to mymodule and changing the relevant function signatures from ProximalOperators.NormL1 to mymodule.NormL1.

Links and tips for handling this, would be very much appreciated.

Best,
Oliver


#2

You can’t redefine a type. Also, unless it is exported, your definition inside another module (possibly Main) will be different from the one in the original module, since they are different namespaces.

Perhaps you should simply create a new NormL1 instance with the desired value? It seems to be just a wrapper with some check, should not be too costly.


#3

Thanks,
I agree that it is not too costly for this MWE, but if I wanted more methods and related functions from that package, I would end up with a lot of redundant code, right?

Furthermore, I actually want to iterate over different lambda, not just the 1.0 I set above…


#4

Perhaps you misunderstand my suggestion, I was not recommending that you duplicate any code, just create new instances, eg NormL1(1.0), since you cannot change existing ones.

It is possible that you are not using this package as it was intended (I cannot say as I am not familiar with it). It appears to be superbly documented, so if something is not clear after reading the docs, perhaps ask the author.


#5

Why not create different instances of NormL1 inside your loop as in:

using ProximalOperators

function foo(g::T) where T <: ProximableFunction
    # g.lambda = 1.0
    # simply use g
end

for lambda in linspace(0, 10, 100)
  foo(NormL1(lambda))
end

In the end, NormL1 is constructed either via a scalar or some array. Arrays are passed by pointer, and neither of the constructions should result in a huge penalty in performance.


#6

Thanks @Tamas_Papp and @aytekinar. That’s exactly what I needed.