Bernstein polynomial type for quadratic clipping











up vote
4
down vote

favorite












I would like to implement a Bernstein polynomial class in Julia for learning purposes. The goal in the end is to implement the quadratic clipping algorithm from this paper. I am completely new to Julia coming from Python + Numpy + Numba, so I want to make sure that I program in the canonical Julia way when using Julia.



The naming of variables follows the aforementioned paper.



using Polynomials

type Bernstein
n::Integer
i::Integer
α::Real
β::Real
end
Bernstein(n, i) = Bernstein(n, i, 0., 1.)

Base.string(B::Bernstein) = "Bernstein $(get_p(B))"
Base.show(io::IO, B::Bernstein) = print(io, string(B))
Base.display(io::IO, B::Bernstein) = print(io, string(B))

function get_p(n, i, α=0, β=1)
@assert β >= α "Invalid interval"
return (binomial(n, i)
* poly([α for _ in 1:i])
* poly([β for _ in 1:(n - i)])
* (-1)^(n - i)
/ (β - α)^n)
end
get_p(B::Bernstein) = get_p(B.n, B.i, B.α, B.β)

function ∫(p::Poly, α=NaN, β=NaN)
integrated = polyint(p)
if isequal(α, NaN) || isequal(β, NaN)
return integrated
else
return integrated(β) - integrated(α)
end
end

function inner_product(p::Poly, q::Poly, α=0., β=1.)
@assert β >= α "Invalid interval"
∫(p * q, α, β)
end

function inner_product(B::Bernstein, Q::Bernstein)
@assert B.α >= Q.α "Invalid interval"
@assert B.β >= Q.β "Invalid interval"
m, i, n, j, α, β = B.n, B.i, Q.n, Q.i, B.α, B.β
(β - α)binomial(m, i)binomial(n, j) / ((m + n + 1)binomial(m + n, i + j))
end

# QUESTION: Is it possible to automatically account for commutativity?
(inner_product(p::Poly, B::Bernstein)
= inner_product(B::Bernstein, p::Poly)
= inner_product(B.p, p::Poly))

function Base.LinAlg.norm(q::Poly, α=0., β=1.)
@assert β >= α "Invalid interval"
√inner_product(q, q) / (β - α)
end

function Base.LinAlg.norm(B::Bernstein)
√inner_product(B, B) / (B.β - B.α)
end

B = Bernstein(3, 2)
Q = Bernstein(4, 3)

inner_product(B, Q)
norm(B)









