Skip to content

Commit

Permalink
almost working rfield
Browse files Browse the repository at this point in the history
  • Loading branch information
hrbigelow committed Apr 2, 2019
1 parent 318b3dc commit 98ee22d
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 70 deletions.
14 changes: 14 additions & 0 deletions ae_wavenet.ipynb
Expand Up @@ -9,6 +9,13 @@
"import wavenet as wn"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 3,
Expand Down Expand Up @@ -54,6 +61,13 @@
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
173 changes: 111 additions & 62 deletions rfield.py
@@ -1,6 +1,23 @@
# An instance of this class represents the coordinate relationship between an
# output element and its input receptive field.
import fractions
import numpy as np

class _Stats(object):
'''Describes a 1D tensor of positioned elements'''
def __init__(self, l_pad, r_pad, size, spc, vspc, l_pos, r_pos):
self.l_pad = l_pad
self.r_pad = r_pad
self.size = size
self.spc = spc
self.vspc = vspc
self.l_pos = l_pos
self.r_pos = r_pos

def __repr__(self):
return 'l_pad: {}, r_pad: {}, size: {}, spc: {}, vspc: {}, l_pos:' \
' {}, r_pos: {}'.format(self.l_pad, self.r_pad, self.size, self.spc,
self.vspc, self.l_pos, self.r_pos)

class FieldOffset(object):
'''
Expand All @@ -15,17 +32,15 @@ def __init__(self, padding=(0, 0), wing_sizes=None, filter_sz=None,
stride=1, is_downsample=True, parent=None):
'''Converts a padding strategy into actual padding values.
'''
self.stride = stride
self.parent = parent
self.is_downsample = is_downsample
self.left_pad = padding[0]
self.right_pad = padding[1]
self.l_pad = padding[0]
self.r_pad = padding[1]

# stride_ratio is ratio of output spacing to input spacing
if self.is_downsample:
self.stride_ratio = self.stride
if is_downsample:
self.stride_ratio = fractions.Fraction(stride, 1)
else:
self.stride_ratio = 1.0 / self.stride
self.stride_ratio = fractions.Fraction(1, stride)

if isinstance(wing_sizes, tuple):
self.left_wing_sz = wing_sizes[0]
Expand All @@ -39,22 +54,31 @@ def __init__(self, padding=(0, 0), wing_sizes=None, filter_sz=None,
'wing_sizes tuple or filter_sz (integer)')

def __repr__(self):
return 'left_wing_sz: {}, right_wing_sz: {}, stride: {}, is_downsample: {}'.format(
self.left_wing_sz, self.right_wing_sz, self.stride, self.is_downsample)
return 'left_wing_sz: {}, right_wing_sz: {}, stride_ratio: {}'.format(
self.left_wing_sz, self.right_wing_sz, self.stride_ratio)

def _input_size(self, output_size):
'''calculate the input_size needed to produce the desired output_size'''
if self.is_downsample:
input_size = (output_size - 1) * self.stride + 1 \
+ self.left_wing_sz + self.right_wing_sz \
- self.left_pad - self.right_pad
const = self.left_wing_sz + self.right_wing_sz - self.l_pad - self.r_pad
if self.stride_ratio.denominator == 1:
input_size = (output_size - 1) * self.stride_ratio.numerator + 1 + const
else:
input_size = (self.stride - 1 - self.left_pad \
- self.right_pad + output_size \
+ self.left_wing_sz + self.right_wing_sz) \
// self.stride
input_size = (self.stride_ratio.denominator + output_size - 1 + const) \
// self.stride_ratio.denominator
return input_size

def _input_stride(self, output_stride_r):
return output_stride_r / self.stride_ratio

def _input_spacing(self, out_spc, out_vspc):
if self.stride_ratio > 1:
in_spc = out_vspc / self.stride_ratio
in_vspc = in_spc
else:
in_spc = out_vspc
in_vspc = in_spc / self.stride_ratio
return in_spc, in_vspc

def input_size(self, output_size):
'''calculate input_size size needed to produce the desired output_size.
recurses up to parents until the end.'''
Expand All @@ -65,60 +89,85 @@ def input_size(self, output_size):
return self.parent.input_size(this_input_size)

def _local_bounds(self):
'''For this transformation, calculates the offset between the first elements
of the input and the output, assuming output element spacing of 1.
Return values must be adjusted by the actual output element spacing.
'''
'''
padded_stride = 1 if self.is_downsample else self.stride_ratio
l_ind = self.left_wing_sz - self.left_pad
r_ind = self.right_wing_sz - self.right_pad
l_off = l_ind * padded_stride
r_off = r_ind * padded_stride
# spacing is the distance between any two consecutive elements in this
# tensor, including padding elements
#input_spacing = max(fractions.Fraction(1, 1), self.stride_ratio)
input_spacing = 1
l_ind = self.left_wing_sz - self.l_pad
r_ind = self.right_wing_sz - self.r_pad
l_off = l_ind * input_spacing
r_off = r_ind * input_spacing
return l_off, r_off

