Skip to content

Broadcasting

Jake Bolewski edited this page Jun 18, 2021 · 4 revisions

How Julia broadcasting works

Interface: https://docs.julialang.org/en/v1/manual/interfaces/#man-interfaces-broadcasting

  1. broadcasted(f, args...)
  • each arg in args calls arg = broadcastable(arg)
  • call S = combine_styles(args...)
    • this calls s = BroadcastStyle(typeof(arg)) on each arg
    • combines them with S = result_style(s1, s2, ...)
  • call broadcasted(style, f, args...)
    • default returns Broadcasted{S}(f, args)
    • certain cases here are special cased: e.g. adding a Real + Range
  1. regular assignment (a = f.(args...)) will call materialize on the result of 1.
  • For non-Broadcasted objects, this simply returns the object
  • For bc = Broadcasted{S}(f, args), this calls
    • bc = instantiate(bc): constructs or checks the axes:
      • if bc.axes is nothing, calls combine_axes(bc.args...)
        • this calls axes(arg) on each arg
        • combines them with broadcast_shape(ax1, ax2, ...)
          • currently only defined for Tuple arguments
          • handles the array broadcasting semantics
            • length 1 axes can be extended
            • pad out the shape if required
      • if bc.axes is not nothing calls `check_broadcast_axes(bc.axes, bc.args...)
        • this is mainly used for broadcasted assignment (see below)
        • calls check_broadcast_axes(bc.axes, arg) for each arg
          • calls check_broadcast_shape(bc.axes, axes(arg))
            • again, only defined for Tuple arguments
            • checks that axes(arg) is compatible with `bc.axes
    • copy(bc) this first attempts to infer the concrete return type element ElType:
      • if successful, it calls
        • dest = similar(bc, ElType) to construct the output object
        • copyto!(dest, bc)
          • if bc is broadcasting a scalar, it handles it specially
          • otherwise
            • it converts bc = Broadcasted{Nothing}(bc.f, bc.args, bc.axes)
            • calls copyto!(dest, bc)
              • if identity.(arg), calls copyto!(dest, arg)
              • bc = preprocess(destm bc)
                • checks if each arg aliases with dest
                  • if they are exactly equal, it leaves them (since they won't overlap)
                  • otherwise calls unalias(dest, arg) (recursively)
                • bc = extrude(bc)
                  • ???
              • iterates `I in eachindex(bc)
                • dest[I] = bc′[I]
      • if not, it uses widening:
        • it evaluates the first item, and creates an output object with the same element type
        • it calls copyto_nonleaf! to construct the rest and widen as necessary
  1. For broadcasted assignment (dest .= f.(args...)), it calls materialize!(dest, bc):
    • For non-Broadcasted objects, it wraps it in an identity Broadcasted object.
    • For bc = Broadcasted{S}(f, args)
      • DS = combine_styles(dest, bc) (see above)
      • broadcasted!(DS, dest, bc)
        • this constructs a new object dbc = Broadcasted{DS}(bc.f, bc.args, axes(dest))
        • calls dbc = instantiate(dbc) (which checks the axes are compatible)
        • copyto!(dest, dbc)
Clone this wiki locally