SymbolicUtils.jl icon indicating copy to clipboard operation
SymbolicUtils.jl copied to clipboard

RFC: Wrapper type interface

Open shashi opened this issue 3 years ago • 6 comments

It maybe worth considering making a macro which generates an interface to wrap a symbolic expression to be a subtype of a certain outside type.


@symbolic_wrap Num <: Real

would generate:

struct Num <: Real
    value
end

wrapper_type(::Type{<:Real}) = Num
iswrapper(::Type{Num}) = true # also, there would be iswrapped(x) = iswrapper(typeof(x))
unwrap(x::Num) = x.value

The benefits are:

  1. @variables macro in Symbolics.jl can use wrapper_type to wrap objects it creates in the right wrapper.
  2. We could also have a @wrapped macro which unwraps the arguments and wraps the results appropriately. For example
@symbolic_wrap Num <: Real
@symbolic_wrap IntLike <: Integer

@wrapped function foo(x::Integer, y::Real, z)
    <expr> # expr can do istree etc!
end

Would generate the equivalent of

function _foo(x, y, z)
    res = <expr>
end

foo(x::Symbolic{<:Integer}, y::Symbolic{<:Real}, z) = _foo(x, y, z)
foo(x::IntLike, y::SymUnion{Real}, z) = wrap(_foo(unwrap(x), unwrap(y), z))
foo(x::SymUnion{Integer}, y::Num, z) = wrap(_foo(unwrap(x), unwrap(y), z))

# where 
# const SymUnion{T} = Union{T, Symbolic{<:T}}
# wrap(x) = wrapper_type(typeof(x))(x)

So only code that is aware of the symbolic nature of itself will use @wrapped, non-symbolic code will try to do things as usual.

I'm opening this in SymbolicUtils rather than Symbolics because we can just define @wrapped numeric methods here rather than have a @number_methods type.

This was prompted by attempts to create symbolic ranges to use with symbolic arrays.

shashi avatar Mar 12 '21 19:03 shashi

@MasonProtter @YingboMa what are your thoughts?

shashi avatar Mar 13 '21 07:03 shashi

It's not clear what methods to overload. For instance, for real numbers, we will have conj(x) = x etc, and for <:Number, we may or may not assume a * b = b * a.

YingboMa avatar Mar 13 '21 17:03 YingboMa

Yes, but the proposal is to just create conveniences for writing methods. You still have to define them on a case by case basis for every type you want to symbolically support.

shashi avatar Mar 13 '21 18:03 shashi

I think this is going to ultimately be necessary if we want to do this without fancy compiler tools. One thing I'll note though is that an external using generating

SymbolicUtils.wrapper_type(::Type{<:Real}) = Num

would be piracy, so if multiple users created their own <:Real types, there'd be trouble.

MasonProtter avatar Mar 15 '21 21:03 MasonProtter

Yes and we can allow some authorized piracy and document it. Important Base types could be made available here or in Symbolics.

On Mar 16, 2021, at 2:40 AM, Mason Protter @.***> wrote:

 I think this is going to ultimately be necessary if we want to do this without fancy compiler tools. One thing I'll note though is that an external using generating

SymbolicUtils.wrapper_type(::Type{<:Real}) = Num would be piracy, so if multiple users created their own <:Real types, there'd be trouble.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

shashi avatar Mar 16 '21 03:03 shashi

Note to also support @symbolic_wrap Arr{T,N} <: AbstractArray{T, N}

shashi avatar Mar 22 '21 22:03 shashi