share|improve this question


























    up vote
    4
    down vote

    favorite












    I would like to implement a Bernstein polynomial class in Julia for learning purposes. The goal in the end is to implement the quadratic clipping algorithm from this paper. I am completely new to Julia coming from Python + Numpy + Numba, so I want to make sure that I program in the canonical Julia way when using Julia.



    The naming of variables follows the aforementioned paper.



    using Polynomials

    type Bernstein
    n::Integer
    i::Integer
    α::Real
    β::Real
    end
    Bernstein(n, i) = Bernstein(n, i, 0., 1.)

    Base.string(B::Bernstein) = "Bernstein $(get_p(B))"
    Base.show(io::IO, B::Bernstein) = print(io, string(B))
    Base.display(io::IO, B::Bernstein) = print(io, string(B))

    function get_p(n, i, α=0, β=1)
    @assert β >= α "Invalid interval"
    return (binomial(n, i)
    * poly([α for _ in 1:i])
    * poly([β for _ in 1:(n - i)])
    * (-1)^(n - i)
    / (β - α)^n)
    end
    get_p(B::Bernstein) = get_p(B.n, B.i, B.α, B.β)

    function ∫(p::Poly, α=NaN, β=NaN)
    integrated = polyint(p)
    if isequal(α, NaN) || isequal(β, NaN)
    return integrated
    else
    return integrated(β) - integrated(α)
    end
    end

    function inner_product(p::Poly, q::Poly, α=0., β=1.)
    @assert β >= α "Invalid interval"
    ∫(p * q, α, β)
    end

    function inner_product(B::Bernstein, Q::Bernstein)
    @assert B.α >= Q.α "Invalid interval"
    @assert B.β >= Q.β "Invalid interval"
    m, i, n, j, α, β = B.n, B.i, Q.n, Q.i, B.α, B.β
    (β - α)binomial(m, i)binomial(n, j) / ((m + n + 1)binomial(m + n, i + j))
    end

    # QUESTION: Is it possible to automatically account for commutativity?
    (inner_product(p::Poly, B::Bernstein)
    = inner_product(B::Bernstein, p::Poly)
    = inner_product(B.p, p::Poly))

    function Base.LinAlg.norm(q::Poly, α=0., β=1.)
    @assert β >= α "Invalid interval"
    √inner_product(q, q) / (β - α)
    end

    function Base.LinAlg.norm(B::Bernstein)
    √inner_product(B, B) / (B.β - B.α)
    end

    B = Bernstein(3, 2)
    Q = Bernstein(4, 3)

    inner_product(B, Q)
    norm(B)









    share|improve this question
























      up vote
      4
      down vote

      favorite









      up vote
      4
      down vote

      favorite











      I would like to implement a Bernstein polynomial class in Julia for learning purposes. The goal in the end is to implement the quadratic clipping algorithm from this paper. I am completely new to Julia coming from Python + Numpy + Numba, so I want to make sure that I program in the canonical Julia way when using Julia.



      The naming of variables follows the aforementioned paper.



      using Polynomials

      type Bernstein
      n::Integer
      i::Integer
      α::Real
      β::Real
      end
      Bernstein(n, i) = Bernstein(n, i, 0., 1.)

      Base.string(B::Bernstein) = "Bernstein $(get_p(B))"
      Base.show(io::IO, B::Bernstein) = print(io, string(B))
      Base.display(io::IO, B::Bernstein) = print(io, string(B))

      function get_p(n, i, α=0, β=1)
      @assert β >= α "Invalid interval"
      return (binomial(n, i)
      * poly([α for _ in 1:i])
      * poly([β for _ in 1:(n - i)])
      * (-1)^(n - i)
      / (β - α)^n)
      end
      get_p(B::Bernstein) = get_p(B.n, B.i, B.α, B.β)

      function ∫(p::Poly, α=NaN, β=NaN)
      integrated = polyint(p)
      if isequal(α, NaN) || isequal(β, NaN)
      return integrated
      else
      return integrated(β) - integrated(α)
      end
      end

      function inner_product(p::Poly, q::Poly, α=0., β=1.)
      @assert β >= α "Invalid interval"
      ∫(p * q, α, β)
      end

      function inner_product(B::Bernstein, Q::Bernstein)
      @assert B.α >= Q.α "Invalid interval"
      @assert B.β >= Q.β "Invalid interval"
      m, i, n, j, α, β = B.n, B.i, Q.n, Q.i, B.α, B.β
      (β - α)binomial(m, i)binomial(n, j) / ((m + n + 1)binomial(m + n, i + j))
      end

      # QUESTION: Is it possible to automatically account for commutativity?
      (inner_product(p::Poly, B::Bernstein)
      = inner_product(B::Bernstein, p::Poly)
      = inner_product(B.p, p::Poly))

      function Base.LinAlg.norm(q::Poly, α=0., β=1.)
      @assert β >= α "Invalid interval"
      √inner_product(q, q) / (β - α)
      end

      function Base.LinAlg.norm(B::Bernstein)
      √inner_product(B, B) / (B.β - B.α)
      end

      B = Bernstein(3, 2)
      Q = Bernstein(4, 3)

      inner_product(B, Q)
      norm(B)









      share|improve this question













      I would like to implement a Bernstein polynomial class in Julia for learning purposes. The goal in the end is to implement the quadratic clipping algorithm from this paper. I am completely new to Julia coming from Python + Numpy + Numba, so I want to make sure that I program in the canonical Julia way when using Julia.



      The naming of variables follows the aforementioned paper.



      using Polynomials

      type Bernstein
      n::Integer
      i::Integer
      α::Real
      β::Real
      end
      Bernstein(n, i) = Bernstein(n, i, 0., 1.)

      Base.string(B::Bernstein) = "Bernstein $(get_p(B))"
      Base.show(io::IO, B::Bernstein) = print(io, string(B))
      Base.display(io::IO, B::Bernstein) = print(io, string(B))

      function get_p(n, i, α=0, β=1)
      @assert β >= α "Invalid interval"
      return (binomial(n, i)
      * poly([α for _ in 1:i])
      * poly([β for _ in 1:(n - i)])
      * (-1)^(n - i)
      / (β - α)^n)
      end
      get_p(B::Bernstein) = get_p(B.n, B.i, B.α, B.β)

      function ∫(p::Poly, α=NaN, β=NaN)
      integrated = polyint(p)
      if isequal(α, NaN) || isequal(β, NaN)
      return integrated
      else
      return integrated(β) - integrated(α)
      end
      end

      function inner_product(p::Poly, q::Poly, α=0., β=1.)
      @assert β >= α "Invalid interval"
      ∫(p * q, α, β)
      end

      function inner_product(B::Bernstein, Q::Bernstein)
      @assert B.α >= Q.α "Invalid interval"
      @assert B.β >= Q.β "Invalid interval"
      m, i, n, j, α, β = B.n, B.i, Q.n, Q.i, B.α, B.β
      (β - α)binomial(m, i)binomial(n, j) / ((m + n + 1)binomial(m + n, i + j))
      end

      # QUESTION: Is it possible to automatically account for commutativity?
      (inner_product(p::Poly, B::Bernstein)
      = inner_product(B::Bernstein, p::Poly)
      = inner_product(B.p, p::Poly))

      function Base.LinAlg.norm(q::Poly, α=0., β=1.)
      @assert β >= α "Invalid interval"
      √inner_product(q, q) / (β - α)
      end

      function Base.LinAlg.norm(B::Bernstein)
      √inner_product(B, B) / (B.β - B.α)
      end

      B = Bernstein(3, 2)
      Q = Bernstein(4, 3)

      inner_product(B, Q)
      norm(B)






      beginner julia






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Dec 14 '17 at 10:09









      mcocdawc

      304110




      304110






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          The below code uses Julia 1.0. Full code as a Pkg3 project is available here.



          Preliminaries



          There's enough code for it to get its own module. This also makes testing easier.



          module Bernstein

          using Polynomials

          import Base: convert, promote_rule, show
          import LinearAlgebra: dot, norm
          import Polynomials: poly


          Since we're in a module, we must define the exported things. Since most of the things is just
          overloaded methods, only the type remains.



          export BernsteinPoly


          You do this check so often, we can factor it out. Alternative: using a separate type
          for intervals.



          macro checkinterval(α, β)
          :(@assert $(esc(α)) <= $(esc(β)) "Invalid interval")
          end


          Parametrization by a constrained type is better than fields of an abstract type. But in the case of n and i, concrete Ints make more sense, since they represent natural numbers
          always (this makes conversions below easier).
          And I'm calling it BernsteinPoly, since it is a variant of Poly.



          struct BernsteinPoly{T<:Number}
          n::Int
          i::Int
          α::T
          β::T
          end

          BernsteinPoly(n, i) = BernsteinPoly(n, i, 0.0, 1.0)


          Just implementing show using print is the recommended way for custom pretty-printing. Here, we can reuse Polynomials.printpoly for nicer and more consistent formatting:



          function show(io::IO, b::BernsteinPoly)
          print(io, "BernsteinPoly(")
          Polynomials.printpoly(io, poly(b))
          print(io, ")")
          end


          Conversions



          get_p was essentially the conversion from BernsteinPoly to Poly. We can replace this
          by the implementation of proper convert methods. The default parameters are unnecessary,
          since already occuring defaulted in the BernsteinPoly constructor.



          function convert(::Type{Poly{S}}, b::BernsteinPoly) where {S<:Number}
          n, i, α, β = b.n, b.i, convert(S, b.α), convert(S, b.β)
          @checkinterval α β

          return (binomial(n, i)
          * poly(fill(α, i)) # `fill` instead of list comprehension
          * poly(fill(β, n - i))
          * (-1)^(n - i)
          / (β - α)^n)
          end


          This allows you to just say convert(Poly, b), automatically reusing the inner type of b



          convert(::Type{Poly}, b::BernsteinPoly{T}) where {T<:Number} =
          convert(Poly{T}, b)


          While we're at it: conversion between different BernsteinPoly values



          convert(::Type{BernsteinPoly{S}}, b::BernsteinPoly) where {S<:Number} =
          BernsteinPoly(b.n, b.i, convert(S, b.α), convert(S, b.β))


          If we're handling different representations, we sometimes need to determine the "most general"
          form, which is called promotion:



          promote_rule(a::Type{BernsteinPoly{S}}, b::Type{Poly{T}}) where {S<:Number, T<:Number} =
          Poly{promote_type(S, T)}

          promote_rule(::Type{BernsteinPoly{S}}, ::Type{BernsteinPoly{T}}) where {S<:Number, T<:Number} =
          BernsteinPoly{promote_type(S, T)}


          Also add a method to the poly smart constuctor, which is now trivial:



          poly(b::BernsteinPoly) = convert(Poly, b)


          Linear Algebra



          Now to the linear algebra part. We could add methods to dot and norm from LinearAlgebra:



          function dot(p::Poly{T}, q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
          @checkinterval α β
          polyint(p * q, α, β)
          end


          And the norm induced by that inner product:



          function norm(q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
          @checkinterval α β
          √dot(q, q, α, β) / (β - α)
          end


          But such "overloads from outside" are frowned upon. As of writing this, there's a norm method
          in Polynomials, but not dot. There's an issue about that; basically, dot is not
          unique, hence we need to specify the intervals each time here.



          For Bernstein polynomials, on the other hand, the inner product is defined uniquely, if I
          understood correctly.



          function dot(b::BernsteinPoly{T}, q::BernsteinPoly{T}) where {T<:Number}
          @checkinterval b.α q.α
          @checkinterval b.β q.β
          m, i, n, j, α, β = b.n, b.i, q.n, q.i, b.α, b.β
          (β - α) * binomial(m, i) * binomial(n, j) / ((m + n + 1) * binomial(m + n, i + j))
          end


          And the induced norm, as before.



          function norm(b::BernsteinPoly)
          √dot(b, b) / (b.β - b.α)
          end


          As the last remaining question, how to do cross-type inner products. If dot is defined for
          Poly as written above, we can use dot(promote(p, q)..., α, β). But it's difficult to get
          that working as a method. I tried



          dot(p::Union{Poly{T}, BernsteinPoly{T}}, q::Union{Poly{T}, BernsteinPoly{T}},
          α = zero(T), β = one(T)) where {T<:Number} =
          dot(promote(p, q)..., α, β)


          but that doesn't work. It's probably not recommended to do that anyway. Use promote
          explicitely where necessary.



          Example output



          julia> B = BernsteinPoly(3, 2)
          BernsteinPoly(3.0*x^2 - 3.0*x^3)

          julia> Q = BernsteinPoly(4, 3)
          BernsteinPoly(4.0*x^3 - 4.0*x^4)

          julia> using LinearAlgebra

          julia> dot(B, Q)
          0.07142857142857142

          julia> norm(B)
          0.29277002188455997


          end # module





          share|improve this answer





















            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f182759%2fbernstein-polynomial-type-for-quadratic-clipping%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote













            The below code uses Julia 1.0. Full code as a Pkg3 project is available here.



            Preliminaries



            There's enough code for it to get its own module. This also makes testing easier.



            module Bernstein

            using Polynomials

            import Base: convert, promote_rule, show
            import LinearAlgebra: dot, norm
            import Polynomials: poly


            Since we're in a module, we must define the exported things. Since most of the things is just
            overloaded methods, only the type remains.



            export BernsteinPoly


            You do this check so often, we can factor it out. Alternative: using a separate type
            for intervals.



            macro checkinterval(α, β)
            :(@assert $(esc(α)) <= $(esc(β)) "Invalid interval")
            end


            Parametrization by a constrained type is better than fields of an abstract type. But in the case of n and i, concrete Ints make more sense, since they represent natural numbers
            always (this makes conversions below easier).
            And I'm calling it BernsteinPoly, since it is a variant of Poly.



            struct BernsteinPoly{T<:Number}
            n::Int
            i::Int
            α::T
            β::T
            end

            BernsteinPoly(n, i) = BernsteinPoly(n, i, 0.0, 1.0)


            Just implementing show using print is the recommended way for custom pretty-printing. Here, we can reuse Polynomials.printpoly for nicer and more consistent formatting:



            function show(io::IO, b::BernsteinPoly)
            print(io, "BernsteinPoly(")
            Polynomials.printpoly(io, poly(b))
            print(io, ")")
            end


            Conversions



            get_p was essentially the conversion from BernsteinPoly to Poly. We can replace this
            by the implementation of proper convert methods. The default parameters are unnecessary,
            since already occuring defaulted in the BernsteinPoly constructor.



            function convert(::Type{Poly{S}}, b::BernsteinPoly) where {S<:Number}
            n, i, α, β = b.n, b.i, convert(S, b.α), convert(S, b.β)
            @checkinterval α β

            return (binomial(n, i)
            * poly(fill(α, i)) # `fill` instead of list comprehension
            * poly(fill(β, n - i))
            * (-1)^(n - i)
            / (β - α)^n)
            end


            This allows you to just say convert(Poly, b), automatically reusing the inner type of b



            convert(::Type{Poly}, b::BernsteinPoly{T}) where {T<:Number} =
            convert(Poly{T}, b)


            While we're at it: conversion between different BernsteinPoly values



            convert(::Type{BernsteinPoly{S}}, b::BernsteinPoly) where {S<:Number} =
            BernsteinPoly(b.n, b.i, convert(S, b.α), convert(S, b.β))


            If we're handling different representations, we sometimes need to determine the "most general"
            form, which is called promotion:



            promote_rule(a::Type{BernsteinPoly{S}}, b::Type{Poly{T}}) where {S<:Number, T<:Number} =
            Poly{promote_type(S, T)}

            promote_rule(::Type{BernsteinPoly{S}}, ::Type{BernsteinPoly{T}}) where {S<:Number, T<:Number} =
            BernsteinPoly{promote_type(S, T)}


            Also add a method to the poly smart constuctor, which is now trivial:



            poly(b::BernsteinPoly) = convert(Poly, b)


            Linear Algebra



            Now to the linear algebra part. We could add methods to dot and norm from LinearAlgebra:



            function dot(p::Poly{T}, q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
            @checkinterval α β
            polyint(p * q, α, β)
            end


            And the norm induced by that inner product:



            function norm(q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
            @checkinterval α β
            √dot(q, q, α, β) / (β - α)
            end


            But such "overloads from outside" are frowned upon. As of writing this, there's a norm method
            in Polynomials, but not dot. There's an issue about that; basically, dot is not
            unique, hence we need to specify the intervals each time here.



            For Bernstein polynomials, on the other hand, the inner product is defined uniquely, if I
            understood correctly.



            function dot(b::BernsteinPoly{T}, q::BernsteinPoly{T}) where {T<:Number}
            @checkinterval b.α q.α
            @checkinterval b.β q.β
            m, i, n, j, α, β = b.n, b.i, q.n, q.i, b.α, b.β
            (β - α) * binomial(m, i) * binomial(n, j) / ((m + n + 1) * binomial(m + n, i + j))
            end


            And the induced norm, as before.



            function norm(b::BernsteinPoly)
            √dot(b, b) / (b.β - b.α)
            end


            As the last remaining question, how to do cross-type inner products. If dot is defined for
            Poly as written above, we can use dot(promote(p, q)..., α, β). But it's difficult to get
            that working as a method. I tried



            dot(p::Union{Poly{T}, BernsteinPoly{T}}, q::Union{Poly{T}, BernsteinPoly{T}},
            α = zero(T), β = one(T)) where {T<:Number} =
            dot(promote(p, q)..., α, β)


            but that doesn't work. It's probably not recommended to do that anyway. Use promote
            explicitely where necessary.



            Example output



            julia> B = BernsteinPoly(3, 2)
            BernsteinPoly(3.0*x^2 - 3.0*x^3)

            julia> Q = BernsteinPoly(4, 3)
            BernsteinPoly(4.0*x^3 - 4.0*x^4)

            julia> using LinearAlgebra

            julia> dot(B, Q)
            0.07142857142857142

            julia> norm(B)
            0.29277002188455997


            end # module





            share|improve this answer

























              up vote
              0
              down vote













              The below code uses Julia 1.0. Full code as a Pkg3 project is available here.



              Preliminaries



              There's enough code for it to get its own module. This also makes testing easier.



              module Bernstein

              using Polynomials

              import Base: convert, promote_rule, show
              import LinearAlgebra: dot, norm
              import Polynomials: poly


              Since we're in a module, we must define the exported things. Since most of the things is just
              overloaded methods, only the type remains.



              export BernsteinPoly


              You do this check so often, we can factor it out. Alternative: using a separate type
              for intervals.



              macro checkinterval(α, β)
              :(@assert $(esc(α)) <= $(esc(β)) "Invalid interval")
              end


              Parametrization by a constrained type is better than fields of an abstract type. But in the case of n and i, concrete Ints make more sense, since they represent natural numbers
              always (this makes conversions below easier).
              And I'm calling it BernsteinPoly, since it is a variant of Poly.



              struct BernsteinPoly{T<:Number}
              n::Int
              i::Int
              α::T
              β::T
              end

              BernsteinPoly(n, i) = BernsteinPoly(n, i, 0.0, 1.0)


              Just implementing show using print is the recommended way for custom pretty-printing. Here, we can reuse Polynomials.printpoly for nicer and more consistent formatting:



              function show(io::IO, b::BernsteinPoly)
              print(io, "BernsteinPoly(")
              Polynomials.printpoly(io, poly(b))
              print(io, ")")
              end


              Conversions



              get_p was essentially the conversion from BernsteinPoly to Poly. We can replace this
              by the implementation of proper convert methods. The default parameters are unnecessary,
              since already occuring defaulted in the BernsteinPoly constructor.



              function convert(::Type{Poly{S}}, b::BernsteinPoly) where {S<:Number}
              n, i, α, β = b.n, b.i, convert(S, b.α), convert(S, b.β)
              @checkinterval α β

              return (binomial(n, i)
              * poly(fill(α, i)) # `fill` instead of list comprehension
              * poly(fill(β, n - i))
              * (-1)^(n - i)
              / (β - α)^n)
              end


              This allows you to just say convert(Poly, b), automatically reusing the inner type of b



              convert(::Type{Poly}, b::BernsteinPoly{T}) where {T<:Number} =
              convert(Poly{T}, b)


              While we're at it: conversion between different BernsteinPoly values



              convert(::Type{BernsteinPoly{S}}, b::BernsteinPoly) where {S<:Number} =
              BernsteinPoly(b.n, b.i, convert(S, b.α), convert(S, b.β))


              If we're handling different representations, we sometimes need to determine the "most general"
              form, which is called promotion:



              promote_rule(a::Type{BernsteinPoly{S}}, b::Type{Poly{T}}) where {S<:Number, T<:Number} =
              Poly{promote_type(S, T)}

              promote_rule(::Type{BernsteinPoly{S}}, ::Type{BernsteinPoly{T}}) where {S<:Number, T<:Number} =
              BernsteinPoly{promote_type(S, T)}


              Also add a method to the poly smart constuctor, which is now trivial:



              poly(b::BernsteinPoly) = convert(Poly, b)


              Linear Algebra



              Now to the linear algebra part. We could add methods to dot and norm from LinearAlgebra:



              function dot(p::Poly{T}, q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
              @checkinterval α β
              polyint(p * q, α, β)
              end


              And the norm induced by that inner product:



              function norm(q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
              @checkinterval α β
              √dot(q, q, α, β) / (β - α)
              end


              But such "overloads from outside" are frowned upon. As of writing this, there's a norm method
              in Polynomials, but not dot. There's an issue about that; basically, dot is not
              unique, hence we need to specify the intervals each time here.



              For Bernstein polynomials, on the other hand, the inner product is defined uniquely, if I
              understood correctly.



              function dot(b::BernsteinPoly{T}, q::BernsteinPoly{T}) where {T<:Number}
              @checkinterval b.α q.α
              @checkinterval b.β q.β
              m, i, n, j, α, β = b.n, b.i, q.n, q.i, b.α, b.β
              (β - α) * binomial(m, i) * binomial(n, j) / ((m + n + 1) * binomial(m + n, i + j))
              end


              And the induced norm, as before.



              function norm(b::BernsteinPoly)
              √dot(b, b) / (b.β - b.α)
              end


              As the last remaining question, how to do cross-type inner products. If dot is defined for
              Poly as written above, we can use dot(promote(p, q)..., α, β). But it's difficult to get
              that working as a method. I tried



              dot(p::Union{Poly{T}, BernsteinPoly{T}}, q::Union{Poly{T}, BernsteinPoly{T}},
              α = zero(T), β = one(T)) where {T<:Number} =
              dot(promote(p, q)..., α, β)


              but that doesn't work. It's probably not recommended to do that anyway. Use promote
              explicitely where necessary.



              Example output



              julia> B = BernsteinPoly(3, 2)
              BernsteinPoly(3.0*x^2 - 3.0*x^3)

              julia> Q = BernsteinPoly(4, 3)
              BernsteinPoly(4.0*x^3 - 4.0*x^4)

              julia> using LinearAlgebra

              julia> dot(B, Q)
              0.07142857142857142

              julia> norm(B)
              0.29277002188455997


              end # module





              share|improve this answer























                up vote
                0
                down vote










                up vote
                0
                down vote









                The below code uses Julia 1.0. Full code as a Pkg3 project is available here.



                Preliminaries



                There's enough code for it to get its own module. This also makes testing easier.



                module Bernstein

                using Polynomials

                import Base: convert, promote_rule, show
                import LinearAlgebra: dot, norm
                import Polynomials: poly


                Since we're in a module, we must define the exported things. Since most of the things is just
                overloaded methods, only the type remains.



                export BernsteinPoly


                You do this check so often, we can factor it out. Alternative: using a separate type
                for intervals.



                macro checkinterval(α, β)
                :(@assert $(esc(α)) <= $(esc(β)) "Invalid interval")
                end


                Parametrization by a constrained type is better than fields of an abstract type. But in the case of n and i, concrete Ints make more sense, since they represent natural numbers
                always (this makes conversions below easier).
                And I'm calling it BernsteinPoly, since it is a variant of Poly.



                struct BernsteinPoly{T<:Number}
                n::Int
                i::Int
                α::T
                β::T
                end

                BernsteinPoly(n, i) = BernsteinPoly(n, i, 0.0, 1.0)


                Just implementing show using print is the recommended way for custom pretty-printing. Here, we can reuse Polynomials.printpoly for nicer and more consistent formatting:



                function show(io::IO, b::BernsteinPoly)
                print(io, "BernsteinPoly(")
                Polynomials.printpoly(io, poly(b))
                print(io, ")")
                end


                Conversions



                get_p was essentially the conversion from BernsteinPoly to Poly. We can replace this
                by the implementation of proper convert methods. The default parameters are unnecessary,
                since already occuring defaulted in the BernsteinPoly constructor.



                function convert(::Type{Poly{S}}, b::BernsteinPoly) where {S<:Number}
                n, i, α, β = b.n, b.i, convert(S, b.α), convert(S, b.β)
                @checkinterval α β

                return (binomial(n, i)
                * poly(fill(α, i)) # `fill` instead of list comprehension
                * poly(fill(β, n - i))
                * (-1)^(n - i)
                / (β - α)^n)
                end


                This allows you to just say convert(Poly, b), automatically reusing the inner type of b



                convert(::Type{Poly}, b::BernsteinPoly{T}) where {T<:Number} =
                convert(Poly{T}, b)


                While we're at it: conversion between different BernsteinPoly values



                convert(::Type{BernsteinPoly{S}}, b::BernsteinPoly) where {S<:Number} =
                BernsteinPoly(b.n, b.i, convert(S, b.α), convert(S, b.β))


                If we're handling different representations, we sometimes need to determine the "most general"
                form, which is called promotion:



                promote_rule(a::Type{BernsteinPoly{S}}, b::Type{Poly{T}}) where {S<:Number, T<:Number} =
                Poly{promote_type(S, T)}

                promote_rule(::Type{BernsteinPoly{S}}, ::Type{BernsteinPoly{T}}) where {S<:Number, T<:Number} =
                BernsteinPoly{promote_type(S, T)}


                Also add a method to the poly smart constuctor, which is now trivial:



                poly(b::BernsteinPoly) = convert(Poly, b)


                Linear Algebra



                Now to the linear algebra part. We could add methods to dot and norm from LinearAlgebra:



                function dot(p::Poly{T}, q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
                @checkinterval α β
                polyint(p * q, α, β)
                end


                And the norm induced by that inner product:



                function norm(q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
                @checkinterval α β
                √dot(q, q, α, β) / (β - α)
                end


                But such "overloads from outside" are frowned upon. As of writing this, there's a norm method
                in Polynomials, but not dot. There's an issue about that; basically, dot is not
                unique, hence we need to specify the intervals each time here.



                For Bernstein polynomials, on the other hand, the inner product is defined uniquely, if I
                understood correctly.



                function dot(b::BernsteinPoly{T}, q::BernsteinPoly{T}) where {T<:Number}
                @checkinterval b.α q.α
                @checkinterval b.β q.β
                m, i, n, j, α, β = b.n, b.i, q.n, q.i, b.α, b.β
                (β - α) * binomial(m, i) * binomial(n, j) / ((m + n + 1) * binomial(m + n, i + j))
                end


                And the induced norm, as before.



                function norm(b::BernsteinPoly)
                √dot(b, b) / (b.β - b.α)
                end


                As the last remaining question, how to do cross-type inner products. If dot is defined for
                Poly as written above, we can use dot(promote(p, q)..., α, β). But it's difficult to get
                that working as a method. I tried



                dot(p::Union{Poly{T}, BernsteinPoly{T}}, q::Union{Poly{T}, BernsteinPoly{T}},
                α = zero(T), β = one(T)) where {T<:Number} =
                dot(promote(p, q)..., α, β)


                but that doesn't work. It's probably not recommended to do that anyway. Use promote
                explicitely where necessary.



                Example output



                julia> B = BernsteinPoly(3, 2)
                BernsteinPoly(3.0*x^2 - 3.0*x^3)

                julia> Q = BernsteinPoly(4, 3)
                BernsteinPoly(4.0*x^3 - 4.0*x^4)

                julia> using LinearAlgebra

                julia> dot(B, Q)
                0.07142857142857142

                julia> norm(B)
                0.29277002188455997


                end # module





                share|improve this answer












                The below code uses Julia 1.0. Full code as a Pkg3 project is available here.



                Preliminaries



                There's enough code for it to get its own module. This also makes testing easier.



                module Bernstein

                using Polynomials

                import Base: convert, promote_rule, show
                import LinearAlgebra: dot, norm
                import Polynomials: poly


                Since we're in a module, we must define the exported things. Since most of the things is just
                overloaded methods, only the type remains.



                export BernsteinPoly


                You do this check so often, we can factor it out. Alternative: using a separate type
                for intervals.



                macro checkinterval(α, β)
                :(@assert $(esc(α)) <= $(esc(β)) "Invalid interval")
                end


                Parametrization by a constrained type is better than fields of an abstract type. But in the case of n and i, concrete Ints make more sense, since they represent natural numbers
                always (this makes conversions below easier).
                And I'm calling it BernsteinPoly, since it is a variant of Poly.



                struct BernsteinPoly{T<:Number}
                n::Int
                i::Int
                α::T
                β::T
                end

                BernsteinPoly(n, i) = BernsteinPoly(n, i, 0.0, 1.0)


                Just implementing show using print is the recommended way for custom pretty-printing. Here, we can reuse Polynomials.printpoly for nicer and more consistent formatting:



                function show(io::IO, b::BernsteinPoly)
                print(io, "BernsteinPoly(")
                Polynomials.printpoly(io, poly(b))
                print(io, ")")
                end


                Conversions



                get_p was essentially the conversion from BernsteinPoly to Poly. We can replace this
                by the implementation of proper convert methods. The default parameters are unnecessary,
                since already occuring defaulted in the BernsteinPoly constructor.



                function convert(::Type{Poly{S}}, b::BernsteinPoly) where {S<:Number}
                n, i, α, β = b.n, b.i, convert(S, b.α), convert(S, b.β)
                @checkinterval α β

                return (binomial(n, i)
                * poly(fill(α, i)) # `fill` instead of list comprehension
                * poly(fill(β, n - i))
                * (-1)^(n - i)
                / (β - α)^n)
                end


                This allows you to just say convert(Poly, b), automatically reusing the inner type of b



                convert(::Type{Poly}, b::BernsteinPoly{T}) where {T<:Number} =
                convert(Poly{T}, b)


                While we're at it: conversion between different BernsteinPoly values



                convert(::Type{BernsteinPoly{S}}, b::BernsteinPoly) where {S<:Number} =
                BernsteinPoly(b.n, b.i, convert(S, b.α), convert(S, b.β))


                If we're handling different representations, we sometimes need to determine the "most general"
                form, which is called promotion:



                promote_rule(a::Type{BernsteinPoly{S}}, b::Type{Poly{T}}) where {S<:Number, T<:Number} =
                Poly{promote_type(S, T)}

                promote_rule(::Type{BernsteinPoly{S}}, ::Type{BernsteinPoly{T}}) where {S<:Number, T<:Number} =
                BernsteinPoly{promote_type(S, T)}


                Also add a method to the poly smart constuctor, which is now trivial:



                poly(b::BernsteinPoly) = convert(Poly, b)


                Linear Algebra



                Now to the linear algebra part. We could add methods to dot and norm from LinearAlgebra:



                function dot(p::Poly{T}, q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
                @checkinterval α β
                polyint(p * q, α, β)
                end


                And the norm induced by that inner product:



                function norm(q::Poly{T}, α = zero(T), β = one(T)) where {T<:Number}
                @checkinterval α β
                √dot(q, q, α, β) / (β - α)
                end


                But such "overloads from outside" are frowned upon. As of writing this, there's a norm method
                in Polynomials, but not dot. There's an issue about that; basically, dot is not
                unique, hence we need to specify the intervals each time here.



                For Bernstein polynomials, on the other hand, the inner product is defined uniquely, if I
                understood correctly.



                function dot(b::BernsteinPoly{T}, q::BernsteinPoly{T}) where {T<:Number}
                @checkinterval b.α q.α
                @checkinterval b.β q.β
                m, i, n, j, α, β = b.n, b.i, q.n, q.i, b.α, b.β
                (β - α) * binomial(m, i) * binomial(n, j) / ((m + n + 1) * binomial(m + n, i + j))
                end


                And the induced norm, as before.



                function norm(b::BernsteinPoly)
                √dot(b, b) / (b.β - b.α)
                end


                As the last remaining question, how to do cross-type inner products. If dot is defined for
                Poly as written above, we can use dot(promote(p, q)..., α, β). But it's difficult to get
                that working as a method. I tried



                dot(p::Union{Poly{T}, BernsteinPoly{T}}, q::Union{Poly{T}, BernsteinPoly{T}},
                α = zero(T), β = one(T)) where {T<:Number} =
                dot(promote(p, q)..., α, β)


                but that doesn't work. It's probably not recommended to do that anyway. Use promote
                explicitely where necessary.



                Example output



                julia> B = BernsteinPoly(3, 2)
                BernsteinPoly(3.0*x^2 - 3.0*x^3)

                julia> Q = BernsteinPoly(4, 3)
                BernsteinPoly(4.0*x^3 - 4.0*x^4)

                julia> using LinearAlgebra

                julia> dot(B, Q)
                0.07142857142857142

                julia> norm(B)
                0.29277002188455997


                end # module






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 5 hours ago









                phg

                1213




                1213






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f182759%2fbernstein-polynomial-type-for-quadratic-clipping%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Morgemoulin

                    Scott Moir

                    Souastre