@@ -449,6 +449,8 @@ V = view(A, [1,2,4], :) # is not strided, as the spacing between rows is not f
449
449
| ` Base.copy(bc::Broadcasted{DestStyle}) ` | Custom implementation of ` broadcast ` |
450
450
| ` Base.copyto!(dest, bc::Broadcasted{DestStyle}) ` | Custom implementation of ` broadcast! ` , specializing on ` DestStyle ` |
451
451
| ` Base.copyto!(dest::DestType, bc::Broadcasted{Nothing}) ` | Custom implementation of ` broadcast! ` , specializing on ` DestType ` |
452
+ | ` Base.Broadcast.make(f, args...) ` | Override the default lazy behavior within a fused expression |
453
+ | ` Base.Broadcast.instantiate(bc::Broadcasted{DestStyle}) ` | Override the computation of the wrapper's axes and indexers |
452
454
453
455
[ Broadcasting] ( @ref ) is triggered by an explicit call to ` broadcast ` or ` broadcast! ` , or implicitly by
454
456
"dot" operations like ` A .+ b ` or ` f.(x, y) ` . Any object that has [ ` axes ` ] ( @ref ) and supports
@@ -461,16 +463,16 @@ in an `Array`. This basic framework is extensible in three major ways:
461
463
462
464
Not all types support ` axes ` and indexing, but many are convenient to allow in broadcast.
463
465
The [ ` Base.broadcastable ` ] ( @ref ) function is called on each argument to broadcast, allowing
464
- it to return something different that supports ` axes ` and indexing if it does not . By
466
+ it to return something different that supports ` axes ` and indexing. By
465
467
default, this is the identity function for all ` AbstractArray ` s and ` Number ` s — they already
466
468
support ` axes ` and indexing. For a handful of other types (including but not limited to
467
469
types themselves, functions, special singletons like ` missing ` and ` nothing ` , and dates),
468
470
` Base.broadcastable ` returns the argument wrapped in a ` Ref ` to act as a 0-dimensional
469
471
"scalar" for the purposes of broadcasting. Custom types can similarly specialize
470
472
` Base.broadcastable ` to define their shape, but they should follow the convention that
471
- ` collect(Base.broadcastable(x)) == collect(x) ` . A notable exception are ` AbstractString ` s ;
472
- they are special-cased to behave as scalars for the purposes of broadcast even though they
473
- are iterable collections of their characters.
473
+ ` collect(Base.broadcastable(x)) == collect(x) ` . A notable exception is ` AbstractString ` ;
474
+ strings are special-cased to behave as scalars for the purposes of broadcast even though
475
+ they are iterable collections of their characters.
474
476
475
477
The next two steps (selecting the output array and implementation) are dependent upon
476
478
determining a single answer for a given set of arguments. Broadcast must take all the varied
@@ -481,12 +483,11 @@ styles into a single answer — the "destination style".
481
483
482
484
### Broadcast Styles
483
485
484
- ` Base.BroadcastStyle ` is the abstract type from which all styles are
485
- derived. When used as a function it has two possible forms,
486
- unary (single-argument) and binary.
487
- The unary variant states that you intend to
488
- implement specific broadcasting behavior and/or output type,
489
- and do not wish to rely on the default fallback ([ ` Broadcast.DefaultArrayStyle ` ] ( @ref ) ).
486
+ ` Base.BroadcastStyle ` is the abstract type from which all broadcast styles are derived. When used as a
487
+ function it has two possible forms, unary (single-argument) and binary. The unary variant states
488
+ that you intend to implement specific broadcasting behavior and/or output type, and do not wish to
489
+ rely on the default fallback [ ` Broadcast.DefaultArrayStyle ` ] ( @ref ) .
490
+
490
491
To override these defaults, you can define a custom ` BroadcastStyle ` for your object:
491
492
492
493
``` julia
@@ -505,29 +506,30 @@ leverage one of the general broadcast wrappers:
505
506
506
507
When your broadcast operation involves several arguments, individual argument styles get
507
508
combined to determine a single ` DestStyle ` that controls the type of the output container.
508
- For more detail , see [ below] (@ref writing-binary-broadcasting-rules).
509
+ For more details , see [ below] (@ref writing-binary-broadcasting-rules).
509
510
510
511
### Selecting an appropriate output array
511
512
512
- The actual allocation of the result array is handled by ` Base.broadcast_similar ` :
513
+ The broadcast style is computed for every broadcasting operation to allow for
514
+ dispatch and specialization. The actual allocation of the result array is
515
+ handled by ` Base.broadcast_similar ` , using this style as its first argument.
513
516
514
517
``` julia
515
518
Base. broadcast_similar (:: DestStyle , :: Type{ElType} , inds, bc)
516
519
```
517
520
518
- ` DestStyle ` signals the final result from combining the input styles.
519
521
The fallback definition is
520
522
521
523
``` julia
522
524
broadcast_similar (:: DefaultArrayStyle{N} , :: Type{ElType} , inds:: Indices{N} , bc) where {N,ElType} =
523
525
similar (Array{ElType}, inds)
524
526
```
525
527
526
- However, if needed you can specialize on any or all of these arguments.
527
- ` bc ` is the overall ` Broadcasted ` wrapper, available in case allocation of the output requires
528
- access to some of the inputs. For these purposes, the important field of ` Broadcasted ` is called
529
- ` args ` , which stores the inputs as a linked list (a ` TupleLL ` ). ` ll.head ` extracts the first
530
- element, while ` ll.rest ` retrieves the remaining list. The list is terminated by a ` TupleLLEnd() ` .
528
+ However, if needed you can specialize on any or all of these arguments. The final argument
529
+ ` bc ` is a lazy representation of a (potentially fused) broadcast operation, a ` Broadcasted `
530
+ object. For these purposes, the most important fields of the wrapper are
531
+ ` f ` and ` args ` , describing the function and argument list, respectively. Note that the argument
532
+ list can — and often does — include other nested ` Broadcasted ` wrappers .
531
533
532
534
For a complete example, let's say you have created a type, ` ArrayAndChar ` , that stores an
533
535
array and a single character:
564
566
565
567
"`A = find_aac(As)` returns the first ArrayAndChar among the arguments."
566
568
find_aac(bc::Base.Broadcast.Broadcasted) = find_aac(bc.args)
567
- find_aac(ll::Base.TupleLL ) = find_aac(find_aac(ll.head ), ll.rest )
569
+ find_aac(args::Tuple ) = find_aac(find_aac(args[1] ), Base.tail(args) )
568
570
find_aac(x) = x
569
571
find_aac(a::ArrayAndChar, rest) = a
570
572
find_aac(::Any, rest) = find_aac(rest)
@@ -588,21 +590,6 @@ julia> a .+ [5,10]
588
590
13 14
589
591
```
590
592
591
- ### Customizing the broadcast result type
592
-
593
- All ` AbstractArray ` s support broadcasting in arbitrary combinations with one another, but the
594
- default result (output) type is ` Array ` . The ` Broadcasted ` container has a dedicated type parameter
595
- — ` Broadcasted{DestStyle} ` — specifically to allow for dispatch and specialization. It computes
596
- this "broadcast style" by recursively asking every argument for its ` Base.BroadcastStyle ` and
597
- [ combining them together with a promotion-like computation] (@ref writing-binary-broadcasting-rules).
598
-
599
- ` Base.BroadcastStyle ` is an abstract type from which all styles are derived. When used as a
600
- function it has two possible forms, unary (single-argument) and binary. The unary variant states
601
- that you intend to implement specific broadcasting behavior and/or output type, and do not wish to
602
- rely on the default fallback ([ ` Broadcast.Scalar ` ] ( @ref ) or [ ` Broadcast.DefaultArrayStyle ` ] ( @ref ) ).
603
- To achieve this, you can define a custom ` BroadcastStyle ` for your object:
604
-
605
-
606
593
### [ Extending broadcast with custom implementations] (@id extending-in-place-broadcast)
607
594
608
595
In general, a broadcast operation is represented by a lazy ` Broadcasted ` container that holds onto
@@ -618,98 +605,73 @@ it, and then finally copy the realization of the `Broadcasted` object into it wi
618
605
` broadcast! ` methods similarly construct a transient ` Broadcasted ` representation of the operation
619
606
so they can follow the same codepath. This allows custom array implementations to
620
607
provide their own ` copyto! ` specialization to customize and
621
- optimize broadcasting. In order to get to that point, though, custom arrays must first signal the
622
- fact that they should return a custom array from the broadcast operation.
623
-
608
+ optimize broadcasting. This is again determined by the computed broadcast style. This is such
609
+ an important part of the operation that it is stored as the first type parameter of the
610
+ ` Broadcasted ` type, allowing for dispatch and specialization.
624
611
625
612
For some types, the machinery to "fuse" operations across nested levels of broadcasting
626
- is not available. In such cases, you may need to evaluate ` x .* (x .+ 1) ` as if it had been
613
+ is not available or could be done more efficiently incrementally. In such cases, you may
614
+ need or want to evaluate ` x .* (x .+ 1) ` as if it had been
627
615
written ` broadcast(*, x, broadcast(+, x, 1)) ` , where the inner operation is evaluated before
628
- tackling the outer operation. You can force eager evaluation by defining
616
+ tackling the outer operation. This sort of eager operation is directly supported by a bit
617
+ of indirection; instead of directly constructing ` Broadcasted ` objects, Julia lowers the
618
+ fused expression ` x .* (x .+ 1) ` to ` Broadcast.make(*, x, Broadcast.make(+, x, 1)) ` . Now,
619
+ by default, ` make ` just calls the ` Broadcasted ` constructor to create the lazy representation
620
+ of the fused expression tree, but you can choose to override it for a particular combination
621
+ of function and arguments.
622
+
623
+ As an example, the builtin ` AbstractRange ` objects use this machinery to optimize pieces
624
+ of broadcasted expressions that can be eagerly evaluated purely in terms of the start,
625
+ step, and length (or stop) instead of computing every single element. Just like all the
626
+ other machinery, ` make ` also computes and exposes the combined broadcast style of its
627
+ arguments, so instead of specializing on ` make(f, args...) ` , you can specialize on
628
+ ` make(::DestStyle, f, args...) ` for any combination of style, function, and arguments.
629
+
630
+ For example, the following definition supports the negation of ranges:
629
631
630
632
``` julia
631
- is_broadcast_incremental (bc :: Broadcasted{DestStyle} ) = true
633
+ make ( :: DefaultArrayStyle{1} , :: typeof ( - ), r :: OrdinalRange ) = range ( - first (r), step = - step (r), length = length (r))
632
634
```
633
- In such cases you need to supply specific methods
634
- ``` julia
635
- broadcast (f, arg1:: ArgType1 , ... )
636
- ```
637
- for all operations that might be triggered, otherwise the result will be circular and a
638
- ` StackOverflowError ` will result.
639
635
640
- Your definition of ` is_broadcast_incremental ` can be more sophisticated, if necessary;
641
- in particular, you can examine the types of ` bc.args ` if you need to make a more nuanced decision.
642
- As an example, here is the implementation that allows Julia to return ` AbstractRange ` objects
643
- from broadcasting:
636
+ ### [ Extending in-place broadcasting] (@id extending-in-place-broadcast)
644
637
645
- ``` julia
646
- is_broadcast_incremental (bc:: Broadcasted{DefaultArrayStyle{1}} ) = maybe_range_safe (bc)
647
-
648
- # Support incremental evaluation only for 1- or 2-argument broadcasting
649
- # Broadcast.broadcast_all(f_filter, arg_filter, bc) is a function that checks all
650
- # inputs to a nested broadcasting operation, ensuring that the function `f` and
651
- # arguments return `true` for their respective filter functions.
652
- const Args1{T} = TupleLL{T,TupleLLEnd}
653
- const Args2{S,T} = TupleLL{S,TupleLL{T,TupleLLEnd}}
654
- @inline maybe_range_safe (bc:: Broadcasted{Style} ) where {Style<: AbstractArrayStyle{1} } =
655
- Broadcast. broadcast_all (maybe_range_safe_f, maybe_range_safe_arg, bc) && bc. args isa Union{Args1,Args2}
656
-
657
- # Support incremental evaluation only for operations that might return an AbstractRange
658
- maybe_range_safe_f (:: typeof (+ )) = true
659
- maybe_range_safe_f (:: typeof (- )) = true
660
- maybe_range_safe_f (:: typeof (* )) = true
661
- maybe_range_safe_f (:: typeof (/ )) = true
662
- maybe_range_safe_f (:: typeof (\ )) = true
663
- maybe_range_safe_f (f) = false
664
-
665
- maybe_range_safe_arg (:: AbstractRange ) = true
666
- maybe_range_safe_arg (:: Number ) = true
667
- maybe_range_safe_arg (x) = false
668
- ```
669
-
670
- It's then necessary to write ` broadcast ` methods for all 1- and 2-argument versions of operations
671
- involving at least one ` AbstractRange ` and the supported operations ` + ` , ` - ` , ` * ` , ` / ` , and ` \ ` .
672
- For example,
638
+ In-place broadcasting can be supported by defining the appropriate ` copyto!(dest, bc::Broadcasted) `
639
+ method. Because you might want to specialize either on ` dest ` or the specific subtype of ` bc ` ,
640
+ to avoid ambiguities between packages we recommend the following convention.
673
641
642
+ If you wish to specialize on a particular style ` DestStyle ` , define a method for
674
643
``` julia
675
- broadcast ( :: typeof ( - ), r :: OrdinalRange ) = range ( - first (r), - step (r), length (r) )
644
+ copyto! (dest, bc :: Broadcasted{DestStyle} )
676
645
```
677
- to define negation of a range.
678
-
679
- Extending ` broadcast! ` (in-place broadcast) should be done with care, as it is easy to introduce
680
- ambiguities between packages. To avoid these ambiguities, we adhere to the following conventions.
646
+ Optionally, with this form you can also specialize on the type of ` dest ` .
681
647
682
- First, if you want to specialize on the destination type, say ` DestType ` , then you should
683
- define a method with the following signature:
648
+ If instead you want to specialize on the destination type ` DestType ` without specializing
649
+ on ` DestStyle ` , then you should define a method with the following signature:
684
650
685
651
``` julia
686
- broadcast! (f, dest:: DestType , :: Nothing , As ... )
652
+ copyto! ( dest:: DestType , bc :: Broadcasted{ Nothing} )
687
653
```
688
654
689
- Note that no bounds should be placed on the types of ` f ` and ` As... ` .
690
-
691
- Second, if specialized ` broadcast! ` behavior is desired depending on the input types,
692
- you should write [ binary broadcasting rules] (@ref writing-binary-broadcasting-rules) to
693
- determine a custom ` BroadcastStyle ` given the input types, say ` MyBroadcastStyle ` , and you should define a method with the following
694
- signature:
695
-
696
- ``` julia
697
- broadcast! (f, dest, :: MyBroadcastStyle , As... )
698
- ```
655
+ This leverages a fallback implementation of ` copyto! ` that converts the wrapper into a
656
+ ` Broadcasted{Nothing} ` . Consequently, specializing on ` DestType ` has lower precedence than
657
+ methods that specialize on ` DestStyle ` .
699
658
700
- Note the lack of bounds on ` f ` , ` dest ` , and ` As... ` .
659
+ Similarly, you can completely override out-of-place broadcasting with a ` copy(::Broadcasted) `
660
+ method.
701
661
702
- Third, simultaneously specializing on both the type of ` dest ` and the ` BroadcastStyle ` is fine. In this case,
703
- it is also allowed to specialize on the types of the source arguments (` As... ` ). For example, these method signatures are OK:
662
+ #### Working with ` Broadcasted ` objects
704
663
705
- ``` julia
706
- broadcast! (f, dest:: DestType , :: MyBroadcastStyle , As... )
707
- broadcast! (f, dest:: DestType , :: MyBroadcastStyle , As:: AbstractArray... )
708
- broadcast! (f, dest:: DestType , :: Broadcast.DefaultArrayStyle{0} , As:: Number... )
709
- ```
664
+ In order to implement such a ` copy ` or ` copyto! ` , method, of course, you must
665
+ work with the ` Broadcasted ` wrapper to compute each element. There are two main
666
+ ways of doing so:
710
667
668
+ * ` Broadcast.flatten ` recomputes the potentially nested operation into a single
669
+ function and flat list of arguments. You are responsible for implementing the
670
+ broadcasting shape rules yourself, but this may be helpful in limited situations.
671
+ * Iterating over the ` CartesianIndices ` of the ` axes(::Broadcasted) ` and using
672
+ indexing with the resulting ` CartesianIndex ` object to compute the result.
711
673
712
- #### [ Writing binary broadcasting rules] (@id writing-binary-broadcasting-rules)
674
+ ### [ Writing binary broadcasting rules] (@id writing-binary-broadcasting-rules)
713
675
714
676
The precedence rules are defined by binary ` BroadcastStyle ` calls:
715
677
@@ -772,26 +734,3 @@ yields another `SparseVecStyle`, that its combination with a 2-dimensional array
772
734
yields a ` SparseMatStyle ` , and anything of higher dimensionality falls back to the dense arbitrary-dimensional framework.
773
735
These rules allow broadcasting to keep the sparse representation for operations that result
774
736
in one or two dimensional outputs, but produce an ` Array ` for any other dimensionality.
775
-
776
- ### [ Extending in-place broadcasting] (@id extending-in-place-broadcast)
777
-
778
- In-place broadcasting can be supported by defining the appropriate ` copyto!(dest, bc::Broadcasted) `
779
- method. Because you might want to specialize either on ` dest ` or the specific subtype of ` bc ` ,
780
- to avoid ambiguities between packages we recommend the following convention.
781
-
782
- If you wish to specialize on a particular style ` DestStyle ` , define a method for
783
- ``` julia
784
- copyto! (dest, bc:: Broadcasted{DestStyle} )
785
- ```
786
- Optionally, with this form you can also specialize on the type of ` dest ` .
787
-
788
- If instead you want to specialize on the destination type ` DestType ` without specializing
789
- on ` DestStyle ` , then you should define a method with the following signature:
790
-
791
- ``` julia
792
- copyto! (dest:: DestType , bc:: Broadcasted{Nothing} )
793
- ```
794
-
795
- This leverages a fallback implementation of ` copyto! ` that converts the wrapper into a
796
- ` Broadcasted{Nothing} ` . Consequently, specializing on ` DestType ` has lower precedence than
797
- methods that specialize on ` DestStyle ` .
0 commit comments