From 60d2ee453c9751b83ce012328bf4437ff69ad8e4 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 10:06:16 +0100 Subject: [PATCH 1/7] Fix bug with Python 3.6 TypeError: a bytes-like object is required, not 'str' --- bin/produce_figure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/produce_figure b/bin/produce_figure index 503666a..d54e5d8 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -220,7 +220,7 @@ def compile_figure(which_, name, step, **kwargs): stderr=subprocess.PIPE) stdoutdata, stderrdata = p.communicate(input=tex_string) # Remove logs and aux if compilation was successfull - if '! LaTeX Error' in stdoutdata or '! Emergency stop' in stdoutdata: + if '! LaTeX Error' in str(stdoutdata) or '! Emergency stop' in str(stdoutdata): print('! LaTeX Error: check the log file in pdf/{}.log'.format(jobname)) else: subprocess.call(['rm'] + glob('pdf/{}.aux'.format(jobname)) + From cde02a0af29db610ac31756baa50544db12fcba4 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 10:38:25 +0100 Subject: [PATCH 2/7] Support for non-square sizes --- bin/produce_figure | 84 ++++++++++++++++++--------------- templates/arithmetic_figure.txt | 4 +- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index d54e5d8..042d26b 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -125,18 +125,18 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, ---------- step : int Which step of the animation to generate the LaTeX string for. - input_size : int - Convolution input size. - output_size : int - Convolution output size. - padding : int - Zero padding. - kernel_size : int - Convolution kernel size. - stride : int - Convolution stride. - dilation: int - Input Dilation + input_size : ndarray + Convolution input size as numpy array of shape (2,), and dtype int. + output_size : ndarray + Convolution output size as numpy array of shape (2,), and dtype int. + padding : ndarray + Zero padding as numpy array of shape (2,), and dtype int. + kernel_size : ndarray + Convolution kernel size as numpy array of shape (2,), and dtype int. + stride : ndarray + Convolution stride as numpy array of shape (2,), and dtype int. + dilation: ndarray + Input Dilation as numpy array of shape (2,), and dtype int. transposed : bool If ``True``, generate strings for the transposed convolution animation. @@ -159,13 +159,13 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, y_adjustment = 0 else: # Not used in convolutions - bottom_pad = 0 + bottom_pad = numpy.zeros(shape=(2,)) - spacing = 1 + spacing = numpy.ones(shape=(2,)) total_input_size = input_size + 2 * padding - y_adjustment = (total_input_size - (kernel_size - stride)) % stride + y_adjustment = ((total_input_size - (kernel_size - stride)) % stride).max() - max_steps = output_size ** 2 + max_steps = numpy.prod(output_size) if step >= max_steps: raise ValueError('step {} out of bounds (there are '.format(step) + '{} steps for this animation'.format(max_steps)) @@ -175,36 +175,36 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, with open(os.path.join('templates', 'unit.txt'), 'r') as f: unit_template = f.read() - offsets = list(itertools.product(range(output_size - 1, -1, -1), - range(output_size))) + offsets = list(itertools.product(reversed(range(output_size[1])), range(output_size[0]))) offset_y, offset_x = offsets[step] return six.b(tex_template.format(**{ - 'PADDING_TO': '{0},{0}'.format(total_input_size), + 'PADDING_TO': '{},{}'.format(*total_input_size), 'INPUT_UNITS': ''.join( - unit_template.format(padding + spacing * i, - bottom_pad + padding + spacing * j, - padding + spacing * i + 1, - bottom_pad + padding + spacing * j + 1) - for i, j in itertools.product(range(input_size), - range(input_size))), + unit_template.format(padding[0] + spacing[0] * i, + bottom_pad[1] + padding[1] + spacing[1] * j, + padding[0] + spacing[0] * i + 1, + bottom_pad[1] + padding[1] + spacing[1] * j + 1) + for i, j in itertools.product(range(input_size[0]), + range(input_size[1]))), 'INPUT_GRID_FROM_X': '{}'.format( - stride * offset_x), + stride[0] * offset_x), 'INPUT_GRID_FROM_Y': '{}'.format( - y_adjustment + stride * offset_y), + y_adjustment + stride[1] * offset_y), 'INPUT_GRID_TO_X': '{}'.format( - stride * offset_x + kernel_size), + stride[0] * offset_x + kernel_size[0]), 'INPUT_GRID_TO_Y': '{}'.format( - y_adjustment + stride * offset_y + kernel_size), - 'DILATION': '{}'.format(dilation), + y_adjustment + stride[1] * offset_y + kernel_size[1]), + 'DILATION_X': '{}'.format(dilation[0]), + 'DILATION_Y': '{}'.format(dilation[1]), 'OUTPUT_BOTTOM_LEFT': '{},{}'.format(offset_x, offset_y), 'OUTPUT_BOTTOM_RIGHT': '{},{}'.format(offset_x + 1, offset_y), 'OUTPUT_TOP_LEFT': '{},{}'.format(offset_x, offset_y + 1), 'OUTPUT_TOP_RIGHT': '{},{}'.format(offset_x + 1, offset_y + 1), - 'OUTPUT_TO': '{0},{0}'.format(output_size), + 'OUTPUT_TO': '{},{}'.format(*output_size), 'OUTPUT_GRID_FROM': '{},{}'.format(offset_x, offset_y), 'OUTPUT_GRID_TO': '{},{}'.format(offset_x + 1, offset_y + 1), - 'OUTPUT_ELEVATION': '{}cm'.format(total_input_size + 1), + 'OUTPUT_ELEVATION': '{}cm'.format(total_input_size[1] + 1), })) @@ -238,17 +238,17 @@ if __name__ == "__main__": parent_parser.add_argument("name", type=str, help="name for the animation") parent_parser.add_argument("step", type=int, help="animation step") parent_parser.add_argument("-i", "--input-size", type=int, default=5, - help="input size") + help="input size", nargs='+') parent_parser.add_argument("-o", "--output-size", type=int, default=3, - help="output size") + help="output size", nargs='+') parent_parser.add_argument("-p", "--padding", type=int, default=0, - help="zero padding") + help="zero padding", nargs='+') parent_parser.add_argument("-k", "--kernel-size", type=int, default=3, - help="kernel size") + help="kernel size", nargs='+') parent_parser.add_argument("-s", "--stride", type=int, default=1, - help="stride") + help="stride", nargs='+') parent_parser.add_argument("-d", "--dilation", type=int, default=1, - help="dilation") + help="dilation", nargs='+') subparser = subparsers.add_parser('arithmetic', parents=[parent_parser], help='convolution arithmetic animation') @@ -269,4 +269,12 @@ if __name__ == "__main__": name = args_dict.pop('name') step = args_dict.pop('step') + # Normalize arguments allowing tuples to numpy arrays of shape (2,) + for arg_name in ['input_size', 'output_size', 'padding', 'kernel_size', 'stride', 'dilation']: + arg_value = args_dict.pop(arg_name) + arg_value = numpy.asarray(arg_value).squeeze() + if arg_value.ndim < 1 or arg_value.shape == (1,): + arg_value = numpy.stack([arg_value, arg_value]) + args_dict[arg_name] = arg_value + compile_figure(which_, name, step, **args_dict) diff --git a/templates/arithmetic_figure.txt b/templates/arithmetic_figure.txt index 0351e2a..51fa1ed 100644 --- a/templates/arithmetic_figure.txt +++ b/templates/arithmetic_figure.txt @@ -14,8 +14,8 @@ \draw[step=10mm, base03, dashed, thick] (0,0) grid ({PADDING_TO}); {INPUT_UNITS} - \foreach \x in {{ {INPUT_GRID_FROM_X},\number\numexpr {INPUT_GRID_FROM_X}+{DILATION},...,\number\numexpr {INPUT_GRID_TO_X}-1 }} {{ - \foreach \y in {{ {INPUT_GRID_FROM_Y},\number\numexpr {INPUT_GRID_FROM_Y}+{DILATION},...,\number\numexpr {INPUT_GRID_TO_Y}-1 }} {{ + \foreach \x in {{ {INPUT_GRID_FROM_X},\number\numexpr {INPUT_GRID_FROM_X}+{DILATION_X},...,\number\numexpr {INPUT_GRID_TO_X}-1 }} {{ + \foreach \y in {{ {INPUT_GRID_FROM_Y},\number\numexpr {INPUT_GRID_FROM_Y}+{DILATION_Y},...,\number\numexpr {INPUT_GRID_TO_Y}-1 }} {{ \draw[fill=base02, opacity=0.4] (\x,\y) rectangle (\x+1,\y+1); }} From e79e770dbc756980d0c1c8ab37f08b40e1ee300d Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 10:44:11 +0100 Subject: [PATCH 3/7] Custom colors --- bin/produce_figure | 13 ++++++++++++- templates/arithmetic_figure.txt | 6 +++--- templates/unit.txt | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index 042d26b..abb6856 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -118,7 +118,8 @@ def make_numerical_tex_string(step, input_size, output_size, padding, def make_arithmetic_tex_string(step, input_size, output_size, padding, - kernel_size, stride, dilation, transposed): + kernel_size, stride, dilation, transposed, + input_color, output_color): """Creates a LaTeX string for a convolution arithmetic animation. Parameters @@ -140,6 +141,10 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, transposed : bool If ``True``, generate strings for the transposed convolution animation. + input_color: (int, int, int) + Color of input (as RGB tuple, 0-255) + output_color: (int, int, int) + Color of output (as RGB tuple, 0-255) Returns ------- @@ -205,6 +210,8 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, 'OUTPUT_GRID_FROM': '{},{}'.format(offset_x, offset_y), 'OUTPUT_GRID_TO': '{},{}'.format(offset_x + 1, offset_y + 1), 'OUTPUT_ELEVATION': '{}cm'.format(total_input_size[1] + 1), + 'INPUT_COLOR': '{},{},{}'.format(*input_color), + 'OUTPUT_COLOR': '{},{},{}'.format(*output_color), })) @@ -249,6 +256,10 @@ if __name__ == "__main__": help="stride", nargs='+') parent_parser.add_argument("-d", "--dilation", type=int, default=1, help="dilation", nargs='+') + parent_parser.add_argument("--input_color", type=int, nargs=3, + default=[38, 139, 210], help="input color (RGB)") + parent_parser.add_argument("--output_color", type=int, nargs=3, + default=[42, 161, 152], help="output color (RGB)") subparser = subparsers.add_parser('arithmetic', parents=[parent_parser], help='convolution arithmetic animation') diff --git a/templates/arithmetic_figure.txt b/templates/arithmetic_figure.txt index 51fa1ed..c688008 100644 --- a/templates/arithmetic_figure.txt +++ b/templates/arithmetic_figure.txt @@ -1,8 +1,8 @@ \documentclass[class=minimal,border=10pt]{{standalone}} \usepackage{{tikz}} \usepackage{{xcolor}} -\definecolor{{blue}}{{RGB}}{{38,139,210}} -\definecolor{{cyan}}{{RGB}}{{42,161,152}} +\definecolor{{input_color}}{{RGB}}{{{INPUT_COLOR}}} +\definecolor{{output_color}}{{RGB}}{{{OUTPUT_COLOR}}} \definecolor{{base01}}{{RGB}}{{88,110,117}} \definecolor{{base02}}{{RGB}}{{7,54,66}} \definecolor{{base03}}{{RGB}}{{0,43,54}} @@ -32,7 +32,7 @@ yslant=0.5,xslant=-0.7] \draw (BL) -- ({OUTPUT_BOTTOM_LEFT}) (BR) -- ({OUTPUT_BOTTOM_RIGHT}) (TL) -- ({OUTPUT_TOP_LEFT}) (TR) -- ({OUTPUT_TOP_RIGHT}); - \draw[fill=cyan] (0,0) rectangle ({OUTPUT_TO}); + \draw[fill=output_color] (0,0) rectangle ({OUTPUT_TO}); \draw[step=10mm, base03, thick] (0,0) grid ({OUTPUT_TO}); \draw[fill=base02, opacity=0.4] ({OUTPUT_GRID_FROM}) rectangle ({OUTPUT_GRID_TO}); diff --git a/templates/unit.txt b/templates/unit.txt index cd426e9..dd41a04 100644 --- a/templates/unit.txt +++ b/templates/unit.txt @@ -1 +1 @@ -\draw[draw=base03, fill=blue, thick] ({0},{1}) rectangle ({2},{3}); +\draw[draw=base03, fill=input_color, thick] ({0},{1}) rectangle ({2},{3}); From c8595adf0d7d53010a813b22bd42b3c92e0edaf8 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 10:47:54 +0100 Subject: [PATCH 4/7] Produce complete animation at once --- bin/produce_figure | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index abb6856..e08715e 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -243,7 +243,7 @@ if __name__ == "__main__": parent_parser = argparse.ArgumentParser(add_help=False) parent_parser.add_argument("name", type=str, help="name for the animation") - parent_parser.add_argument("step", type=int, help="animation step") + parent_parser.add_argument("step", type=int, help="animation step (-1 for all)") parent_parser.add_argument("-i", "--input-size", type=int, default=5, help="input size", nargs='+') parent_parser.add_argument("-o", "--output-size", type=int, default=3, @@ -288,4 +288,8 @@ if __name__ == "__main__": arg_value = numpy.stack([arg_value, arg_value]) args_dict[arg_name] = arg_value - compile_figure(which_, name, step, **args_dict) + if step < 0: + for step in range(int(numpy.prod(args_dict['output_size']))): + compile_figure(which_, name, step, **args_dict) + else: + compile_figure(which_, name, step, **args_dict) From 05d222d288bf34e92619c274858e2d0bc383b824 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 10:52:12 +0100 Subject: [PATCH 5/7] Automatic computation of output size --- bin/produce_figure | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index e08715e..57b9185 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -215,6 +215,10 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, })) +def compute_output_size(input_size, padding, kernel_size, stride, dilation, **ign_kwargs): + return (input_size + 2 * padding - kernel_size - (kernel_size - 1) * (dilation - 1)) // stride + 1 + + def compile_figure(which_, name, step, **kwargs): if which_ == 'arithmetic': tex_string = make_arithmetic_tex_string(step, **kwargs) @@ -246,8 +250,6 @@ if __name__ == "__main__": parent_parser.add_argument("step", type=int, help="animation step (-1 for all)") parent_parser.add_argument("-i", "--input-size", type=int, default=5, help="input size", nargs='+') - parent_parser.add_argument("-o", "--output-size", type=int, default=3, - help="output size", nargs='+') parent_parser.add_argument("-p", "--padding", type=int, default=0, help="zero padding", nargs='+') parent_parser.add_argument("-k", "--kernel-size", type=int, default=3, @@ -281,12 +283,13 @@ if __name__ == "__main__": step = args_dict.pop('step') # Normalize arguments allowing tuples to numpy arrays of shape (2,) - for arg_name in ['input_size', 'output_size', 'padding', 'kernel_size', 'stride', 'dilation']: + for arg_name in ['input_size', 'padding', 'kernel_size', 'stride', 'dilation']: arg_value = args_dict.pop(arg_name) arg_value = numpy.asarray(arg_value).squeeze() if arg_value.ndim < 1 or arg_value.shape == (1,): arg_value = numpy.stack([arg_value, arg_value]) args_dict[arg_name] = arg_value + args_dict['output_size'] = compute_output_size(**args_dict) if step < 0: for step in range(int(numpy.prod(args_dict['output_size']))): From 7baa1b720c8f147e590b030d197ad08778d9deb9 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 11:21:34 +0100 Subject: [PATCH 6/7] Update numerical figure --- bin/produce_figure | 118 ++++++++++++++++++--------------- templates/numerical_figure.txt | 6 +- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index 57b9185..252ecb8 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -12,25 +12,32 @@ numpy.random.seed(1234) def make_numerical_tex_string(step, input_size, output_size, padding, - kernel_size, stride, dilation, mode): + kernel_size, stride, dilation, mode, + input_color, output_color): """Creates a LaTeX string for a numerical convolution animation. Parameters ---------- step : int Which step of the animation to generate the LaTeX string for. - input_size : int - Convolution input size. - output_size : int - Convolution output size. - padding : int - Zero padding. - kernel_size : int - Convolution kernel size. - stride : int - Convolution stride. + input_size : ndarray + Convolution input size as numpy array of shape (2,), and dtype int. + output_size : ndarray + Convolution output size as numpy array of shape (2,), and dtype int. + padding : ndarray + Zero padding as numpy array of shape (2,), and dtype int. + kernel_size : ndarray + Convolution kernel size as numpy array of shape (2,), and dtype int. + stride : ndarray + Convolution stride as numpy array of shape (2,), and dtype int. + dilation: ndarray + Input Dilation as numpy array of shape (2,), and dtype int. mode : str Kernel mode, one of {'convolution', 'average', 'max'}. + input_color: (int, int, int) + Color of input (as RGB tuple, 0-255) + output_color: (int, int, int) + Color of output (as RGB tuple, 0-255) Returns ------- @@ -42,9 +49,9 @@ def make_numerical_tex_string(step, input_size, output_size, padding, if mode not in ('convolution', 'average', 'max'): raise ValueError("wrong convolution mode, choices are 'convolution', " "'average' or 'max'") - if dilation != 1: + if (dilation != 1).any(): raise ValueError("Only a dilation of 1 is currently supported for numerical output") - max_steps = output_size ** 2 + max_steps = numpy.prod(output_size) if step >= max_steps: raise ValueError('step {} out of bounds (there are '.format(step) + '{} steps for this animation'.format(max_steps)) @@ -54,66 +61,67 @@ def make_numerical_tex_string(step, input_size, output_size, padding, total_input_size = input_size + 2 * padding - input_ = numpy.zeros((total_input_size, total_input_size), dtype='int32') - input_[padding: padding + input_size, - padding: padding + input_size] = numpy.random.randint( - low=0, high=4, size=(input_size, input_size)) + input_ = numpy.zeros(total_input_size, dtype='int32') + input_[padding[0]: padding[0] + input_size[0], + padding[1]: padding[1] + input_size[1]] = numpy.random.randint( + low=0, high=4, size=input_size) kernel = numpy.random.randint( - low=0, high=3, size=(kernel_size, kernel_size)) - output = numpy.empty((output_size, output_size), dtype='float32') - for offset_x, offset_y in itertools.product(range(output_size), - range(output_size)): + low=0, high=3, size=kernel_size) + output = numpy.empty(output_size, dtype='float32') + for offset_x, offset_y in itertools.product(range(output_size[0]), + range(output_size[1])): if mode == 'convolution': output[offset_x, offset_y] = ( - input_[stride * offset_x: stride * offset_x + kernel_size, - stride * offset_y: stride * offset_y + kernel_size] * kernel).sum() + input_[stride[0] * offset_x: stride[0] * offset_x + kernel_size[0], + stride[1] * offset_y: stride[1] * offset_y + kernel_size[1]] * kernel).sum() elif mode == 'average': output[offset_x, offset_y] = ( - input_[stride * offset_x: stride * offset_x + kernel_size, - stride * offset_y: stride * offset_y + kernel_size]).mean() + input_[stride[0] * offset_x: stride[0] * offset_x + kernel_size[0], + stride[1] * offset_y: stride[1] * offset_y + kernel_size[1]]).mean() else: output[offset_x, offset_y] = ( - input_[stride * offset_x: stride * offset_x + kernel_size, - stride * offset_y: stride * offset_y + kernel_size]).max() + input_[stride[0] * offset_x: stride[0] * offset_x + kernel_size[0], + stride[1] * offset_y: stride[1] * offset_y + kernel_size[1]]).max() - offsets = list(itertools.product(range(output_size - 1, -1, -1), - range(output_size))) + offsets = list(itertools.product(reversed(range(output_size[1])), range(output_size[0]))) offset_y, offset_x = offsets[step] if mode == 'convolution': kernel_values_string = ''.join( "\\node (node) at ({0},{1}) {{\\tiny {2}}};\n".format( - i + 0.8 + stride * offset_x, j + 0.2 + stride * offset_y, - kernel[kernel_size - 1 - j, i]) - for i, j in itertools.product(range(kernel_size), - range(kernel_size))) + i + 0.8 + stride[0] * offset_x, j + 0.2 + stride[1] * offset_y, + kernel[kernel_size[0] - 1 - j, i]) + for i, j in itertools.product(range(kernel_size[1]), + range(kernel_size[0]))) else: kernel_values_string = '\n' return six.b(tex_template.format(**{ - 'PADDING_TO': '{0},{0}'.format(total_input_size), - 'INPUT_FROM': '{0},{0}'.format(padding), - 'INPUT_TO': '{0},{0}'.format(padding + input_size), + 'PADDING_TO': '{},{}'.format(*total_input_size), + 'INPUT_FROM': '{},{}'.format(*padding), + 'INPUT_TO': '{},{}'.format(*(padding + input_size)), 'INPUT_VALUES': ''.join( "\\node (node) at ({0},{1}) {{\\footnotesize {2}}};\n".format( - i + 0.5, j + 0.5, input_[total_input_size - 1 - j, i]) - for i, j in itertools.product(range(total_input_size), - range(total_input_size))), - 'INPUT_GRID_FROM': '{},{}'.format(stride * offset_x, - stride * offset_y), - 'INPUT_GRID_TO': '{},{}'.format(stride * offset_x + kernel_size, - stride * offset_y + kernel_size), + i + 0.5, j + 0.5, input_[total_input_size[0] - 1 - j, i]) + for i, j in itertools.product(range(total_input_size[0]), + range(total_input_size[1]))), + 'INPUT_GRID_FROM': '{},{}'.format(stride[0] * offset_x, + stride[1] * offset_y), + 'INPUT_GRID_TO': '{},{}'.format(stride[0] * offset_x + kernel_size[0], + stride[1] * offset_y + kernel_size[1]), 'KERNEL_VALUES': kernel_values_string, - 'OUTPUT_TO': '{0},{0}'.format(output_size), + 'OUTPUT_TO': '{},{}'.format(*output_size), 'OUTPUT_GRID_FROM': '{},{}'.format(offset_x, offset_y), 'OUTPUT_GRID_TO': '{},{}'.format(offset_x + 1, offset_y + 1), 'OUTPUT_VALUES': ''.join( "\\node (node) at ({0},{1}) {{\\tiny {2:.1f}}};\n".format( - i + 0.5, j + 0.5, output[output_size - 1 - j, i]) - for i, j in itertools.product(range(output_size), - range(output_size))), - 'XSHIFT': '{}cm'.format(total_input_size + 1), - 'YSHIFT': '{}cm'.format((total_input_size - output_size) // 2), + i + 0.5, j + 0.5, output[output_size[0] - 1 - j, i]) + for i, j in itertools.product(range(output_size[0]), + range(output_size[1]))), + 'XSHIFT': '{}cm'.format(total_input_size[0] + 1), + 'YSHIFT': '{}cm'.format((total_input_size[1] - output_size[1]) // 2), + 'INPUT_COLOR': '{},{},{}'.format(*input_color), + 'OUTPUT_COLOR': '{},{},{}'.format(*output_color), })) @@ -159,14 +167,14 @@ def make_arithmetic_tex_string(step, input_size, output_size, padding, bottom_pad = (input_size + 2 * padding - kernel_size) % stride input_size, output_size, padding, spacing, stride = ( - output_size, input_size, kernel_size - 1 - padding, stride, 1) + output_size, input_size, kernel_size - 1 - padding, stride, numpy.ones(shape=(2,), dtype=numpy.int32)) total_input_size = output_size + kernel_size - 1 y_adjustment = 0 else: # Not used in convolutions - bottom_pad = numpy.zeros(shape=(2,)) + bottom_pad = numpy.zeros(shape=(2,), dtype=numpy.int32) - spacing = numpy.ones(shape=(2,)) + spacing = numpy.ones(shape=(2,), dtype=numpy.int32) total_input_size = input_size + 2 * padding y_adjustment = ((total_input_size - (kernel_size - stride)) % stride).max() @@ -292,7 +300,11 @@ if __name__ == "__main__": args_dict['output_size'] = compute_output_size(**args_dict) if step < 0: - for step in range(int(numpy.prod(args_dict['output_size']))): + if args_dict['transposed']: + max_step = numpy.prod(args_dict['input_size']) + else: + max_step = numpy.prod(args_dict['output_size']) + for step in range(int(max_step)): compile_figure(which_, name, step, **args_dict) else: compile_figure(which_, name, step, **args_dict) diff --git a/templates/numerical_figure.txt b/templates/numerical_figure.txt index 211fe28..4f1ef25 100644 --- a/templates/numerical_figure.txt +++ b/templates/numerical_figure.txt @@ -1,8 +1,8 @@ \documentclass[class=article,border=10pt]{{standalone}} \usepackage{{tikz}} \usepackage{{xcolor}} -\definecolor{{blue}}{{RGB}}{{38,139,210}} -\definecolor{{cyan}}{{RGB}}{{42,161,152}} +\definecolor{{input_color}}{{RGB}}{{{INPUT_COLOR}}} +\definecolor{{output_color}}{{RGB}}{{{OUTPUT_COLOR}}} \definecolor{{base01}}{{RGB}}{{88,110,117}} \definecolor{{base02}}{{RGB}}{{7,54,66}} \definecolor{{base03}}{{RGB}}{{0,43,54}} @@ -21,7 +21,7 @@ {KERNEL_VALUES} \end{{scope}} \begin{{scope}}[xshift={XSHIFT}, yshift={YSHIFT}] - \draw[fill=cyan] (0,0) rectangle ({OUTPUT_TO}); + \draw[fill=output_color] (0,0) rectangle ({OUTPUT_TO}); \draw[step=10mm, base03, thick] (0,0) grid ({OUTPUT_TO}); \draw[fill=base02, opacity=0.4] ({OUTPUT_GRID_FROM}) rectangle ({OUTPUT_GRID_TO}); From 9e786307a14fb121dfe4c225f82a758c623e3b20 Mon Sep 17 00:00:00 2001 From: Max Berrendorf Date: Fri, 2 Mar 2018 11:28:54 +0100 Subject: [PATCH 7/7] Passing explicit output shape --- bin/produce_figure | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bin/produce_figure b/bin/produce_figure index 252ecb8..d5e7330 100755 --- a/bin/produce_figure +++ b/bin/produce_figure @@ -246,6 +246,13 @@ def compile_figure(which_, name, step, **kwargs): glob('pdf/{}.log'.format(jobname))) +def normalize_tuple(arg_value): + arg_value = numpy.asarray(arg_value).squeeze() + if arg_value.ndim < 1 or arg_value.shape == (1,): + arg_value = numpy.stack([arg_value, arg_value]) + return arg_value + + if __name__ == "__main__": parser = argparse.ArgumentParser( description="Compile a LaTeX figure as part of a convolution " @@ -258,6 +265,8 @@ if __name__ == "__main__": parent_parser.add_argument("step", type=int, help="animation step (-1 for all)") parent_parser.add_argument("-i", "--input-size", type=int, default=5, help="input size", nargs='+') + parent_parser.add_argument("-o", "--output-size", type=int, default=None, + help="output size", nargs='+') parent_parser.add_argument("-p", "--padding", type=int, default=0, help="zero padding", nargs='+') parent_parser.add_argument("-k", "--kernel-size", type=int, default=3, @@ -292,12 +301,13 @@ if __name__ == "__main__": # Normalize arguments allowing tuples to numpy arrays of shape (2,) for arg_name in ['input_size', 'padding', 'kernel_size', 'stride', 'dilation']: - arg_value = args_dict.pop(arg_name) - arg_value = numpy.asarray(arg_value).squeeze() - if arg_value.ndim < 1 or arg_value.shape == (1,): - arg_value = numpy.stack([arg_value, arg_value]) - args_dict[arg_name] = arg_value - args_dict['output_size'] = compute_output_size(**args_dict) + args_dict[arg_name] = normalize_tuple(args_dict[arg_name]) + + # Automatic output shape computation? + if args_dict['output_size'] is None: + args_dict['output_size'] = compute_output_size(**args_dict) + else: + args_dict['output_size'] = normalize_tuple(args_dict['output_size']) if step < 0: if args_dict['transposed']: