Hello everyone,
This is a pretty long question and it’s pretty complicated (at least to me it is) so I will highly appreciate all answers.
I am currently writing a python wrapper around the Extremes.jl package. That package has a “BlockMaxima’’ structure, which I need to be able to convert back and forth from python to Julia. BlockMaxima has two different constructors :
struct BlockMaxima{T} <: AbstractExtremeValueModel
data::Variable
location::paramfun
logscale::paramfun
shape::paramfun
end
function BlockMaxima{GeneralizedExtremeValue}(data::Variable;
locationcov::Vector{<:DataItem} = Vector{Variable}(),
logscalecov::Vector{<:DataItem} = Vector{Variable}(),
shapecov::Vector{<:DataItem} = Vector{Variable}())::BlockMaxima
# …A lot of unnecessary code for my question…
return BlockMaxima{GeneralizedExtremeValue}(data, paramfun(locationcov, locationfun), paramfun(logscalecov, logscalefun), paramfun(shapecov, shapefun))
end
function BlockMaxima{Gumbel}(data::Variable;
locationcov::Vector{<:DataItem} = Vector{Variable}(),
logscalecov::Vector{<:DataItem} = Vector{Variable}())::BlockMaxima
# …A lot of unnecessary code for my question…
return BlockMaxima{Gumbel}(data, paramfun(locationcov, locationfun), paramfun(logscalecov, logscalefun), paramfun(shapecov, shapefun))
end
Here is the BlockMaxima class I defined in python:
class AbstractExtremeValueModel:
pass
class BlockMaxima(AbstractExtremeValueModel):
data: Variable
location: paramfun
logscale: paramfun
shape: paramfun
type: str
def __init__(self, data: Variable, location: paramfun, logscale: paramfun, shape: paramfun, type: str):
self.data, self.location, self.logscale, self.shape, self.type = data, location, logscale, shape, type
The MAJOR problem I am encountering is that the BlockMaxima constructor in Julia has 2 different signatures, BloackMaxima{GeneralizedExtremeValue}(…) and BloackMaxima{Gumbel}(…), and I am unable to recreate the call to any of those signatures from python. For instance, the first implementation of python → Julia conversion I tried was:
def py_blockmaxima_to_jl_blockmaxima(py_blockmaxima: BlockMaxima):
jl_data = py_blockmaxima.data.py_variable_to_jl_variable()
jl_locationcov = py_blockmaxima.location.covariate
jl_logscalecov = py_blockmaxima.logscale.covariate
jl_shapecov = py_blockmaxima.shape.covariate
if py_blockmaxima.type == "BlockMaxima{Distributions.GeneralizedExtremeValue}":
return Extremes.BlockMaxima(jl_data, jl_locationcov, jl_logscalecov, jl_shapecov)
elif py_blockmaxima.type == "BlockMaxima{Distributions.Gumbel}":
return Extremes.BlockMaxima(jl_data, jl_locationcov, jl_logscalecov)
else:
raise ValueError("Unsupported BlockMaxima type: {}".format(py_blockmaxima.type))
When I call this function by doing
jl_blockmaxima = py_blockmaxima_to_jl_blockmaxima(fm1.model) #fm1.model, in this case, is a python BlockMaxima object whose type attribute is the string “BlockMaxima{Distributions.GeneralizedExtremeValue}”
I get the error
TypeError: Julia: MethodError: no method matching BlockMaxima(::Variable, ::Vector{Variable}, ::Vector{Variable}, ::Vector{Variable})
I suspect that the issue is that I am calling BlockMaxima(…) instead of BlockMaxima{GeneralizedExtremeValue}(…), but I don’t know how to do that with juliacall… For instance, simply doing
return Extremes.BlockMaxima{GeneralizedExtremeValue}(jl_data, jl_locationcov, jl_logscalecov, jl_shapecov)
of course makes no sense in python and results in a syntax error.
If anyone knows how I could solve this conversion problem, I would be extremely grateful for some help!
Additional information:
• The Variable and paramfun types in my python code are classes I defined
• The ‘’Extremes’’ keyword in my python code is defined like so:
from juliacall import Main as jl
from juliapkg import add
add("Extremes", "fe3fe864-1b39-11e9-20b8-1f96fa57382d")
jl.seval("using Extremes")
Extremes = jl.Extremes
• Here’s a few examples of other conversion functions I wrote before for simpler structures:
def py_str_to_jl_symbol(str: str):
return jl.Symbol(str)
def py_list_to_jl_vector(py_list: list):
if all(isinstance(i, float) or isinstance(i, int) for i in py_list):
return jl_convert(jl.Vector[jl.Real], py_list)
if all(isinstance(i, str) for i in py_list):
return jl_convert(jl.Vector[jl.String], py_list)
else:
return jl_convert(jl.Vector[jl.Any], py_list) # for other types of values
def jl_vector_to_py_list(jl_vector) -> list:
return list(jl_vector)
Thanks!