def _bounds(self):
if self.parent is None:
l_in_pos, r_in_pos, in_stride = 0, 0, 1
else:
l_in_pos, r_in_pos, in_stride = self.parent._bounds()

def _geometry(self, out_size, out_spc, out_vspc, l_out_pos, r_out_pos, accu=None):
l_off, r_off = self._local_bounds()

# Accumulate offsets
l_out_pos = l_in_pos + l_off * in_stride
r_out_pos = r_in_pos + r_off * in_stride
out_stride = self.stride_ratio * in_stride

return l_out_pos, r_out_pos, out_stride

def bounds(self):
'''Considering the full chain of transformations, calculate the number
of positions the output boundaries are inset from the input boundaries.
'''
l_pos, r_pos, stride = self._bounds()
return l_pos, r_pos

def _min_stride(self, pre_stride, min_stride):
'''Traverse the chain of transformations, recording the minimal stride
seen'''
cur_stride = pre_stride / self.stride_ratio
min_stride = min(cur_stride, min_stride)
in_size = self._input_size(out_size)
in_spc, in_vspc = self._input_spacing(out_spc, out_vspc)
l_in_pos = l_out_pos + l_off * in_spc
r_in_pos = r_out_pos + r_off * in_spc

if accu is not None:
assert isinstance(accu, list)
if len(accu) == 0:
first_stats = _Stats(0, 0, out_size, out_spc, out_vspc,
l_out_pos, r_out_pos)
accu.append(first_stats)

stats = _Stats(self.l_pad, self.r_pad, in_size, in_spc,
in_vspc, l_in_pos, r_in_pos)
accu.append(stats)

if self.parent is None:
return min_stride
if accu is None:
return in_size, l_in_pos, r_in_pos
else:
return accu
else:
return self.parent._min_stride(cur_stride, min_stride)
return self.parent._geometry(in_size, in_spc, in_vspc, l_in_pos, r_in_pos, accu)

def geometry(self, out_size):
'''calculate in_size, left_pos, right_pos needed for the chain of transformations
to produce the desired out_size'''
return self._geometry(out_size, 1, 1, 0, 0, None)

def print(self, output_size, lpad_rpad_inpad_data_sym='<>-*'):
'''print a symbolic stack of units with the given output_size'''
lpad, rpad, ipad, data = list(lpad_rpad_inpad_data_sym)
span = self.input_size(output_size)
min_stride = self._min_stride(1, 1)







'''pretty print a symbolic stack of units with the given output_size'''

lpad, rpad, ipad, data = list(lpad_rpad_inpad_data_sym)
stats = self._geometry(output_size, 1, 1, 0, 0, [])

