1use crate::{each_op, each_op_slice_mut, Matrix};
2use custos::{impl_stack, number::Float, CDatatype, Device, MainMemory, Shape};
3
4#[cfg(feature = "cpu")]
5use custos::CPU;
6
7#[cfg(feature = "stack")]
8use custos::Stack;
9
10#[cfg(feature = "opencl")]
11use crate::{cl_str_op_mut, opencl::cl_str_op_mat};
12#[cfg(feature = "opencl")]
13use custos::OpenCL;
14
15#[cfg(feature = "cuda")]
16use crate::{cu_str_op, cu_str_op_mut};
17#[cfg(feature = "cuda")]
18use custos::CUDA;
19
20impl<'a, T, D: ActivationOps<T, S>, S: Shape> Matrix<'a, T, D, S> {
21 #[inline]
22 pub fn tanh(&self) -> Matrix<'a, T, D, S> {
23 self.device().tanh(self)
24 }
25
26 #[inline]
27 pub fn tanh_grad(&self) -> Matrix<'a, T, D, S> {
28 self.device().tanh_grad(self)
29 }
30
31 #[inline]
32 pub fn relu(&self) -> Matrix<'a, T, D, S> {
33 self.device().relu(self)
34 }
35
36 #[inline]
37 pub fn relu_mut(&mut self) {
38 self.device().relu_mut(self)
39 }
40
41 #[inline]
42 pub fn relu_grad(&self) -> Matrix<'a, T, D, S> {
43 self.device().relu_grad(self)
44 }
45
46 #[inline]
47 pub fn relu_grad_mut(&mut self) {
48 self.device().relu_grad_mut(self)
49 }
50
51 #[inline]
52 pub fn sigmoid(&self) -> Matrix<'a, T, D, S> {
53 self.device().sigmoid(self)
54 }
55
56 #[inline]
57 pub fn sigmoid_grad(&self) -> Matrix<'a, T, D, S> {
59 self.device().sigmoid_grad(self)
60 }
61}
62
63pub trait ActivationOps<T, S: Shape = (), D: Device = Self>: Device {
64 fn sigmoid(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
65 fn sigmoid_grad(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
66 fn tanh(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
67 fn tanh_grad(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
68 fn relu(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
69 fn relu_mut(&self, x: &mut Matrix<T, D, S>);
71 fn relu_grad(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S>;
72 fn relu_grad_mut(&self, x: &mut Matrix<T, D, S>);
74}
75
76#[cfg(feature = "opencl")]
77impl<T: CDatatype + Float> ActivationOps<T> for OpenCL {
78 #[inline]
79 fn sigmoid(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
80 cl_str_op_mat(self, x, "1.0 / (1.0 + exp(-x))").unwrap()
81 }
82
83 fn sigmoid_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
84 cl_str_op_mat(self, x, "x * (1.0 - x)").unwrap()
85 }
86
87 #[inline]
88 fn tanh(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
89 cl_str_op_mat(self, x, "tanh(x)").unwrap()
90 }
91
92 #[inline]
93 fn tanh_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
94 cl_str_op_mat(self, x, "1.0 - pow(tanh(x), 2)").unwrap()
95 }
96
97 #[inline]
98 fn relu(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
99 cl_str_op_mat(self, x, "x * (x >= 0)").unwrap()
100 }
101
102 #[inline]
103 fn relu_mut(&self, x: &mut Matrix<T, Self, ()>) {
104 cl_str_op_mut(self, x, "x * (x >= 0)").unwrap();
105 }
106
107 #[inline]
108 fn relu_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
109 cl_str_op_mat(self, x, "(x >= 0)").unwrap()
110 }
111
112 #[inline]
113 fn relu_grad_mut(&self, x: &mut Matrix<T, Self, ()>) {
114 cl_str_op_mut(self, x, "(x >= 0)").unwrap()
115 }
116}
117
118#[impl_stack]
119impl<T: Float, D: MainMemory, S: Shape> ActivationOps<T, S, D> for CPU {
120 #[inline]
121 fn sigmoid(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
122 each_op(self, x, |x| T::one() / (T::one() + -x.exp()))
123 }
124 #[inline]
125 fn sigmoid_grad(&self, activated: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
126 each_op(self, activated, |x| x * (T::one() - x))
127 }
128
129 #[inline]
130 fn tanh(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
131 each_op(self, x, |x| x.tanh())
132 }
133
134 #[inline]
135 fn tanh_grad(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
136 each_op(self, x, |x| T::one() - x.tanh().powi(2))
137 }
138
139 #[inline]
140 fn relu(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
141 each_op(self, x, |x| T::from_usize((x >= T::zero()) as usize) * x)
142 }
143
144 #[inline]
145 fn relu_mut(&self, x: &mut Matrix<T, D, S>) {
146 each_op_slice_mut(x, |x| T::from_usize((x >= T::zero()) as usize) * x)
147 }
148
149 #[inline]
150 fn relu_grad(&self, x: &Matrix<T, D, S>) -> Matrix<T, Self, S> {
151 each_op(self, x, |x| T::from_usize((x >= T::default()) as usize))
152 }
153
154 #[inline]
155 fn relu_grad_mut(&self, x: &mut Matrix<T, D, S>) {
156 each_op_slice_mut(x, |x| T::from_usize((x >= T::default()) as usize))
157 }
158}
159
160#[cfg(feature = "cuda")]
161impl<T: CDatatype> ActivationOps<T> for CUDA {
162 #[inline]
163 fn sigmoid(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
164 let out = cu_str_op(self, x, "1.0 / (1.0 + exp(-x))").unwrap();
165 (out, x.dims()).into()
166 }
167
168 fn sigmoid_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
169 let out = cu_str_op(self, x, "x * (1.0 - x)").unwrap();
170 (out, x.dims()).into()
171 }
172
173 #[inline]
174 fn tanh(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
175 let out = cu_str_op(self, x, "tanh(x)").unwrap();
176 (out, x.dims()).into()
177 }
178
179 #[inline]
180 fn tanh_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
181 let out = cu_str_op(self, x, "1.0 - pow(tanh(x), 2)").unwrap();
182 (out, x.dims()).into()
183 }
184
185 #[inline]
186 fn relu(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
187 let out = cu_str_op(self, x, "x * (x >= 0)").unwrap();
188 (out, x.dims()).into()
189 }
190
191 #[inline]
192 fn relu_grad(&self, x: &Matrix<T, Self>) -> Matrix<T, Self> {
193 let out = cu_str_op(self, x, "(x >= 0)").unwrap();
194 (out, x.dims()).into()
195 }
196
197 #[inline]
198 fn relu_mut(&self, x: &mut Matrix<T, Self, ()>) {
199 cu_str_op_mut(self, x, "x * (x >= 0)").unwrap();
200 }
201
202 #[inline]
203 fn relu_grad_mut(&self, x: &mut Matrix<T, Self, ()>) {
204 cu_str_op_mut(self, x, "(x >= 0)").unwrap()
205 }
206}