defmodule Evision.Backend do @behaviour Nx.Backend defstruct [:ref] alias Nx.Tensor, as: T alias Evision.Backend, as: EB @dialyzer :no_contracts @spec reject_error(Evision.Mat.maybe_mat_out()) :: Evision.Mat.t() defp reject_error(maybe_error) do case maybe_error do {:error, message} -> raise RuntimeError, message mat -> Evision.Internal.Structurise.to_struct(mat) end end ## Creation @impl true @doc """ Tensor with constant values. ## Example Nx.tensor(1.0, backend: Evision.Backend) #Nx.Tensor< f32 Evision.Backend 1.0 > """ @spec constant(Nx.Tensor.t(), number(), any()) :: Nx.Tensor.t() def constant(%T{shape: {}, type: type} = out, scalar, _backend_options) do Evision.Mat.number(scalar, type) |> reject_error() |> to_nx(out) end @spec constant(Nx.Tensor.t(), number(), any()) :: Nx.Tensor.t() def constant(%T{shape: shape, type: type} = out, scalar, _backend_options) do Evision.Mat.full(shape, scalar, type) |> reject_error() |> to_nx(out) end @impl true @spec from_binary(Nx.Tensor.t(), binary(), any()) :: Nx.Tensor.t() def from_binary(%T{shape: shape, type: type} = out, binary, _backend_options) when is_binary(binary) do Evision.Mat.from_binary_by_shape(binary, type, shape) |> reject_error() |> to_nx(out) end @impl true @spec eye(Nx.Tensor.t(), any()) :: Nx.Tensor.t() def eye(%T{shape: {n, n}, type: type} = out, _backend_options) do Evision.Mat.eye(n, type) |> reject_error() |> to_nx(out) end @impl true @spec iota(Nx.Tensor.t(), nil | non_neg_integer(), any()) :: Nx.Tensor.t() def iota(%T{shape: {}, type: type} = out, nil, _backend_options) do Evision.Mat.arange(0, 1, 1, type) |> reject_error() |> to_nx(out) end def iota(%T{shape: shape, type: type} = out, nil, _backend_options) do Evision.Mat.arange( 0, Nx.size(shape), 1, type, shape ) |> reject_error() |> to_nx(out) end @impl true def iota(%T{shape: {n}, type: type} = out, 0, _backend_options) do Evision.Mat.arange(0, n, 1, type) |> reject_error() |> to_nx(out) end def iota(%T{shape: shape, type: type} = out, axis, _backend_options) do # gets the size of iota dim = elem(shape, axis) # build the iota in one dimension aten = reject_error(Evision.Mat.arange(0, dim, 1, type)) # reshape the tensor above to be have shape where everything is 1, except for dim reshape = Tuple.duplicate(1, Nx.rank(shape)) |> put_elem(axis, dim) aten = reject_error(Evision.Mat.reshape(aten, reshape)) # Now broadcast the tensor using the original shape Evision.Mat.broadcast_to(aten, shape) |> reject_error() |> to_nx(out) end @impl true @spec backend_copy(Nx.Tensor.t(), atom, any) :: Nx.Tensor.t() def backend_copy(tensor, Nx.Tensor, opts) do backend_copy(tensor, Nx.BinaryBackend, opts) end def backend_copy(tensor, Evision.Backend, _opts) do Evision.Mat.clone(from_nx(tensor)) |> reject_error() |> to_nx(tensor) end def backend_copy(tensor, backend, opts) do case Evision.Mat.to_binary(from_nx(tensor), 0) do {:error, msg} -> raise RuntimeError, msg binary -> backend.from_binary(tensor, binary, opts) end end @impl true @spec backend_transfer(Nx.Tensor.t(), atom, any) :: Nx.Tensor.t() def backend_transfer(tensor, backend, opts) do backend_copy(tensor, backend, opts) end @impl true def backend_deallocate(_tensor) do :ok end @impl true def to_batched(out, %T{shape: shape} = tensor, opts) do leftover = opts[:leftover] batch_size = elem(out.shape, 0) Evision.Mat.to_batched(from_nx(tensor), batch_size, shape, leftover: leftover) |> Enum.map(&to_nx(&1, out)) end @impl true @spec to_binary(Nx.Tensor.t(), non_neg_integer) :: binary | {:error, binary} def to_binary(%T{data: %EB{ref: mat}}, limit) when is_struct(mat, Evision.Mat) and is_integer(limit) and limit >= 0 do Evision.Mat.to_binary(mat, limit) end @impl true @spec inspect(Nx.Tensor.t(), Inspect.Opts.t()) :: String.t() def inspect(%T{data: %EB{ref: mat}} = tensor, inspect_opts) do limit = if inspect_opts.limit == :infinity, do: :infinity, else: inspect_opts.limit + 1 Evision.Mat.to_binary(mat, min(limit, Nx.size(tensor))) |> then(&Nx.Backend.inspect(tensor, &1, inspect_opts)) |> maybe_add_signature(tensor) end @impl true @spec as_type(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def as_type(%T{type: type} = out, %T{data: %EB{ref: mat}}) do Evision.Mat.as_type(mat, type) |> reject_error() |> to_nx(out) end @impl true @spec bitcast(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def bitcast(out, tensor) do case Evision.Mat.to_binary(from_nx(tensor), 0) do {:error, msg} -> raise RuntimeError, msg binary -> from_binary(out, binary, []) end end @impl true @spec reshape(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def reshape(%T{shape: shape} = out, %T{data: %EB{ref: mat}}) do Evision.Mat.reshape(mat, Tuple.to_list(shape)) |> reject_error() |> to_nx(out) end @impl true @spec squeeze(Nx.Tensor.t(), Nx.Tensor.t(), term()) :: Nx.Tensor.t() def squeeze(out, %T{data: %EB{ref: mat}}, _axes) do Evision.Mat.squeeze(mat) |> reject_error() |> to_nx(out) end @impl true @spec broadcast(Nx.Tensor.t(), Nx.Tensor.t(), tuple, any) :: Nx.Tensor.t() def broadcast(out, %T{} = t, shape, axes) do {tensor, reshape} = maybe_reshape(t, shape, axes) Evision.Mat.broadcast_to(tensor |> from_nx(), shape, reshape) |> reject_error() |> to_nx(out) end defp maybe_reshape(%T{shape: {n}} = t, {n, _}, [0]) do {Nx.reshape(t, {n, 1}), {n, 1}} end defp maybe_reshape(%T{shape: shape} = t, to_shape, _axes) do l_shape = Tuple.to_list(shape) l_to_shape = Tuple.to_list(to_shape) if length(l_shape) != length(l_to_shape) do src_shape_axis = length(l_shape) - 1 {_src_shape_axis, force_shape} = for axis <- Enum.to_list(length(l_to_shape)-1..0), reduce: {src_shape_axis, []} do {src_shape_axis, acc} -> if src_shape_axis == -1 do {src_shape_axis, [1 | acc]} else case {elem(shape, src_shape_axis), elem(to_shape, axis)} do {1, _d} -> {src_shape_axis - 1, [1 | acc]} {d, d} -> {src_shape_axis - 1, [d | acc]} end end end force_shape = List.to_tuple(force_shape) {Nx.reshape(t, force_shape), force_shape} else {t, shape} end end @impl true @spec dot(Nx.Tensor.t(), Nx.Tensor.t(), any, [], Nx.Tensor.t(), any, []) :: Nx.Tensor.t() def dot( %T{type: out_type} = out, a, _left_axes, [], b, _right_axes, []) do Evision.Mat.matrix_multiply(from_nx(a), from_nx(b), out_type) |> reject_error() |> to_nx(out) end @impl true @spec clip(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def clip(%T{} = out, %T{} = t, %T{} = min, %T{} = max) do t |> Nx.as_type(out.type) |> from_nx() |> Evision.Mat.clip(to_number(min), to_number(max)) |> reject_error() |> to_nx(out) end @impl true @spec transpose(Nx.Tensor.t(), Nx.Tensor.t(), [integer]) :: Nx.Tensor.t() def transpose(out, %T{shape: shape} = tensor, axes) do Evision.Mat.transpose(from_nx(tensor), axes, as_shape: shape) |> to_nx(out) end @impl true def pad(_out, tensor, _constant, config) do case tensor.shape do {} -> tensor {_} -> [{_edge_low, _edge_high, _interior}] = config _ -> permutation = for i <- 0..(Nx.rank(tensor) - 2), do: i _permutation = [Nx.rank(tensor) - 1 | permutation] end raise RuntimeError, "not implemented yet" end @impl true @spec add(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def add(%T{type: type, shape: out_shape}=out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) Evision.Mat.add(from_nx(l), from_nx(r), type) |> reject_error() |> to_nx(out) end @impl true @spec subtract(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def subtract(%T{type: type, shape: out_shape}=out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) Evision.Mat.subtract(from_nx(l), from_nx(r), type) |> reject_error() |> to_nx(out) end @impl true def multiply(%T{type: type}=out, %T{}=l, %T{}=r) do case check_mul_div_kind(l, r) do :per_element -> Evision.Mat.multiply(from_nx(l), from_nx(r), type) :matrix -> l = maybe_cast_type_for_matrix_mul_div(l) r = maybe_cast_type_for_matrix_mul_div(r) Evision.Mat.matrix_multiply(l, r) |> Evision.Mat.as_type(type) end |> to_nx(out) end @impl true def divide(%T{type: type, shape: shape}=out, l, r) do case check_mul_div_kind(l, r) do :per_element -> Evision.Mat.divide(from_nx(l), from_nx(r), type) :matrix -> l = Nx.broadcast(l, shape) r = Nx.broadcast(r, shape) l = maybe_cast_type_for_matrix_mul_div(l) r = maybe_cast_type_for_matrix_mul_div(r) Evision.Mat.divide(l, r, type) end |> to_nx(out) end defp check_mul_div_kind(%T{shape: {}}, _r) do :per_element end defp check_mul_div_kind(_l, %T{shape: {}}) do :per_element end defp check_mul_div_kind(_l, _r) do :matrix end defp maybe_cast_type_for_matrix_mul_div(%T{type: {:f, _}}=tensor) do from_nx(tensor) end defp maybe_cast_type_for_matrix_mul_div(tensor) do Evision.Mat.as_type(from_nx(tensor), {:f, 64}) end @impl true def min(%T{shape: out_shape}=out, l, r) do shape = case out_shape do {} -> {1} _ -> out_shape end l = Nx.broadcast(l, shape) r = Nx.broadcast(r, shape) {l, r} = enforce_same_type(l, r) l = Evision.Mat.reshape(from_nx(l), shape) r = Evision.Mat.reshape(from_nx(r), shape) ret = Evision.min(l, r) to_nx(ret, %T{out | type: Evision.Mat.type(ret)}) end @impl true def max(%T{shape: out_shape}=out, l, r) do shape = case out_shape do {} -> {1} _ -> out_shape end l = Nx.broadcast(l, shape) r = Nx.broadcast(r, shape) {l, r} = enforce_same_type(l, r) l = Evision.Mat.reshape(from_nx(l), shape) r = Evision.Mat.reshape(from_nx(r), shape) ret = Evision.max(l, r) to_nx(ret, %T{out | type: Evision.Mat.type(ret)}) end defp enforce_same_type(%T{type: type}=a, %T{type: type}=b), do: {a, b} defp enforce_same_type(%T{type: a_type={:f, 64}}=a, %T{type: _b_type}=b) do new_type = Evision.Mat.as_type(from_nx(b), a_type) b = %T{b | type: a_type} {a, to_nx(new_type, b)} end defp enforce_same_type(%T{type: _a_type}=a, %T{type: b_type={:f, 64}}=b) do new_type = Evision.Mat.as_type(from_nx(a), b_type) a = %T{a | type: b_type} {to_nx(new_type, a), b} end defp enforce_same_type(%T{type: a_type={:f, 32}}=a, %T{type: _b_type}=b) do new_type = Evision.Mat.as_type(from_nx(b), a_type) b = %T{b | type: a_type} {a, to_nx(new_type, b)} end defp enforce_same_type(%T{type: _a_type}=a, %T{type: b_type={:f, 32}}=b) do new_type = Evision.Mat.as_type(from_nx(a), b_type) a = %T{a | type: b_type} {to_nx(new_type, a), b} end defp enforce_same_type(%T{type: {:u, a_bits}}=a, %T{type: {:u, b_bits}}=b) do new_type = {:u, Kernel.max(a_bits, b_bits)} new_typed_a = Evision.Mat.as_type(from_nx(a), new_type) a = %T{a | type: new_type} new_typed_b = Evision.Mat.as_type(from_nx(b), new_type) b = %T{b | type: new_type} {to_nx(new_typed_a, a), to_nx(new_typed_b, b)} end defp enforce_same_type(%T{type: {:s, a_bits}}=a, %T{type: {:s, b_bits}}=b) do new_type = {:s, Kernel.max(a_bits, b_bits)} new_typed_a = Evision.Mat.as_type(from_nx(a), new_type) a = %T{a | type: new_type} new_typed_b = Evision.Mat.as_type(from_nx(b), new_type) b = %T{b | type: new_type} {to_nx(new_typed_a, a), to_nx(new_typed_b, b)} end defp enforce_same_type(%T{type: {:s, a_bits}}=a, %T{type: {:u, b_bits}}=b) do new_type = {:s, Kernel.max(a_bits, b_bits)} new_typed_a = Evision.Mat.as_type(from_nx(a), new_type) a = %T{a | type: new_type} new_typed_b = Evision.Mat.as_type(from_nx(b), new_type) b = %T{b | type: new_type} {to_nx(new_typed_a, a), to_nx(new_typed_b, b)} end defp enforce_same_type(%T{type: {:u, a_bits}}=a, %T{type: {:s, b_bits}}=b) do new_type = {:s, Kernel.max(a_bits, b_bits)} new_typed_a = Evision.Mat.as_type(from_nx(a), new_type) a = %T{a | type: new_type} new_typed_b = Evision.Mat.as_type(from_nx(b), new_type) b = %T{b | type: new_type} {to_nx(new_typed_a, a), to_nx(new_typed_b, b)} end @impl true def bitwise_and(out, l, r) do {left, right} = maybe_cast_u8(l, r) %T{type: {_, size_left}} = left %T{type: {_, size_right}} = right if size_left >= size_right do Evision.Mat.bitwise_and(from_nx(left), from_nx(right)) else Evision.Mat.bitwise_and(from_nx(right), from_nx(left)) end |> to_nx(out) end @impl true def bitwise_or(out, l, r) do {left, right} = maybe_cast_u8(l, r) %T{type: {_, size_left}} = left %T{type: {_, size_right}} = right if size_left >= size_right do Evision.Mat.bitwise_or(from_nx(left), from_nx(right)) else Evision.Mat.bitwise_or(from_nx(right), from_nx(left)) end |> to_nx(out) end @impl true @spec bitwise_xor(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def bitwise_xor(out, l, r) do {left, right} = maybe_cast_u8(l, r) %T{type: {_, size_left}} = left %T{type: {_, size_right}} = right if size_left >= size_right do Evision.Mat.bitwise_xor(from_nx(left), from_nx(right)) else Evision.Mat.bitwise_xor(from_nx(right), from_nx(left)) end |> to_nx(out) end @spec maybe_cast_u8(Nx.Tensor.t(), Nx.Tensor.t()) :: {Nx.Tensor.t(), Nx.Tensor.t()} defp maybe_cast_u8(%T{type: {t, _}} = left, %T{type: {t, _}} = right), do: {left, right} defp maybe_cast_u8(%T{type: {:u, 8}} = left, %T{} = right), do: {Nx.as_type(left, {:s, 16}), right} defp maybe_cast_u8(%T{} = left, %T{type: {:u, 8}} = right), do: {left, Nx.as_type(right, {:s, 16})} defp maybe_cast_u8(left, right), do: {left, right} @impl true @spec equal(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def equal(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :eq) |> reject_error() |> to_nx(out) end @impl true @spec not_equal(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def not_equal(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :ne) |> reject_error() |> to_nx(out) end @impl true def greater(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :gt) |> reject_error() |> to_nx(out) end @impl true @spec less(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def less(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :lt) |> reject_error() |> to_nx(out) end @impl true @spec greater_equal(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def greater_equal(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :ge) |> reject_error() |> to_nx(out) end @impl true def less_equal(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) from_nx(l) |> Evision.Mat.cmp(from_nx(r), :le) |> to_nx(out) end @spec enforce_same_shape(Nx.Tensor.t(), Nx.Tensor.t(), tuple()) :: {Nx.Tensor.t(), Nx.Tensor.t()} defp enforce_same_shape(l, r, out_shape) do l_mat = Evision.Mat.reshape(from_nx(Nx.broadcast(l, out_shape)), out_shape) b_mat = Evision.Mat.reshape(from_nx(Nx.broadcast(r, out_shape)), out_shape) {to_nx(l_mat, %T{l | shape: out_shape}), to_nx(b_mat, %T{r | shape: out_shape})} end @impl true @spec logical_and(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def logical_and(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) Evision.Mat.logical_and(from_nx(l), from_nx(r)) |> reject_error() |> to_nx(out) |> Nx.not_equal(0) end @impl true @spec logical_or(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def logical_or(%T{shape: out_shape} = out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) Evision.Mat.logical_or(from_nx(l), from_nx(r)) |> reject_error() |> to_nx(out) |> Nx.not_equal(0) end @impl true @spec logical_xor(Nx.Tensor.t(), Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def logical_xor(%T{shape: out_shape} =out, l, r) do {l, r} = enforce_same_shape(l, r, out_shape) {l, r} = enforce_same_type(l, r) Evision.Mat.logical_xor(from_nx(l), from_nx(r)) |> reject_error() |> to_nx(out) |> Nx.not_equal(0) end @impl true @spec abs(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def abs(%T{} = out, tensor) do Evision.Mat.abs(from_nx(tensor)) |> reject_error() |> to_nx(out) end @impl true @spec bitwise_not(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def bitwise_not(%T{type: {s, _}} = out, tensor) when s in [:s, :u] do Evision.Mat.bitwise_not(from_nx(tensor)) |> reject_error() |> to_nx(out) end def bitwise_not(%T{type: type} = _out, _tensor) do raise ArgumentError, "bitwise operators expect integer tensors as inputs " <> "and outputs an integer tensor, got: #{inspect(type)}" end @impl true @spec ceil(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def ceil(%T{type: {s, _}} = out, tensor) when s == :f do Evision.Mat.ceil(from_nx(tensor)) |> reject_error() |> to_nx(out) end def ceil(%T{} = out, tensor) do to_nx(from_nx(tensor), out) end @impl true @spec floor(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def floor(%T{type: {s, _}} = out, tensor) when s == :f do Evision.Mat.floor(from_nx(tensor)) |> reject_error() |> to_nx(out) end def floor(%T{} = out, tensor) do to_nx(from_nx(tensor), out) end @impl true @spec negate(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def negate(%T{} = out, tensor) do Evision.Mat.negate(from_nx(tensor)) |> reject_error() |> to_nx(out) end @impl true @spec round(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def round(%T{type: {s, _}} = out, tensor) when s == :f do Evision.Mat.round(from_nx(tensor)) |> reject_error() |> to_nx(out) end def round(%T{} = out, tensor) do to_nx(from_nx(tensor), out) end @impl true @spec sign(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def sign(%T{} = out, tensor) do Evision.Mat.sign(from_nx(tensor)) |> reject_error() |> to_nx(out) end @impl true @spec exp(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def exp(%T{} = out, tensor) do Evision.exp(from_nx(tensor)) |> reject_error() |> to_nx(out) end @impl true @spec expm1(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def expm1(%T{} = out, tensor) do Evision.Mat.expm1(from_nx(tensor)) |> reject_error() |> to_nx(out) end @impl true @spec log(Nx.Tensor.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def log(%T{} = out, tensor) do Evision.log(from_nx(tensor)) |> reject_error() |> to_nx(out) end @doc false def from_nx(%T{data: %EB{ref: mat_ref}}), do: mat_ref def from_nx(%T{} = tensor) do Nx.backend_transfer(tensor, EB) |> from_nx() end @doc false @spec to_nx(Evision.Mat.t(), Nx.Tensor.t()) :: Nx.Tensor.t() def to_nx(mat_ref, %T{shape: shape} = t) when is_struct(mat_ref, Evision.Mat) do type = Evision.Mat.type(mat_ref) %{t | type: type, data: %__MODULE__{ref: check_shape_and_type(mat_ref, shape, type)}} end @spec to_number(number() | Nx.Tensor.t()) :: number() defp to_number(n) when is_number(n), do: n defp to_number(%T{} = t) do case Evision.Mat.at(from_nx(t), 0) do n when is_number(n) -> n {:error, msg} -> raise RuntimeError, msg end end if Application.compile_env(:evision, :check_shape_and_type, false) do @spec check_shape_and_type(Evision.Mat.t(), tuple, Evision.Mat.mat_type()) :: Evision.Mat.t() defp check_shape_and_type(mat_ref, shape, type) do current_type = Evision.Mat.type(mat_ref) if current_type != type do raise "type mismatch in Evision: expected #{inspect(type)}, got: #{inspect(current_type)}. " <> "Please report this bug" end current_shape = Evision.Mat.shape(mat_ref) if current_shape != shape do raise "shape mismatch in Torchx: expected #{inspect(shape)}, got: #{inspect(current_shape)}. " <> "Please report this bug" end mat_ref end else @spec check_shape_and_type(Evision.Mat.t(), any, any) :: Evision.Mat.t() defp check_shape_and_type(mat_ref, _, _), do: mat_ref end if Application.compile_env(:evision, :add_backend_on_inspect, true) do defp maybe_add_signature(result, %T{data: %EB{ref: _mat_ref}}) do Inspect.Algebra.concat([ "Evision.Backend", Inspect.Algebra.line(), result ]) end else defp maybe_add_signature(result, _tensor) do result end end funs = Nx.Backend.behaviour_info(:callbacks) -- Module.definitions_in(__MODULE__, :def) @doc false def __unimplemented__, do: unquote(funs) for {fun, arity} <- funs do args = Macro.generate_arguments(arity, __MODULE__) @impl true def unquote(fun)(unquote_splicing(args)) do raise "operation #{unquote(fun)} is not yet supported on Evision.Backend.\n" <> "Please use another backend like Nx.BinaryBackend or Torchx.Backend.\n" <> " To use Torchx.Backend, :torchx should be added to your app's deps.\n" <> " Please see https://github.com/elixir-nx/nx/tree/main/torchx for more information on how to install and use it.\n" <> "To convert the tensor to another backend, please use Evision.Mat.to_nx(tensor, Backend.ModuleName)\n" <> " for example, Evision.Mat.to_nx(tensor, Nx.BinaryBackend) or Evision.Mat.to_nx(tensor, Torchx.Backend).\n" <> "Pull request would be more than welcomed if you'd like to implmenent this function and make contributions." end end end