Skip to content

Commit 79ab389

Browse files
committed
add FloorMod layer with broadcasting
1 parent 0ae21a3 commit 79ab389

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

FEATURES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ depth_to_space_layer
1414
div_layer
1515
expand_dims_nd_layer
1616
floor_div_layer
17+
floor_mod_layer
1718
gather_layer
1819
gather_nd_layer
1920
nms_gather_layer
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#ifndef CAFFE_FLOOR_MOD_LAYER_HPP_
2+
#define CAFFE_FLOOR_MOD_LAYER_HPP_
3+
4+
#include <vector>
5+
6+
#include "caffe/blob.hpp"
7+
#include "caffe/layer.hpp"
8+
#include "caffe/proto/caffe.pb.h"
9+
10+
namespace caffe {
11+
12+
/*
13+
* @brief Compute element-wise remainder of division.
14+
* Support broadcasting as TensorFlow do.
15+
* floor(x / y) * y + floor_mod(x, y) = x
16+
* Note: Two dimensions are compatible for broadcasting if both are the same or either is 1.
17+
* The rule starts with the right-most dimension, and works towards the left-most dimension.
18+
*/
19+
20+
template <typename Dtype>
21+
class FloorModLayer : public Layer<Dtype> {
22+
public:
23+
explicit FloorModLayer(const LayerParameter& param)
24+
: Layer<Dtype>(param) {}
25+
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
26+
const vector<Blob<Dtype>*>& top);
27+
28+
virtual inline const char* type() const { return "FloorMod"; }
29+
virtual inline int MinBottomBlobs() const { return 1; }
30+
virtual inline int ExactNumTopBlobs() const { return 1; }
31+
32+
protected:
33+
34+
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
35+
const vector<Blob<Dtype>*>& top);
36+
37+
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
38+
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom){
39+
NOT_IMPLEMENTED;
40+
};
41+
42+
int dim_diff_;
43+
int dim_;
44+
bool is_scalar_;
45+
};
46+
47+
} // namespace caffe
48+
49+
#endif // CAFFE_FLOOR_MOD_LAYER_HPP_
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#include <algorithm>
2+
#include <functional>
3+
#include <utility>
4+
#include <vector>
5+
6+
#include "caffe/layers/floor_mod_layer.hpp"
7+
8+
namespace caffe {
9+
template <typename Dtype>
10+
void FloorModLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
11+
const vector<Blob<Dtype>*>& top) {
12+
CHECK_NE(top[0], bottom[0]) << this->type() << " Layer does not allow in-place computation.";
13+
is_scalar_=false;
14+
Blob<Dtype>* divisor = (bottom.size() > 1) ? bottom[1] : this->blobs_[0].get();
15+
16+
if(bottom[0]->num_axes()==0 || divisor->num_axes()==0)
17+
is_scalar_=true;
18+
dim_diff_ = bottom[0]->num_axes() - divisor->num_axes();
19+
dim_ = bottom[0]->num_axes() >= divisor->num_axes() ? bottom[0]->num_axes() : divisor->num_axes();
20+
vector<int> top_shape(dim_, 1);
21+
if(dim_diff_ == 0)
22+
{
23+
if(!is_scalar_)
24+
{
25+
for(int i=0;i<dim_;i++)
26+
{
27+
CHECK(bottom[0]->shape(i)==divisor->shape(i) || bottom[0]->shape(i)==1 || divisor->shape(i)==1)
28+
<< "Dimensions must be equal or 1 in the bottoms!";
29+
top_shape[i] = bottom[0]->shape(i) >= divisor->shape(i) ? bottom[0]->shape(i): divisor->shape(i);
30+
}
31+
}
32+
}
33+
else if(dim_diff_ > 0) //bottom0 has more axes than bottom1
34+
{
35+
if(!is_scalar_)
36+
{
37+
for(int i=0;i<dim_diff_;i++)
38+
top_shape[i] = bottom[0]->shape(i);
39+
for(int i=dim_diff_; i<dim_; i++)
40+
top_shape[i] = bottom[0]->shape(i) >= divisor->shape(i-dim_diff_) ? bottom[0]->shape(i): divisor->shape(i-dim_diff_);
41+
}
42+
else //bottom1 is a scalar
43+
{
44+
for(int i=0;i<dim_;i++)
45+
top_shape[i] = bottom[0]->shape(i);
46+
}
47+
}
48+
else //dim_diff_<0, bottom1 has more axes than bottom0
49+
{
50+
if(!is_scalar_)
51+
{
52+
for(int i=0;i<-dim_diff_;i++)
53+
top_shape[i] = divisor->shape(i);
54+
for(int i=-dim_diff_; i<dim_; i++)
55+
top_shape[i] = bottom[0]->shape(i+dim_diff_) >= divisor->shape(i) ? bottom[0]->shape(i+dim_diff_): divisor->shape(i);
56+
}
57+
else //bottom0 is a scalar
58+
{
59+
for(int i=0;i<dim_;i++)
60+
top_shape[i] = divisor->shape(i);
61+
}
62+
}
63+
64+
top[0]->Reshape(top_shape);
65+
}
66+
67+
template <typename Dtype>
68+
void FloorModLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
69+
const vector<Blob<Dtype>*>& top) {
70+
const Dtype* bottom0_data = bottom[0]->cpu_data();
71+
Blob<Dtype>* divisor = (bottom.size() > 1) ? bottom[1] : this->blobs_[0].get();
72+
const Dtype* bottom1_data = divisor->cpu_data();
73+
Dtype* top_data = top[0]->mutable_cpu_data();
74+
int count = top[0]->count();
75+
76+
// Assume top index (x,y,z) with top shape (A, B, C)
77+
// top offset d = xBC + yC + z
78+
// So to count the bottom index, should first figure out x, y, z
79+
// x = d / BC
80+
// y = (d % BC) / C
81+
// z = d % C
82+
// Then consider bottom shape (A', B', C'), while A = A' or 1
83+
// So bottom offset = x'B'C' + y'C' + z', while x' = x or 0
84+
if(!is_scalar_)
85+
{
86+
for(int d=0; d<count; d++)
87+
{
88+
int offset0 = 0;
89+
int offset1 = 0;
90+
91+
if(dim_diff_ == 0)
92+
{
93+
for(int i=0;i<dim_-1;i++)
94+
{
95+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
96+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
97+
int n1 = 1 == divisor->shape(i) ? 0 : num;
98+
offset0 += n0 * bottom[0]->count(i+1);
99+
offset1 += n1 * divisor->count(i+1);
100+
}
101+
int z = d % top[0]->shape(dim_-1);
102+
int z0 = 1 == bottom[0]->shape(dim_-1) ? 0 : z;
103+
int z1 = 1 == divisor->shape(dim_-1) ? 0 : z;
104+
offset0 += z0;
105+
offset1 += z1;
106+
}
107+
else if(dim_diff_ > 0) //bottom0 has more axes than bottom1
108+
{
109+
for(int i=0;i<dim_diff_;i++)
110+
{
111+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
112+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
113+
offset0 += n0 * bottom[0]->count(i+1);
114+
}
115+
for(int i=dim_diff_;i<dim_-1;i++)
116+
{
117+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
118+
int n0 = 1 == bottom[0]->shape(i) ? 0 : num;
119+
int n1 = 1 == divisor->shape(i-dim_diff_) ? 0 : num;
120+
offset0 += n0 * bottom[0]->count(i+1);
121+
offset1 += n1 * divisor->count(i-dim_diff_+1);
122+
}
123+
int z = d % top[0]->shape(dim_-1);
124+
int z0 = 1 == bottom[0]->shape(dim_-1) ? 0 : z;
125+
int z1 = 1 == divisor->shape(dim_-dim_diff_-1) ? 0 : z;
126+
offset0 += z0;
127+
offset1 += z1;
128+
}
129+
else //dim_diff_<0, bottom1 has more axes than bottom0
130+
{
131+
for(int i=0;i<-dim_diff_;i++)
132+
{
133+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
134+
int n1 = 1 == divisor->shape(i) ? 0 : num;
135+
offset1 += n1 * divisor->count(i+1);
136+
}
137+
for(int i=-dim_diff_;i<dim_-1;i++)
138+
{
139+
int num = (d % top[0]->count(i)) / top[0]->count(i+1);
140+
int n0 = 1 == bottom[0]->shape(i+dim_diff_) ? 0 : num;
141+
int n1 = 1 == divisor->shape(i) ? 0 : num;
142+
offset0 += n0 * bottom[0]->count(i+dim_diff_+1);
143+
offset1 += n1 * divisor->count(i+1);
144+
}
145+
int z = d % top[0]->shape(dim_-1);
146+
int z0 = 1 == bottom[0]->shape(dim_+dim_diff_-1) ? 0 : z;
147+
int z1 = 1 == divisor->shape(dim_-1) ? 0 : z;
148+
offset0 += z0;
149+
offset1 += z1;
150+
}
151+
top_data[d] = bottom0_data[offset0] -
152+
floor(bottom0_data[offset0] / bottom1_data[offset1]) * bottom1_data[offset1];
153+
}
154+
}
155+
else //is scalar with shape ()
156+
{
157+
if(divisor->num_axes()==0) //bottom1 is a scalar
158+
{
159+
Dtype scalar = bottom1_data[0];
160+
for(int d=0; d<count; d++)
161+
{
162+
top_data[d] = bottom0_data[d] - floor(bottom0_data[d] / scalar) * scalar;
163+
}
164+
}
165+
else //bottom0 is a scalar
166+
{
167+
Dtype scalar = bottom0_data[0];
168+
for(int d=0; d<count; d++)
169+
{
170+
top_data[d] = scalar - floor(scalar / bottom1_data[d]) * bottom1_data[d];
171+
}
172+
}
173+
}
174+
}
175+
176+
177+
INSTANTIATE_CLASS(FloorModLayer);
178+
REGISTER_LAYER_CLASS(FloorMod);
179+
180+
} // namespace caffe

0 commit comments

Comments
 (0)