Plusieurs types pour une fonction Julia — Les-mathematiques.net The most powerful custom community solution in the world

Plusieurs types pour une fonction Julia

Bonjour
Dans Julia on n'est pas obligé de typer les arguments des fonctions, mais je préfère le faire.

J'ai une fonction
function Jack(x::Array{Float64,1}, lambda::Array{Int64,1}, alpha::Float64)
Je voudrais aussi qu'elle marche avec ces types:
function Jack(x::Array{Rational{Int64},1}, lambda::Array{Int64,1}, alpha::Rational{Int64})
Comment faire ça ?
En outre, je définis dans ma fonction Jack une autre fonction, appelée jac:
function Jack(x::Array{Float64,1}, lambda::Array{Int64,1}, alpha::Float64)
    function jac(m::Int64, k::Int64, mu::Array{Int64,1}, nu::Array{Int64,1}, beta::Float64)
       ...
Je voudrais que lorsqu'on utilise Jack avec du Rational{Int64}, le beta de jac soit un Rational{Int64}. Je ne sais pas si je suis clair là ?

Réponses

  • Je crois qu'un truc comme ça c'est pas mal:
    function Jack(x::Array{T,1}, lambda::Array{Int64,1}, alpha::T) where T<:Real
        function jac(m::Int64, k::Int64, mu::Array{Int64,1}, nu::Array{Int64,1}, beta::T)
        .......
    
  • Le problème c'est qu'après j'appelle jac avec beta=1 et il faut que je précise le type de beta. J'ai essayé
    jac(size(x,1), 0, lambda, lambda, 1::T)
    
    mais ça ne marche pas.
  • 1ère question) tu as donné la réponse toi-même: tu écris un bloc de code par méthode. Ok, je n'avais pas compris ta question

    2ème question) pas très claire. Telle que tu l'as écrite, jac ne peux être appelée que par Jack. Pourquoi ne pas mettre le bon type ?
    Sinon: la conversion de type, ça marche: tape Rational(1.0); il va te répondre 1//1 .
    En revanche Rational(pi) te balancera une erreur.
  • Ça marche avec jac(size(x,1), 0, lambda, lambda, convert(T,1)). Est-ce la bonne manière de procéder ?
  • Ah ok, plus joli.

    Voilà mon prog, pour que la 2ème question soit plus claire, et si tu as d'éventuelles remarques.
    function fi(lambda::Array{Int64,1})
        out = Int64[]
        n = size(lambda,1)
        for i in range(1, stop=n)
            out = vcat(out, repeat([lambda[i ]], i))
        end
        return out
    end
    
    function fj(lambda::Array{Int64,1})
        out = Int64[]
        mu = filter(x -> x>0, lambda)
        n = size(mu,1)
        for m in mu
            out = vcat(out, 1:m)
        end
        return out
    end
    
    function dualPartition(lambda::Array{Int64,1})
        out = Int64[]
        for i in 1:lambda[1]
            out = push!(out, size(filter(x -> x>=i, lambda), 1))
        end
        return out
    end
    
    function betaratio(kappa::Array{Int64,1}, mu::Array{Int64,1}, k::Int64, alpha::T)  where T<:Real
        muk = mu[k]
        t = k - alpha * muk
        u = map(i -> t + 1 - i + alpha * kappa[i ], 1:k)
        v = map(i -> t - i + alpha * mu[i ], 1:(k-1))
        muPrime = dualPartition(mu)
        w = map(i -> muPrime[i ] - t - alpha * i, 1:(muk-1))
        prod1 = prod(map(x -> x/(x+alpha-1), u))
        prod2 = prod(map(x -> (x+alpha)/x, v))
        prod3 = prod(map(x -> (x+alpha)/x, w))
        return alpha * prod1 * prod2 * prod3
    end
    
    function fN(lambda::Array{Int64,1}, mu::Array{Int64,1})
        prods = map(i -> prod(Iterators.drop(lambda .+ 1, i)), 1:size(lambda,1))
        return sum(map((x,y) -> x*y, mu, prods))
    end
    
    function Jack(x::Array{T,1}, lambda::Array{Int64,1}, alpha::T) where T<:Real
        function jac(m::Int64, k::Int64, mu::Array{Int64,1}, nu::Array{Int64,1}, beta::T)
            if (size(nu,1)==0 || nu[1]==0 || m==0)
                return convert(T,1)
            end
            if (size(nu,1)>m && nu[m+1]>0)
                return convert(T,0)
            end
            if (m==1)
                return x[1]^(nu[1]) * prod(1 .+ alpha .* collect(1:(nu[1]-1)))
            end
            v = S[fN(lambda,nu),m]
            if (k == 0 && !isnan(v))
                return v
            end
            i = max(1,k)
            s = jac(m-1, 0, nu, nu, convert(T,1)) * beta * x[m]^(sum(mu)-sum(nu))
            while (length(nu) >= i && nu[i ] > 0)
                if (length(nu) == i && nu[i ] > 0 || nu[i ] > nu[i+1])
                    nuPrime = copy(nu)
                    nuPrime[i,1] = nu[i,1] - 1
                    gamma = beta * betaratio(mu, nu, i, alpha)
                    if (nu[i ] > 1)
                        s = s + jac(m, i, mu, nuPrime, gamma)
                    else
                        s = s + jac(m-1, 0, nuPrime, nuPrime, convert(T,1)) * gamma * x[m]^(sum(mu)-sum(nuPrime))
                    end
                end
                i += 1
            end
            if k == 0
                S[fN(lambda,nu), m] = s
            end
            return s
        end
        S = fill(NaN, (fN(lambda,lambda), size(x,1)))
        jac(size(x,1), 0, lambda, lambda, convert(T,1))
    end
    
  • Pour améliorer la lisibilité, tu peux écrire Vector{T} plutôt que Array{T,1}.
  • Effectivement, le problème de typer, c'est que si tu types une fois, tu es un peu obligé de tout typer, car sinon quand Julia essaie de deviner le type, il peut se tromper.

    Ceci dit, quitte à typer, sauf si tu as des raisons très sérieuses de le faire; il est sans doute plus raisonnable de typer avec des types abstraits, mettons Integer plutôt que Int64.
  • Ok merci.

    Je suis accro au typage depuis que je me suis mis à Haskell. Mais le système de typage de Haskell me semble mieux élaboré.

    Là je suis un peu obligé : je veux utiliser Jack soit avec du Float soit avec du Rational. Dans R, j'ai dû faire une fonction pour chaque type.

    Il n'y a pas de typage dans R (mon langage quotidien) et je ne trouve ça pas cool. Imagine f(n)n doit être un entier. Que se passe-t-il si l'utilisateur entre un non-entier ? Il faut tester, faire un truc du genre:
    f <- function(n){
      if(floor(n) != n) stop("n must be an integer")
      ......
    }
    

    Pas besoin de faire ça avec le typage.

    Et puis c'est plus sympa pour le lecteur du code.

    Je viens de tester Jack. Il semble que l'implémentation Julia pulvérise mon implémentation Haskell (en rapidité). Mais je ne suis pas une star en Haskell, peut-être que ma prog n'est pas optimale.
  • Tu peux faire

    typeof(a) <: Integer

    (false pour a=1.0)

    ou

    isinteger(a)

    true pour a=1.0
  • Ok, mais c'est moins sympa que de faire simplement a::Integer, je trouve.

    Finalement, j'ai comparé avec une autre de mes implémentations Haskell, ça semble comparable en rapidité. Dur à dire sans faire des benchmarks.
  • Je viens de voir un truc que je ne comprends pas.

    J'initialise le tableau S à la fin de la fonction Jack:
    S = fill(NaN, (fN(lambda,lambda), size(x,1)))
    
    NaN est de type Float64.

    Puis le prog, quand je l'utilise avec des Rational, remplit ce tableau avec des Rational. Et il retourne bien un Rational à la fin:
    v = S[fN(lambda,nu),m]
    if (k == 0 && !isnan(v))
       return v
    end
    
    Or, quand on met un Rational dans un tableau Float64, le Rational est automatiquement converti en Float64. Je ne comprends donc pas pourquoi Jack retourne quand même un Rational.
  • Je n'aime pas ça... j'ai alors remplacé S par
    S = Array{Union{Missing, T}}(missing, fN(lambda,lambda), size(x,1))
    
    et isnan par ismissing. Voilà qui est clean, maintenant.
  • C'est quand même raide ton truc:

    Jack([1//3;2//1;1],[0;3;4],5//1)

    passe, mais pas

    Jack([1//3;2//1;1],[0;3;4],5)
  • Oui aléa mais est-ce ma faute ? Je veux dire: que puis-je y faire ?
  • Terrifique, j'ai essayé la librairie DynamicPolynomials. En changeant presque rien au code j'ai maintenant les polynômes de Jack en symbolique:
    julia> Jack(2, [3,2], 5//2)
    
    Le résultat ne passe pas sur le phorum (problème d'UTF8 ou je ne sais quoi). Je mets une image:89704
  • aléa écrivait:
    > Jack([1//3;2//1;1],[0;3;4],5//1)

    Attention aléa, le deuxième argument doit être une partition d'un entier: un vecteur d'entiers >0 rangés en ordre décroissant. Sinon je ne sais pas ce que sort le code 8-) et faudra pas se plaindre si tout crashe ;-)
  • Saturne écrivait : http://www.les-mathematiques.net/phorum/read.php?15,1854936,1855126#msg-1855126

    Je peux peut-être faire alpha = T(alpha).
    julia> Rational{Int64}(1)
    1//1
    
  • @aléa, as-tu compris la nuance entre convert(T,x) et T(x) ? Il semble que convert(T,x) est plus "safe".
    Conversion vs. Construction
    Note that the behavior of convert(T, x) appears to be nearly identical to T(x). Indeed, it usually is. However, there is a key semantic difference: since convert can be called implicitly, its methods are restricted to cases that are considered "safe" or "unsurprising". convert will only convert between types that represent the same basic kind of thing (e.g. different representations of numbers, or different string encodings). It is also usually lossless; converting a value to a different type and back again should result in the exact same value. [...]

    https://docs.julialang.org/en/v1/manual/conversion-and-promotion/index.html
  • Si je comprends bien, T est une méthode de construction du type x, alors que convert essaye de convertir.
    Moralement, il me semble que T(x) ne doit être utilisé que quand le programmeur sait parfaitement ce qu'il y a dans x.
    Quand j'ai lu T() dans des codes, c'était souvent T(1) ou T(0).
Connectez-vous ou Inscrivez-vous pour répondre.
Success message!