32namespace detail::cuda_reference
35template <
class Mdspec>
using transform_scalar_t = std::remove_cv_t<typename std::remove_cvref_t<Mdspec>::element_type>;
37template <
class OutputMdspec,
class... InputMdspecs>
38concept RawCudaOverwriteMdspecs =
40 is_raw_mutable_cuda_mdspec<OutputMdspec> && (is_raw_const_cuda_mdspec<InputMdspecs> && ...) &&
41 ((std::remove_cvref_t<OutputMdspec>::rank() == std::remove_cvref_t<InputMdspecs>::rank()) && ...) &&
42 (std::same_as<transform_scalar_t<OutputMdspec>, transform_scalar_t<InputMdspecs>> && ...) &&
43 (std::remove_cvref_t<OutputMdspec>::rank() == 0 ||
46template <
class OutputMdspec,
class Value>
47concept SupportedFillMdspec = RawCudaOverwriteMdspecs<OutputMdspec> &&
48 std::same_as<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Value>> &&
49 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
51template <
class OutputMdspec,
class InputMdspec,
class Function>
52concept SupportedStatelessUnaryMdspecs =
53 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> && RegisteredStatelessUnary<Function> &&
54 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
56template <
class OutputMdspec,
class InputMdspec,
class Factor>
57concept SupportedScaleMdspecs =
58 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> &&
59 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
61template <
class OutputMdspec,
class LhsMdspec,
class RhsMdspec,
class Function>
62concept SupportedStatelessBinaryMdspecs =
63 RawCudaOverwriteMdspecs<OutputMdspec, LhsMdspec, RhsMdspec> && RegisteredStatelessBinary<Function> &&
64 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
66template <
class OutputMdspec,
class Factor>
67concept SupportedInplaceScaleMdspec =
68 RawCudaOverwriteMdspecs<OutputMdspec> &&
69 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
71template <
class OutputMdspec,
class InputMdspec,
class Function>
72concept SupportedInplaceStatelessBinaryMdspecs =
73 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> && RegisteredStatelessBinary<Function> &&
74 supports_elementwise_arithmetic<transform_scalar_t<OutputMdspec>>;
76template <
class OutputMdspec,
class InputMdspec,
class Factor>
77concept SupportedAddScaledMdspecs =
78 RawCudaOverwriteMdspecs<OutputMdspec, InputMdspec> &&
79 supports_elementwise_scale<transform_scalar_t<OutputMdspec>, std::remove_cvref_t<Factor>>;
81template <
class Scalar, std::
size_t InputCount, std::
size_t MaximumRank>
struct OverwriteTransformPlan
83 using scalar_type = Scalar;
84 static constexpr std::size_t input_count = InputCount;
85 static constexpr std::size_t maximum_rank = MaximumRank;
89 std::array<uni20::cuda::CudaBuffer<Scalar>
const*, InputCount> input_buffers{};
90 std::size_t output_offset = 0;
91 std::array<std::size_t, InputCount> input_offsets{};
92 std::size_t output_span_size = 0;
93 std::array<std::size_t, InputCount> input_span_sizes{};
94 LoweredStridedElementwisePlan<InputCount + 1, MaximumRank> elementwise_plan;
95 bool has_work =
false;
98template <
class Scalar>
using FillPlan = OverwriteTransformPlan<Scalar, 0, elementwise_arithmetic_maximum_rank>;
100template <
class Scalar>
101using UnaryArithmeticPlan = OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>;
103template <
class Scalar,
class Factor>
104struct ScalePlan : OverwriteTransformPlan<Scalar, 1, elementwise_scale_maximum_rank>
109template <
class Scalar>
110using BinaryArithmeticPlan = OverwriteTransformPlan<Scalar, 2, elementwise_arithmetic_maximum_rank>;
112template <
class Scalar,
class Factor>
113struct InplaceScalePlan : OverwriteTransformPlan<Scalar, 0, elementwise_arithmetic_maximum_rank>
118template <
class Scalar>
119using InplaceBinaryPlan = OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>;
121template <
class Scalar,
class Factor>
122struct AddScaledPlan : OverwriteTransformPlan<Scalar, 1, elementwise_arithmetic_maximum_rank>
127template <std::size_t MaximumRank,
class OutputMdspec,
class... InputMdspecs>
129 ((std::remove_cvref_t<OutputMdspec>::rank() == std::remove_cvref_t<InputMdspecs>::rank()) && ...) &&
130 (std::remove_cvref_t<OutputMdspec>::rank() == 0 ||
132[[nodiscard]]
bool try_make_elementwise_transform_plan(
133 OutputMdspec
const& output, LoweredStridedElementwisePlan<
sizeof...(InputMdspecs) + 1, MaximumRank>& plan,
134 InputMdspecs
const&... inputs)
137 return try_lower_strided_elementwise_plan<MaximumRank>(host_plan, plan);
140template <
class Mdspec> [[nodiscard]]
auto required_span_size(Mdspec
const& mdspec) -> std::optional<std::size_t>
142 auto const required = mdspec.mapping().required_span_size();
143 if (std::cmp_less(required, 0) || std::cmp_greater(required, std::numeric_limits<std::size_t>::max()))
145 return static_cast<std::size_t
>(required);
148template <
class Plan,
class OutputMdspec,
class... InputMdspecs>
149 requires RawCudaOverwriteMdspecs<OutputMdspec, InputMdspecs...> && (Plan::input_count ==
sizeof...(InputMdspecs))
150[[nodiscard]]
auto prepare_overwrite_transform(Plan plan, OutputMdspec& output, InputMdspecs&... inputs)
152 using output_type = std::remove_cvref_t<OutputMdspec>;
154 if constexpr (
sizeof...(InputMdspecs) > 0)
156 bool extents_match =
true;
157 auto check_extents = [&](
auto const& input) {
158 for (std::size_t axis = 0; axis < output_type::rank(); ++axis)
160 if (output.extent(axis) != input.extent(axis)) extents_match =
false;
163 (check_extents(inputs), ...);
171 if (!output.mapping().is_unique() ||
172 !try_make_elementwise_transform_plan<Plan::maximum_rank>(output, plan.elementwise_plan, inputs...))
178 plan.has_work = plan.elementwise_plan.element_count() != 0;
185 auto const output_span_size = required_span_size(output);
186 if (!output_span_size)
191 plan.output_span_size = *output_span_size;
193 auto output_descriptor = output.data_descriptor();
194 plan.output_buffer = std::addressof(output_descriptor.buffer());
195 plan.output_offset = output_descriptor.element_offset();
196 CHECK(plan.output_offset <= plan.output_buffer->size() &&
197 plan.output_span_size <= plan.output_buffer->size() - plan.output_offset,
198 plan.output_offset, plan.output_span_size, plan.output_buffer->size());
200 if constexpr (
sizeof...(InputMdspecs) > 0)
202 std::size_t input_index = 0;
203 auto capture_input = [&](
auto& input) {
204 auto const input_span_size = required_span_size(input);
205 if (!input_span_size)
return false;
207 auto input_descriptor = input.data_descriptor();
208 auto const* input_buffer = std::addressof(input_descriptor.buffer());
209 auto const input_offset = input_descriptor.element_offset();
210 CHECK(input_offset <= input_buffer->
size() && *input_span_size <= input_buffer->
size() - input_offset,
211 input_offset, *input_span_size, input_buffer->size());
213 plan.input_buffers[input_index] = input_buffer;
214 plan.input_offsets[input_index] = input_offset;
215 plan.input_span_sizes[input_index] = *input_span_size;
219 if (!(capture_input(inputs) && ...))
226 for (std::size_t input = 0; input < Plan::input_count; ++input)
228 if (plan.output_buffer->device() != plan.input_buffers[input]->device())
233 if (plan.output_buffer == plan.input_buffers[input] && plan.output_offset == plan.input_offsets[input])
244template <
class Function,
class OutputMdspec,
class InputMdspec>
245 requires SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>
246[[nodiscard]]
auto prepare_stateless_unary(OutputMdspec& output, InputMdspec& input)
248 using scalar_type = transform_scalar_t<OutputMdspec>;
249 return prepare_overwrite_transform(UnaryArithmeticPlan<scalar_type>{}, output, input);
252template <
class OutputMdspec,
class Value>
253 requires SupportedFillMdspec<OutputMdspec, Value>
254[[nodiscard]]
auto prepare_fill(OutputMdspec& output)
256 using scalar_type = transform_scalar_t<OutputMdspec>;
257 return prepare_overwrite_transform(FillPlan<scalar_type>{}, output);
260template <
class OutputMdspec,
class InputMdspec,
class Factor>
261 requires SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>
262[[nodiscard]]
auto prepare_scale(OutputMdspec& output, InputMdspec& input, Factor factor)
264 using scalar_type = transform_scalar_t<OutputMdspec>;
265 using factor_type = std::remove_cvref_t<Factor>;
266 ScalePlan<scalar_type, factor_type> plan;
267 plan.factor = std::move(factor);
268 return prepare_overwrite_transform(std::move(plan), output, input);
271template <
class OutputMdspec,
class Factor>
272 requires SupportedInplaceScaleMdspec<OutputMdspec, Factor>
273[[nodiscard]]
auto prepare_inplace_scale(OutputMdspec& output, Factor factor)
275 using scalar_type = transform_scalar_t<OutputMdspec>;
276 using factor_type = std::remove_cvref_t<Factor>;
277 InplaceScalePlan<scalar_type, factor_type> plan;
278 plan.factor = std::move(factor);
279 return prepare_overwrite_transform(std::move(plan), output);
282template <
class Function,
class OutputMdspec,
class InputMdspec>
283 requires SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>
284[[nodiscard]]
auto prepare_inplace_stateless_binary(OutputMdspec& output, InputMdspec& input)
286 using scalar_type = transform_scalar_t<OutputMdspec>;
287 return prepare_overwrite_transform(InplaceBinaryPlan<scalar_type>{}, output, input);
290template <
class OutputMdspec,
class InputMdspec,
class Factor>
291 requires SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>
292[[nodiscard]]
auto prepare_add_scaled(OutputMdspec& output, InputMdspec& input, Factor factor)
294 using scalar_type = transform_scalar_t<OutputMdspec>;
295 using factor_type = std::remove_cvref_t<Factor>;
296 AddScaledPlan<scalar_type, factor_type> plan;
297 plan.factor = std::move(factor);
298 return prepare_overwrite_transform(std::move(plan), output, input);
301template <
class Function,
class OutputMdspec,
class LhsMdspec,
class RhsMdspec>
302 requires SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>
303[[nodiscard]]
auto prepare_stateless_binary(OutputMdspec& output, LhsMdspec& lhs, RhsMdspec& rhs)
305 using scalar_type = transform_scalar_t<OutputMdspec>;
306 return prepare_overwrite_transform(BinaryArithmeticPlan<scalar_type>{}, output, lhs, rhs);
309template <
class Plan,
class Launch>
void execute_overwrite_transform(Plan
const& plan, Launch&& launch)
311 using scalar_type =
typename Plan::scalar_type;
313 if (!plan.has_work)
return;
314 CHECK(plan.output_buffer !=
nullptr);
315 for (
auto const* input_buffer : plan.input_buffers)
316 CHECK(input_buffer != nullptr);
318 auto stream = plan.output_buffer->resources().streams().acquire();
319 int const device = plan.output_buffer->device().ordinal();
323 auto output_access = plan.output_buffer->write_synchronized_with(stream);
324 auto* output = output_access.data() + plan.output_offset;
325 std::array<std::optional<uni20::cuda::ReadAccess<scalar_type>>, Plan::input_count> input_accesses;
326 std::array<scalar_type const*, Plan::input_count> input_data{};
328 for (std::size_t input = 0; input < Plan::input_count; ++input)
330 auto const* input_buffer = plan.input_buffers[input];
331 if (input_buffer == plan.output_buffer)
333 input_data[input] = output_access.data() + plan.input_offsets[input];
337 std::size_t prior = 0;
338 for (; prior < input; ++prior)
339 if (plan.input_buffers[prior] == input_buffer)
break;
343 input_accesses[input].emplace(input_buffer->read_synchronized_with(stream));
344 input_data[input] = input_accesses[input]->data() + plan.input_offsets[input];
348 CHECK(input_accesses[prior].has_value());
349 input_data[input] = input_accesses[prior]->data() + plan.input_offsets[input];
353 std::forward<Launch>(launch)(output, input_data,
stream.native_handle(), device);
356 completion.release(output_access);
357 for (
auto& input_access : input_accesses)
358 if (input_access) completion.
release(*input_access);
361template <
class Scalar, RegisteredStatelessUnary Function>
362void execute_stateless_unary(UnaryArithmeticPlan<Scalar>
const& plan, Function function)
364 execute_overwrite_transform(plan, [&](Scalar* output,
auto const& inputs, cudaStream_t stream,
int device) {
365 if constexpr (supports_elementwise_arithmetic<Scalar>)
367 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
368 enqueue_elementwise_unary(output, inputs[0], function, elementwise_plan, stream, device);
373 PANIC(
"unsupported scalar reached CUDA reference unary arithmetic");
378template <
class Scalar>
void execute_fill(FillPlan<Scalar>
const& plan, Scalar
value)
380 execute_overwrite_transform(plan, [&](Scalar* output,
auto const&, cudaStream_t stream,
int device) {
381 if constexpr (supports_elementwise_arithmetic<Scalar>)
383 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
384 enqueue_elementwise_fill(output,
value, elementwise_plan, stream, device);
389 PANIC(
"unsupported scalar reached CUDA reference elementwise fill");
394template <
class Scalar,
class Factor>
void execute_scale(ScalePlan<Scalar, Factor>
const& plan)
396 execute_overwrite_transform(plan, [&](Scalar* output,
auto const& inputs, cudaStream_t stream,
int device) {
397 if constexpr (supports_elementwise_scale<Scalar, Factor>)
399 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
400 enqueue_elementwise_scale(output, inputs[0], plan.factor, elementwise_plan, stream, device);
405 PANIC(
"unsupported scalar reached CUDA reference elementwise scaling");
410template <
class Scalar, RegisteredStatelessBinary Function>
411void execute_stateless_binary(BinaryArithmeticPlan<Scalar>
const& plan, Function function)
413 execute_overwrite_transform(plan, [&](Scalar* output,
auto const& inputs, cudaStream_t stream,
int device) {
414 if constexpr (supports_elementwise_arithmetic<Scalar>)
416 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
417 enqueue_elementwise_binary(output, inputs[0], inputs[1], function, elementwise_plan, stream, device);
422 PANIC(
"unsupported scalar reached CUDA reference binary arithmetic");
427template <
class Scalar,
class Factor>
void execute_inplace_scale(InplaceScalePlan<Scalar, Factor>
const& plan)
429 execute_overwrite_transform(plan, [&](Scalar* output,
auto const&, cudaStream_t stream,
int device) {
430 if constexpr (supports_elementwise_scale<Scalar, Factor>)
432 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
433 enqueue_elementwise_inplace_scale(output, plan.factor, elementwise_plan, stream, device);
438 PANIC(
"unsupported scalar reached CUDA reference in-place scaling");
443template <
class Scalar, RegisteredStatelessBinary Function>
444void execute_inplace_stateless_binary(InplaceBinaryPlan<Scalar>
const& plan, Function function)
446 execute_overwrite_transform(plan, [&](Scalar* output,
auto const& inputs, cudaStream_t stream,
int device) {
447 if constexpr (supports_elementwise_arithmetic<Scalar>)
449 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
450 enqueue_elementwise_inplace_binary(output, inputs[0], function, elementwise_plan, stream, device);
455 PANIC(
"unsupported scalar reached CUDA reference in-place binary arithmetic");
460template <
class Scalar,
class Factor>
void execute_add_scaled(AddScaledPlan<Scalar, Factor>
const& plan)
462 execute_overwrite_transform(plan, [&](Scalar* output,
auto const& inputs, cudaStream_t stream,
int device) {
463 if constexpr (supports_elementwise_scale<Scalar, Factor>)
465 plan.elementwise_plan.visit([&](
auto const& elementwise_plan) {
466 enqueue_elementwise_add_scaled(output, inputs[0], plan.factor, elementwise_plan, stream, device);
471 PANIC(
"unsupported scalar reached CUDA reference add-scaled update");
479template <
class Value, uni20::MutableMdspecLike OutputMdspec>
482 if constexpr (detail::cuda_reference::SupportedFillMdspec<OutputMdspec, Value>)
489template <
class Value,
class OutputMdspec>
490 requires detail::cuda_reference::SupportedFillMdspec<OutputMdspec, Value>
493 auto preparation = detail::cuda_reference::prepare_fill<OutputMdspec, Value>(output);
495 detail::cuda_reference::execute_fill(preparation, operation.function.value);
505 if constexpr (detail::cuda_reference::SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>)
512template <detail::cuda_reference::RegisteredStatelessUnary Function,
class OutputMdspec,
class InputMdspec>
513 requires detail::cuda_reference::SupportedStatelessUnaryMdspecs<OutputMdspec, InputMdspec, Function>
517 auto preparation = detail::cuda_reference::prepare_stateless_unary<Function>(output, input);
519 detail::cuda_reference::execute_stateless_unary(preparation, operation.function);
524template <
class Factor, uni20::MutableMdspecLike OutputMdspec, uni20::MdspecLike InputMdspec>
528 if constexpr (detail::cuda_reference::SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>)
535template <
class Factor,
class OutputMdspec,
class InputMdspec>
536 requires detail::cuda_reference::SupportedScaleMdspecs<OutputMdspec, InputMdspec, Factor>
540 auto preparation = detail::cuda_reference::prepare_scale(output, input, operation.function.factor);
542 detail::cuda_reference::execute_scale(preparation);
550 LhsMdspec&, RhsMdspec&)
552 if constexpr (detail::cuda_reference::SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>)
559template <detail::cuda_reference::RegisteredStatelessBinary Function,
class OutputMdspec,
class LhsMdspec,
561 requires detail::cuda_reference::SupportedStatelessBinaryMdspecs<OutputMdspec, LhsMdspec, RhsMdspec, Function>
563 LhsMdspec& lhs, RhsMdspec& rhs)
565 auto preparation = detail::cuda_reference::prepare_stateless_binary<Function>(output, lhs, rhs);
567 detail::cuda_reference::execute_stateless_binary(preparation, operation.function);
572template <
class Factor, uni20::MutableMdspecLike OutputMdspec>
576 if constexpr (detail::cuda_reference::SupportedInplaceScaleMdspec<OutputMdspec, Factor>)
583template <
class Factor,
class OutputMdspec>
584 requires detail::cuda_reference::SupportedInplaceScaleMdspec<OutputMdspec, Factor>
586 OutputMdspec& output)
588 auto preparation = detail::cuda_reference::prepare_inplace_scale(output, operation.function.factor);
590 detail::cuda_reference::execute_inplace_scale(preparation);
600 if constexpr (detail::cuda_reference::SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>)
607template <detail::cuda_reference::RegisteredStatelessBinary Function,
class OutputMdspec,
class InputMdspec>
608 requires detail::cuda_reference::SupportedInplaceStatelessBinaryMdspecs<OutputMdspec, InputMdspec, Function>
612 auto preparation = detail::cuda_reference::prepare_inplace_stateless_binary<Function>(output, input);
614 detail::cuda_reference::execute_inplace_stateless_binary(preparation, operation.function);
619template <
class Factor, uni20::MutableMdspecLike OutputMdspec, uni20::MdspecLike InputMdspec>
621 OutputMdspec&, InputMdspec&)
623 if constexpr (detail::cuda_reference::SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>)
630template <
class Factor,
class OutputMdspec,
class InputMdspec>
631 requires detail::cuda_reference::SupportedAddScaledMdspecs<OutputMdspec, InputMdspec, Factor>
633 OutputMdspec& output, InputMdspec& input)
635 auto preparation = detail::cuda_reference::prepare_add_scaled(output, input, operation.function.factor);
637 detail::cuda_reference::execute_add_scaled(preparation);
Typed CUDA device buffers and scoped stream access guards.
One operation-tail completion shared by multiple CUDA buffer accesses.
Definition buffer.hpp:1160
Move-only logical CUDA buffer and completion state.
Definition buffer.hpp:222
Temporarily select one CUDA device and restore the previous device on destruction.
Definition runtime.hpp:44
Mdspan metadata whose data handle is immediate or available through a data descriptor.
Definition mdspan/concepts.hpp:304
MdspecLike types whose eventual storage supports writes.
Definition mdspan/concepts.hpp:398
An mdspec-like type whose mapping exposes multidimensional strides.
Definition mdspan/concepts.hpp:427
CUDA Tensor storage with deferred device-memory data descriptors.
locale_t value
Definition data_table.cpp:49
Operation-tag backend dispatch over tensor-view operands.
Compiled CUDA entry points for named stateless arithmetic transforms.
Named backend-neutral function objects for elementwise transforms.
Compiled CUDA entry points for elementwise scaling.
auto make_multi_iteration_plan(std::tuple< Mappings... > const &mappings)
Build a merged iteration plan for multiple tensors sharing the same extents.
Definition iteration_plan.hpp:263
Helpers for constructing merged iteration plans over strided tensors.
Mdspan concept and accessor extensions for Uni20.
Shared CUDA reference-backend traits for raw buffer mdspecs.
std::pair< int, int > size(std::FILE *stream)
Return the terminal size associated with a C stream.
Definition terminal.cpp:504
ReadBuffer< T > && release(ReadBuffer< T > &in)
Adaptor for transferring ReadBuffer ownership into an await expression.
Definition buffers.hpp:227
stream
Selects the ordinary display stream for a message.
Definition display.hpp:25
Definition linalg/async.hpp:28
constexpr bool kernel_attempt_succeeded(KernelAttempt attempt) noexcept
Return whether a backend performed the operation.
Definition kernel_attempt.hpp:40
KernelAttempt try_kernel(BlasBackend, gemm_op const &, C &c, S const &alpha, A &a, B &b, S const &beta)
Pack logical host values and execute GEMM through value-preserving provider conversion.
Definition linalg/backends/blas/binary80.hpp:30
KernelAttempt
Result of one runtime kernel backend attempt.
Definition kernel_attempt.hpp:27
constexpr KernelAcceptance< KernelTypeAcceptance::no > kernel_types_no
Report that a backend rejects the argument types.
Definition dispatch_error.hpp:34
consteval auto kernel_accepts_types(BlasBackend, gemm_op const &, C &, S const &, A &, B &, S const &)
Accept host-readable native binary80 operands, including transforming accessors.
Definition linalg/backends/blas/binary80.hpp:19
constexpr KernelAcceptance< KernelTypeAcceptance::maybe > kernel_types_maybe
Report that runtime operand values determine backend acceptance.
Definition dispatch_error.hpp:37
Backend value for generic CUDA runtime kernels and transfers.
Definition backend_selector.hpp:48
Add one retained scalar multiple to an existing element value.
Definition elementwise_functions.hpp:86
Nullary elementwise function that returns one retained value.
Definition elementwise_functions.hpp:20
Multiply one element value by a retained scalar factor.
Definition elementwise_functions.hpp:68
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309