silicium 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +3 -3
- data/.gitignore +13 -13
- data/.rakeTasks +8 -0
- data/.travis.yml +28 -25
- data/CODE_OF_CONDUCT.md +74 -74
- data/Gemfile +8 -8
- data/LICENSE.txt +21 -21
- data/Makefile +269 -269
- data/README.md +588 -46
- data/Rakefile +16 -16
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/docs/Object.html +117 -117
- data/docs/README_md.html +142 -142
- data/docs/Silicium/Combinatorics.html +270 -270
- data/docs/Silicium/Dice/Polyhedron.html +315 -315
- data/docs/Silicium/Dice/PolyhedronSet.html +321 -321
- data/docs/Silicium/Dice.html +99 -99
- data/docs/Silicium/Error.html +106 -106
- data/docs/Silicium/Geometry/Line2dCanon.html +243 -243
- data/docs/Silicium/Geometry/VariablesOrderException.html +106 -106
- data/docs/Silicium/Geometry.html +940 -940
- data/docs/Silicium/GraphVisualizer.html +226 -0
- data/docs/Silicium/Graphs/GraphError.html +106 -106
- data/docs/Silicium/Graphs/OrientedGraph.html +901 -775
- data/docs/Silicium/Graphs/UnorientedGraph.html +237 -284
- data/docs/Silicium/Graphs.html +374 -164
- data/docs/Silicium/IntegralDoesntExistError.html +106 -106
- data/docs/Silicium/NumericalIntegration.html +521 -521
- data/docs/Silicium/Optimization.html +629 -639
- data/docs/Silicium/Plotter/Image.html +297 -297
- data/docs/Silicium/Plotter.html +186 -186
- data/docs/Silicium.html +101 -101
- data/docs/created.rid +9 -9
- data/docs/css/fonts.css +167 -167
- data/docs/css/rdoc.css +619 -619
- data/docs/index.html +134 -132
- data/docs/js/darkfish.js +84 -84
- data/docs/js/navigation.js +105 -105
- data/docs/js/search.js +110 -110
- data/docs/js/search_index.js +1 -1
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searcher.js +229 -229
- data/docs/table_of_contents.html +697 -608
- data/lib/algebra.rb +452 -0
- data/lib/algebra_diff.rb +258 -0
- data/lib/geometry/figure.rb +62 -0
- data/lib/geometry.rb +290 -236
- data/lib/geometry3d.rb +270 -0
- data/lib/graph/dfs.rb +42 -0
- data/lib/graph/kruskal.rb +36 -0
- data/lib/graph/scc.rb +97 -0
- data/lib/graph.rb +350 -164
- data/lib/graph_visualizer.rb +287 -0
- data/lib/ml_algorithms.rb +181 -0
- data/lib/numerical_integration.rb +184 -147
- data/lib/optimization.rb +209 -144
- data/lib/plotter.rb +256 -96
- data/lib/polynomial_division.rb +132 -0
- data/lib/polynomial_interpolation.rb +94 -0
- data/lib/regression.rb +120 -0
- data/lib/silicium/adding.rb +37 -0
- data/lib/silicium/conversions.rb +23 -0
- data/lib/silicium/multi.rb +82 -0
- data/lib/silicium/sparse.rb +76 -0
- data/lib/silicium/sugar.rb +37 -0
- data/lib/silicium/trans.rb +26 -0
- data/lib/silicium/version.rb +3 -3
- data/lib/silicium.rb +5 -5
- data/lib/theory_of_probability.rb +240 -226
- data/lib/topological_sort.rb +50 -0
- data/oriented_graph.png +0 -0
- data/plot.png +0 -0
- data/silicium.gemspec +38 -39
- metadata +38 -16
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'silicium'
|
2
|
+
require 'chunky_png'
|
3
|
+
require 'ruby2d'
|
4
|
+
require 'ruby2d/window'
|
5
|
+
module Silicium
|
6
|
+
module GraphVisualizer
|
7
|
+
include Silicium::Graphs
|
8
|
+
include Ruby2D
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
# Changes window size
|
13
|
+
def change_window_size(w, h)
|
14
|
+
(Window.get :window).set width: w, height: h
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Changes width of edges
|
19
|
+
def change_edge_width(w)
|
20
|
+
@@line_width = w
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Changes radius of vertices
|
25
|
+
def change_vertices_radius(r)
|
26
|
+
@@vert_radius = r
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Changes labels size
|
31
|
+
def change_label_size(s)
|
32
|
+
@@label_size = s
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Changes labels color
|
37
|
+
def change_label_color(c)
|
38
|
+
@@label_color = Color.new(c)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Changes vertices color
|
43
|
+
def change_vertex_color(c)
|
44
|
+
@@vertex_color = Color.new(c)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Set the graph for visualization
|
49
|
+
def set_graph(graph)
|
50
|
+
if graph.class != OrientedGraph and graph.class != UnorientedGraph
|
51
|
+
raise ArgumentError, "Invalid graph type!"
|
52
|
+
end
|
53
|
+
clear_window
|
54
|
+
set_vertices(graph)
|
55
|
+
set_edges(graph)
|
56
|
+
set_labels(graph)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# clear screen
|
61
|
+
def clear_window
|
62
|
+
Window.clear
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# close screen
|
67
|
+
def close_window
|
68
|
+
Window.close
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# show graph on the screen
|
73
|
+
def show_window
|
74
|
+
Window.show
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# radius of vertices circles
|
79
|
+
@@vert_radius = 15
|
80
|
+
##
|
81
|
+
# width of the edges
|
82
|
+
@@line_width = 5
|
83
|
+
# size of labels
|
84
|
+
@@label_size = 15
|
85
|
+
##
|
86
|
+
# color of labels
|
87
|
+
@@label_color = Color.new('red')
|
88
|
+
##
|
89
|
+
# color of vertices
|
90
|
+
@@vertex_color = Color.new('white')
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
##
|
95
|
+
# creates labels of edges and vertices
|
96
|
+
def set_labels(graph)
|
97
|
+
@v_labels = {}
|
98
|
+
graph.vertex_labels.keys.each do |v|
|
99
|
+
@v_labels[v] = draw_vertex_label(v, graph.vertex_labels[v])
|
100
|
+
end
|
101
|
+
|
102
|
+
@e_labels = {}
|
103
|
+
graph.edge_labels.keys.each do |pair|
|
104
|
+
@e_labels[pair] = draw_edge_label(pair, graph.edge_labels[pair])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# draws label on vertex
|
110
|
+
def draw_vertex_label(v,label)
|
111
|
+
if (label.class != String and label.class != Integer)
|
112
|
+
return
|
113
|
+
end
|
114
|
+
x = @vertices[v].x - Math.sqrt(2)/2*@@vert_radius
|
115
|
+
y = @vertices[v].y - Math.sqrt(2)/2*@@vert_radius
|
116
|
+
return Text.new(label,x: x, y: y, size: @@label_size, color: @@label_color)
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# draws label on edge
|
121
|
+
def draw_edge_label(pair,label)
|
122
|
+
if (label.class != String and label.class != Integer)
|
123
|
+
return
|
124
|
+
end
|
125
|
+
x1 = @vertices[pair[:first]].x
|
126
|
+
y1 = @vertices[pair[:first]].y
|
127
|
+
x2 = @vertices[pair[:second]].x
|
128
|
+
y2 = @vertices[pair[:second]].y
|
129
|
+
x = (x1+x2)/2
|
130
|
+
y = (y1+y2)/2
|
131
|
+
|
132
|
+
if x1 == x2 and y1 == y2
|
133
|
+
x = @edges[pair][:line].x
|
134
|
+
y = @edges[pair][:line].y
|
135
|
+
end
|
136
|
+
|
137
|
+
return Text.new(label,x: x, y: y, size: @@label_size, color: @@label_color)
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# set all edges of the graph
|
142
|
+
def set_edges(graph)
|
143
|
+
@edges = {}
|
144
|
+
@is_oriented = graph.class == OrientedGraph
|
145
|
+
graph.vertices.keys.each do |from_vert|
|
146
|
+
graph.vertices[from_vert].each do |to_vert|
|
147
|
+
push_edge(from_vert,to_vert)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# creates edge and push it to the @edges
|
154
|
+
def push_edge(from_vert, to_vert)
|
155
|
+
col = get_random_color
|
156
|
+
if @is_oriented and has_edge?(to_vert,from_vert)
|
157
|
+
col = @edges[Pair.new(to_vert,from_vert)][:line].color
|
158
|
+
end
|
159
|
+
arrow = @is_oriented ? draw_oriented_edge(from_vert,to_vert,col):draw_edge(from_vert,to_vert,col)
|
160
|
+
@edges[Pair.new(from_vert,to_vert)] = arrow
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# return true if graph contains current edge
|
165
|
+
def has_edge?(from_vert, to_vert)
|
166
|
+
if @is_oriented
|
167
|
+
return @edges.has_key?(Pair.new(from_vert,to_vert))
|
168
|
+
end
|
169
|
+
return (@edges.has_key?(Pair.new(to_vert,from_vert)) or @edges.has_key?(Pair.new(from_vert,to_vert)))
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# returns random color
|
174
|
+
def get_random_color
|
175
|
+
col = Color.new('random')
|
176
|
+
while (col == @@label_color) or (col == @@vertex_color)
|
177
|
+
col = Color.new('random')
|
178
|
+
end
|
179
|
+
return col
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# draws all vertices of the graph
|
184
|
+
def set_vertices(graph)
|
185
|
+
@vertices = {}
|
186
|
+
w = Window.get :width
|
187
|
+
h = Window.get :height
|
188
|
+
radius= [w,h].min*1.0 / 2 - @@vert_radius*4
|
189
|
+
vert_step = (360.0 / graph.vertex_number)*(Math::PI/180)
|
190
|
+
position = 0
|
191
|
+
graph.vertices.keys.each do |vert|
|
192
|
+
x = w/2 + Math.cos(position)*radius
|
193
|
+
y = h/2 + Math.sin(position)*radius
|
194
|
+
@vertices[vert] = draw_vertex(x,y)
|
195
|
+
position += vert_step
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# creates circle for vertex
|
201
|
+
def draw_vertex(x, y)
|
202
|
+
circle = Circle.new(x: x, y: y, radius: @@vert_radius, sectors: 128, color: @@vertex_color)
|
203
|
+
return circle
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# creates arrow of edge between vertices
|
208
|
+
def draw_oriented_edge(v1,v2,col)
|
209
|
+
line = draw_edge(v1,v2,col)
|
210
|
+
|
211
|
+
x1 = @vertices[v1].x
|
212
|
+
y1 = @vertices[v1].y
|
213
|
+
x2 = @vertices[v2].x
|
214
|
+
y2 = @vertices[v2].y
|
215
|
+
|
216
|
+
x_len = x2-x1
|
217
|
+
y_len = y2-y1
|
218
|
+
len = Math.sqrt(x_len*x_len+y_len*y_len)
|
219
|
+
sin = y_len/len
|
220
|
+
cos = x_len/len
|
221
|
+
pos_x1 = x2 - @@vert_radius*cos
|
222
|
+
pos_y1 = y2 - @@vert_radius*sin
|
223
|
+
height_x= pos_x1 - @@line_width*4*cos
|
224
|
+
height_y= pos_y1 - @@line_width*4*sin
|
225
|
+
sin, cos = cos, sin
|
226
|
+
pos_x2 = height_x + @@line_width*2*cos
|
227
|
+
pos_y3 = height_y + @@line_width*2*sin
|
228
|
+
pos_x3 = height_x - @@line_width*2*cos
|
229
|
+
pos_y2 = height_y - @@line_width*2*sin
|
230
|
+
#triangle = Circle.new(x: pos_x2, y: pos_y3,radius: 4, color: col)
|
231
|
+
#Circle.new(x: pos_x3, y: pos_y2,radius: 4, color: col)
|
232
|
+
triangle = Triangle.new(x1: pos_x1, y1: pos_y1, x2: pos_x2, y2: pos_y2, x3: pos_x3, y3: pos_y3, color: col)
|
233
|
+
|
234
|
+
return {line: line, triangle: triangle}
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# creates edge between vertices
|
239
|
+
def draw_edge(v1,v2,col)
|
240
|
+
x1 = @vertices[v1].x
|
241
|
+
y1 = @vertices[v1].y
|
242
|
+
x2 = @vertices[v2].x
|
243
|
+
y2 = @vertices[v2].y
|
244
|
+
x_len = x1-x2
|
245
|
+
y_len = y1-y2
|
246
|
+
len = Math.sqrt(x_len*x_len+y_len*y_len)
|
247
|
+
|
248
|
+
if len == 0
|
249
|
+
return draw_loop(v1,col)
|
250
|
+
end
|
251
|
+
|
252
|
+
sin = y_len/len
|
253
|
+
cos = x_len/len
|
254
|
+
pos_x0 = x1 - @@vert_radius*cos
|
255
|
+
pos_y0 = y1 - @@vert_radius*sin
|
256
|
+
|
257
|
+
x_len = x2-x1
|
258
|
+
y_len = y2-y1
|
259
|
+
sin = y_len/len
|
260
|
+
cos = x_len/len
|
261
|
+
pos_x1 = x2 - @@vert_radius*cos
|
262
|
+
pos_y1 = y2 - @@vert_radius*sin
|
263
|
+
return Line.new(x1: pos_x0, y1: pos_y0, x2: pos_x1, y2: pos_y1, width: @@line_width, color: col)
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# create loop edge
|
268
|
+
def draw_loop(v,col)
|
269
|
+
x = @vertices[v].x
|
270
|
+
y = @vertices[v].y
|
271
|
+
center_x = (Window.get :width) / 2
|
272
|
+
center_y = (Window.get :height) / 2
|
273
|
+
x_len = center_x-x
|
274
|
+
y_len = center_y-y
|
275
|
+
len = Math.sqrt(x_len*x_len+y_len*y_len)
|
276
|
+
sin = y_len/len
|
277
|
+
cos = x_len/len
|
278
|
+
pos_x1 = x - @@vert_radius*cos*2
|
279
|
+
pos_y1 = y - @@vert_radius*sin*2
|
280
|
+
circle = Circle.new(x: pos_x1, y: pos_y1, radius: @@vert_radius*2, color: col)
|
281
|
+
Circle.new(x: pos_x1, y: pos_y1, radius: @@vert_radius*2-@@line_width, color: Window.get( :background))
|
282
|
+
@vertices[v] = Circle.new(x: x, y: y, radius: @@vert_radius+1, color: @@vertex_color)
|
283
|
+
return circle
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# Class represents computational graph
|
2
|
+
module BackPropogation
|
3
|
+
class ComputationalGraph
|
4
|
+
|
5
|
+
PRIORITY = Hash['(' => 0, '+' => 1, '-' => 1, '*' => 2, '/' => 2, '^' => 3]
|
6
|
+
TEMPLATES = {
|
7
|
+
operand: /^\s*([^\+\-\*\/\(\)\^\s]+)\s*(.*)/,
|
8
|
+
string: /^\s*([\+\-\*\/\^])\s*(.*)/,
|
9
|
+
brackets: /^\s*\(\s*(.*)/,
|
10
|
+
nested: /^\s*\)\s*(.*)/
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_accessor :graph
|
14
|
+
def initialize(expr_s)
|
15
|
+
exprproc = ComputationalGraph::polish_parser(expr_s, [])
|
16
|
+
pregraph = []
|
17
|
+
@graph = []
|
18
|
+
exprproc.split.each do |elem|
|
19
|
+
case elem
|
20
|
+
when '+'
|
21
|
+
dot = ComputationalGates::SummGate.new(elem)
|
22
|
+
dot.connect(pregraph.pop,pregraph.pop)
|
23
|
+
when '*'
|
24
|
+
dot = ComputationalGates::MultGate.new(elem)
|
25
|
+
dot.connect(pregraph.pop,pregraph.pop)
|
26
|
+
when '/'
|
27
|
+
dot = ComputationalGates::DivGate.new(elem)
|
28
|
+
scnd = pregraph.pop
|
29
|
+
frst = pregraph.pop
|
30
|
+
dot.connect(frst,scnd)
|
31
|
+
else
|
32
|
+
dot = ComputationalGates::CompGate.new(elem)
|
33
|
+
end
|
34
|
+
pregraph.push(dot)
|
35
|
+
@graph.push(dot)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
#Compute a value of expression
|
39
|
+
def forward_pass(variables_val)
|
40
|
+
@graph.each do |elem|
|
41
|
+
if elem.class != ComputationalGates::CompGate
|
42
|
+
elem.forward_pass
|
43
|
+
else
|
44
|
+
elem.frwrd = variables_val[elem.name]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
graph.last.frwrd
|
48
|
+
end
|
49
|
+
#Compute a gradient value for inputs
|
50
|
+
def backward_pass(loss_value)
|
51
|
+
param_grad = Hash.new()
|
52
|
+
@graph.last.bckwrd = loss_value
|
53
|
+
@graph.reverse.each do |elem|
|
54
|
+
if elem.class != ComputationalGates::CompGate
|
55
|
+
elem.backward_pass
|
56
|
+
else
|
57
|
+
param_grad[elem.name] = elem.bckwrd
|
58
|
+
end
|
59
|
+
end
|
60
|
+
param_grad
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def self.parse_operand(left, right, stack)
|
65
|
+
left + ' ' + polish_parser(right, stack)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.parse_string(left, right, i_str, stack)
|
69
|
+
if stack.empty? || PRIORITY[stack.last] < PRIORITY[left]
|
70
|
+
polish_parser(right, stack.push(left))
|
71
|
+
else
|
72
|
+
stack.pop + ' ' + polish_parser(i_str, stack)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.parse_nested(left, right, stack)
|
77
|
+
raise ArgumentError, 'Error: Excess of closing brackets.' if stack.empty?
|
78
|
+
|
79
|
+
head = stack.pop
|
80
|
+
PRIORITY[head].positive? ? head + ' ' + polish_parser(right, stack) : polish_parser(left, stack)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.parse_brackets(left, stack)
|
84
|
+
polish_parser(left, stack)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.parse_default(left, stack)
|
88
|
+
return '' if stack.empty?
|
89
|
+
raise ArgumentError, 'Error: Excess of opening brackets.' unless PRIORITY[stack.last] > 0
|
90
|
+
|
91
|
+
stack.pop + ' ' + polish_parser(left, stack)
|
92
|
+
end
|
93
|
+
|
94
|
+
#String preprocessing algorithm expression for computation
|
95
|
+
def self.polish_parser(i_str, stack)
|
96
|
+
case i_str
|
97
|
+
when TEMPLATES[:operand]
|
98
|
+
parse_operand(Regexp.last_match(1), Regexp.last_match(2), stack)
|
99
|
+
when TEMPLATES[:string]
|
100
|
+
parse_string(Regexp.last_match(1), Regexp.last_match(2), i_str, stack)
|
101
|
+
when TEMPLATES[:brackets]
|
102
|
+
parse_brackets(Regexp.last_match(1), stack.push('('))
|
103
|
+
when TEMPLATES[:nested]
|
104
|
+
parse_nested(Regexp.last_match(1), i_str, stack)
|
105
|
+
else
|
106
|
+
parse_default(i_str, stack)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
module ComputationalGates
|
112
|
+
class CompGate
|
113
|
+
attr_accessor :frwrd,:bckwrd,:out,:name
|
114
|
+
def initialize(name)
|
115
|
+
@name = name
|
116
|
+
@frwrd = self
|
117
|
+
end
|
118
|
+
end
|
119
|
+
class SummGate < CompGate
|
120
|
+
attr_accessor :in_frst,:in_scnd
|
121
|
+
def initialize(name)
|
122
|
+
super(name)
|
123
|
+
end
|
124
|
+
def connect(f_n,s_n)
|
125
|
+
@in_frst = f_n
|
126
|
+
@in_scnd = s_n
|
127
|
+
f_n.out = self
|
128
|
+
s_n.out = self
|
129
|
+
end
|
130
|
+
|
131
|
+
def forward_pass()
|
132
|
+
@frwrd = @in_frst.frwrd + @in_scnd.frwrd
|
133
|
+
end
|
134
|
+
def backward_pass()
|
135
|
+
@in_frst.bckwrd = @bckwrd
|
136
|
+
@in_scnd.bckwrd = @bckwrd
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
class MultGate < CompGate
|
141
|
+
attr_accessor :in_frst,:in_scnd
|
142
|
+
def initialize(name)
|
143
|
+
super(name)
|
144
|
+
end
|
145
|
+
def connect(f_n,s_n)
|
146
|
+
@in_frst = f_n
|
147
|
+
@in_scnd = s_n
|
148
|
+
f_n.out = self
|
149
|
+
s_n.out = self
|
150
|
+
end
|
151
|
+
def forward_pass()
|
152
|
+
@frwrd = @in_frst.frwrd * @in_scnd.frwrd
|
153
|
+
end
|
154
|
+
def backward_pass()
|
155
|
+
@in_frst.bckwrd = @bckwrd * @in_scnd.frwrd
|
156
|
+
@in_scnd.bckwrd = @bckwrd * @in_frst.frwrd
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
class DivGate < CompGate
|
161
|
+
attr_accessor :in_frst,:in_scnd
|
162
|
+
def initialize(name)
|
163
|
+
super(name)
|
164
|
+
end
|
165
|
+
def connect(f_n,s_n)
|
166
|
+
@in_frst = f_n
|
167
|
+
@in_scnd = s_n
|
168
|
+
f_n.out = self
|
169
|
+
s_n.out = self
|
170
|
+
end
|
171
|
+
def forward_pass()
|
172
|
+
@frwrd = @in_frst.frwrd / @in_scnd.frwrd
|
173
|
+
end
|
174
|
+
def backward_pass()
|
175
|
+
@in_frst.bckwrd = @bckwrd * ((-1)/(@in_scnd.frwrd ** 2))
|
176
|
+
@in_scnd.bckwrd = @bckwrd * ((-1)/(@in_frst.frwrd ** 2))
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|