lcm_denom = fractions.Fraction(np.lcm.reduce(tuple(s.spc.denominator for s in stats)))
for s in stats:
s.spc *= lcm_denom
s.vspc *= lcm_denom
s.l_pos *= lcm_denom
s.r_pos *= lcm_denom


def _dilate_str(string, space_sym, spacing):
space_str = space_sym * (spacing - 1)
return string[0] + ''.join(space_str + s for s in string[1:])

max_l_pos = max(s.l_pos for s in stats)
max_r_pos = max(s.r_pos for s in stats)

for st in stats:
indent = max_l_pos - st.l_pos
dedent = max_r_pos - st.r_pos

assert indent == round(indent)
assert st.spc == round(st.spc)
assert st.vspc == round(st.vspc)
assert st.r_pos == round(st.r_pos)
core = _dilate_str(data * st.size, ipad, round(st.vspc / st.spc))
body = lpad * st.l_pad + core + rpad * st.r_pad
body_dil = _dilate_str(body, ' ', round(st.spc))

s = ' ' * round(indent) + body_dil + ' ' * round(dedent) + '|'
print(s)
return stats

53 changes: 45 additions & 8 deletions rfield_notes.txt
Expand Up @@ -45,13 +45,41 @@ the input, which in general is both padded (self.left_pad) and dilated (self.str

But, we don't want to confuse the stride of the output with the coordinate system that

left_ind stride pad_stride sr pos pos_formula
C * * * 1 3/2x 1/2 pos(B) + left_ind(C) *
B * - * - * 1 3x 3 2 pos(A) + left_ind(B) * pad_stride(A)
A * * * * * * * * * * 0 x 1 0 0
left_ind spacing pad_stride sr pos pos_formula
C * * * 1 1 1/2 pos(B) + left_ind(C) *
B * - * - * 1 1 3 2 pos(A) + left_ind(B) * pad_stride(A)
A * * * * * * * * * * 0 2/3 1 0 0
|
0


Central Idea:

An input tensor of elements is transformed in a series of steps, each producing
a new tensor. At each step, all of the tensor elements have associated with
them a physical coordinate (such as 'x'), and are regularly spaced.

The difference between consecutive elements of a given tensor is called its
'spacing'. But, tensors may have two types of elements: 'value elements' and
'padding elements'. The physical distance between a pair of consecutive value
elements (which, there may be intervening padding elements between this pair of
value elements) is called 'value_spacing'. The physical distance between a
pair of any two consecutive elements, ignoring their type, is called just
'spacing'.

A transformation is characterized by its stride_ratio, which equals
out.value_spacing / in.value_spacing.



Here, there are a few important concepts. First is the spacing between elements in
spacing_ratio. This equals
output_spacing / input_spacing. Note, though that there are two kinds of spacing
for each tensor: unpadded_spacing, and padded_spacing.




The initial input spacing should be such that the output stride is a whole number.
So, this reduces to the problem of finding the multiple for the overall stride that
brings the output to a whole number. Actually, not only that, we need every intermediate
Expand Down Expand Up @@ -85,17 +113,26 @@ So, the print function will not print on the way down. It will maintain a densi
There are several tasks needed. But, mainly, we need to accumulate the following data for
every layer:

left_pad, right_pad, stride, size, left, right
left_pad, right_pad, stride, input_size, local_bounds

all of these are available in one form or another.
left_pad and right_pad are easy
And, maintain the min_stride along with that.

Another difficulty is, the recursion logic is intermixed with the
At the end, start printing. Simple enough.



all of these are available in one form or another.


Another difficulty is, the recursion logic is intermixed with the measurement
logic. But, for the print function we need to use the measurement logic
in different ways.

So, what is the overall blueprint for print?

As we recurse, collect


Another issue is that of keeping track of strides. Each operation imposes a stride ratio factor
to the existing stride. Some intermediate calculations involve fractional

0 comments on commit 98ee22d

Please sign in to comment.